From 1cefb4eab690393267ab1a9219f8771ba3ced09d Mon Sep 17 00:00:00 2001 From: gandalf3 Date: Wed, 5 Jul 2017 02:45:30 -0700 Subject: [PATCH] First test for conditional requests Involves lots of mocking. Part of the issue is that there is no way (?) to know where repo.json is stored when blender isn't running the show. --- blenderpack.py | 44 ++++++++++++++++------- tests/test_refresh_repos.py | 72 +++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 tests/test_refresh_repos.py diff --git a/blenderpack.py b/blenderpack.py index 6d8096e..f70332c 100755 --- a/blenderpack.py +++ b/blenderpack.py @@ -21,7 +21,37 @@ SCHEMA_VERSION = 1 class BadAddon(Exception): pass -def fetch(url, pipe): + +# TODO add repolist class + +def load_repo(url): + """ + Searches local repo.json for repository with a matching 'url' and returns it + """ + pass #TODO + +def write_repo(repo): + """ + Writes repo to local repolist json + """ + pass #TODO + +def fetch_repo(url: str) -> dict: + """ Requests repo.json from URL and embeds etag/last-modification headers""" + local_copy = load_repo(url) + headers = {} + if 'etag' in local_copy: headers['If-None-Match'] = local_copy['etag'] + if 'last-modified' in local_copy: headers['If-Modified-Since'] = local_copy['last-modified'] + + resp = requests.get(url, headers=headers) + + repo = json.loads(resp.json()) + repo['etag'] = resp.headers.get('etag') + repo['last-modified'] = resp.headers.get('last-modified') + + write_repo(repo) + +def refresh(url): # we have to explicitly close the end of the pipe we are NOT using, # otherwise no exception will be generated when the other process closes its end. pipe[0].close() @@ -30,18 +60,8 @@ def fetch(url, pipe): local_repo_path.mkdir(exist_ok=True) try: - # TODO: do conditional request - re = requests.get(url) - + fetch_repojson(url) pipe[1].send(re.status_code) - - repo = re.json() - repo['etag'] = re.headers.get('etag') - repo['last-modified'] = re.headers.get('last-modified') - - # just stick it here for now.. - dump_repo(local_repo_path, repo) - finally: pipe[1].close() diff --git a/tests/test_refresh_repos.py b/tests/test_refresh_repos.py new file mode 100644 index 0000000..98dd42f --- /dev/null +++ b/tests/test_refresh_repos.py @@ -0,0 +1,72 @@ +import requests +import unittest +from unittest import mock +from blenderpack import fetch_repo, load_repo, write_repo +from datetime import datetime +import json + +# based on https://stackoverflow.com/a/28507806/2730823 + +# This method will be used by the mock to replace requests.get +def mocked_requests_get(*args, **kwargs): + cidict = requests.structures.CaseInsensitiveDict + req_headers = cidict(kwargs.get('headers')) + t_fmt = '%a, %m %b %Y %X %Z' + + class MockResponse: + def __init__(self, headers: cidict, status_code: int): + self.headers = headers + self.status_code = status_code + + def json(self): + return json.dumps({'url': 'http://someurl.tld/repo.json'}) + + if args[0] == 'http://someurl.tld/repo.json': + resp_headers = cidict({ + "ETag": '"2a0094b-b74-55326ced274f3"', + "Last-Modified": 'Sun, 13 Mar 2011 13:38:53 GMT', + }) + + if req_headers == {}: + resp_code = 200 + else: + req_headers = cidict(req_headers) + resp_code = 304 if req_headers.get('if-none-match', '') == resp_headers['etag']\ + or datetime.strptime(req_headers.get('if-modified-since', ''), t_fmt) < \ + datetime.strptime(resp_headers['last-modified'], t_fmt) \ + else 200 + return MockResponse(resp_headers, resp_code) + + return MockResponse(None, 404) + +_mocked_repo_storage = {} +def mocked_load_repo(*args, **kwargs): + global _mocked_repo_storage + if args[0] not in _mocked_repo_storage: + _mocked_repo_storage[args[0]] = {'url': args[0]} + + return _mocked_repo_storage[args[0]] + +def mocked_write_repo(*args, **kwargs): + global _mocked_repo_storage + _mocked_repo_storage[args[0]['url']] = args[0] + + +class fetch_url_twice(unittest.TestCase): + + @mock.patch('requests.get', side_effect=mocked_requests_get) + @mock.patch('blenderpack.load_repo', side_effect=mocked_load_repo) + @mock.patch('blenderpack.write_repo', side_effect=mocked_write_repo) + def test_fetch(self, mock_write, mock_load, mock_get): + fetch_repo('http://someurl.tld/repo.json') + mock_get.assert_called_with('http://someurl.tld/repo.json', headers={}) + + fetch_repo('http://someurl.tld/repo.json') + mock_get.assert_called_with('http://someurl.tld/repo.json', headers={ + 'If-None-Match': '"2a0094b-b74-55326ced274f3"', + 'If-Modified-Since': 'Sun, 13 Mar 2011 13:38:53 GMT' + }) + + +if __name__ == '__main__': + unittest.main()