Skip to content

Commit 022059c

Browse files
committed
Add test fixtures and basic Github tests.
1 parent 71f29ad commit 022059c

8 files changed

+333
-0
lines changed

dev-requirements.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
decorator
12
ipython
23
jinja2
4+
pytest
35
pyzmq
46
-r requirements.txt

tests/__init__.py

Whitespace-only changes.

tests/conftest.py

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from tests.mocking import aiopretty
4+
5+
6+
def pytest_configure(config):
7+
config.addinivalue_line(
8+
'markers',
9+
'aiopretty: mark tests to activate aiopretty'
10+
)
11+
12+
13+
def pytest_runtest_setup(item):
14+
marker = item.get_marker('aiopretty')
15+
if marker is not None:
16+
aiopretty.clear()
17+
aiopretty.activate()
18+
19+
20+
def pytest_runtest_teardown(item, nextitem):
21+
marker = item.get_marker('aiopretty')
22+
if marker is not None:
23+
aiopretty.deactivate()

tests/mocking/aiopretty.py

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import json
4+
import asyncio
5+
6+
import aiohttp
7+
8+
9+
class _AioPretty:
10+
def __init__(self):
11+
self.request = None
12+
self.registry = {}
13+
self.calls = []
14+
15+
16+
aiopretty = _AioPretty()
17+
18+
19+
def make_call(**kwargs):
20+
return kwargs
21+
22+
23+
def fake_request(method, uri, **kwargs):
24+
try:
25+
options = aiopretty.registry[(method, uri)]
26+
except KeyError:
27+
raise Exception('NO')
28+
aiopretty.calls.append(make_call(method=method, uri=uri, **kwargs))
29+
mock_response = aiohttp.client.ClientResponse(method, uri)
30+
mock_response.headers = aiohttp.client.CaseInsensitiveMultiDict(options.get('headers', {}))
31+
mock_response._content = options.get('body', 'aiopretty')
32+
future = asyncio.Future()
33+
future.set_result(mock_response)
34+
return future
35+
36+
37+
def register_uri(method, uri, **options):
38+
aiopretty.registry[(method, uri)] = options
39+
40+
41+
def register_json_uri(method, uri, **options):
42+
body = json.dumps(options.pop('body', None)).encode('utf-8')
43+
headers = {'Content-Type': 'application/json'}
44+
headers.update(options.pop('headers', {}))
45+
register_uri(method, uri, body=body, headers=headers, **options)
46+
47+
48+
def activate():
49+
aiohttp.request, aiopretty.request = fake_request, aiohttp.request
50+
51+
52+
def deactivate():
53+
aiohttp.request, aiopretty.request = aiopretty.request, None
54+
55+
56+
def clear():
57+
aiopretty.registry = {}
58+
59+
60+
def compare_call(first, second):
61+
for key, value in first.items():
62+
if second.get(key) != value:
63+
return False
64+
return True
65+
66+
67+
def has_call(**kwargs):
68+
for call in aiopretty.calls:
69+
if compare_call(kwargs, call):
70+
return True
71+
return False

tests/mocking/pypretty.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# -*- coding: utf-8 -*-
2+
3+
4+
import pytest
5+
from . import aiopretty
6+
7+
8+
def pytest_configure(config):
9+
config.addinivalue_line(
10+
'markers',
11+
'aiopretty: mark tests to activate aiopretty'
12+
)
13+
14+
15+
def pytest_runtest_setup(item):
16+
import pdb; pdb.set_trace()
17+
marker = item.get_marker('aiopretty')
18+
if marker is not None:
19+
aiopretty.clear()
20+
aiopretty.activate()
21+
22+
23+
def pytest_runtest_teardown(item, nextitem):
24+
marker = item.get_marker('aiopretty')
25+
if marker is not None:
26+
aiopretty.deactivate()

tests/providers/__init__.py

Whitespace-only changes.

tests/providers/test_github.py

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import pytest
4+
5+
from tests.utils import async
6+
from tests.mocking import aiopretty, pypretty
7+
8+
import io
9+
import os
10+
import json
11+
import base64
12+
import asyncio
13+
14+
from waterbutler.providers import core
15+
from waterbutler.providers.contrib.github import GithubProvider
16+
17+
18+
@pytest.fixture
19+
def auth():
20+
return {
21+
'name': 'cat',
22+
'email': 'cat@cat.com',
23+
}
24+
25+
26+
@pytest.fixture
27+
def identity():
28+
return {
29+
'owner': 'cat',
30+
'repo': 'food',
31+
'token': 'naps',
32+
}
33+
34+
35+
@pytest.fixture
36+
def provider(auth, identity):
37+
return GithubProvider(auth, identity)
38+
39+
40+
@pytest.fixture
41+
def file_content():
42+
return b'hungry'
43+
44+
45+
@pytest.fixture
46+
def file_like(file_content):
47+
return io.BytesIO(file_content)
48+
49+
50+
@pytest.fixture
51+
def file_wrapper(file_like):
52+
return core.FileWrapper(file_like)
53+
54+
55+
@async
56+
@pytest.mark.aiopretty
57+
def test_download(provider):
58+
url = provider.build_repo_url('git', 'blobs', 'mysha')
59+
aiopretty.register_uri('GET', url, body=b'delicious')
60+
result = yield from provider.download('mysha')
61+
content = yield from result.response.read()
62+
assert content == b'delicious'
63+
64+
65+
@pytest.fixture
66+
def repo_contents():
67+
return [
68+
{
69+
'type': 'file',
70+
'size': 625,
71+
'name': 'octokit.rb',
72+
'path': 'lib/octokit.rb',
73+
'sha': 'fff6fe3a23bf1c8ea0692b4a883af99bee26fd3b',
74+
'url': 'https://api.github.com/repos/pengwynn/octokit/contents/lib/octokit.rb',
75+
'git_url': 'https://api.github.com/repos/pengwynn/octokit/git/blobs/fff6fe3a23bf1c8ea0692b4a883af99bee26fd3b',
76+
'html_url': 'https://github.com/pengwynn/octokit/blob/master/lib/octokit.rb',
77+
'_links': {
78+
'self': 'https://api.github.com/repos/pengwynn/octokit/contents/lib/octokit.rb',
79+
'git': 'https://api.github.com/repos/pengwynn/octokit/git/blobs/fff6fe3a23bf1c8ea0692b4a883af99bee26fd3b',
80+
'html': 'https://github.com/pengwynn/octokit/blob/master/lib/octokit.rb',
81+
},
82+
},
83+
{
84+
'type': 'dir',
85+
'size': 0,
86+
'name': 'octokit',
87+
'path': 'lib/octokit',
88+
'sha': 'a84d88e7554fc1fa21bcbc4efae3c782a70d2b9d',
89+
'url': 'https://api.github.com/repos/pengwynn/octokit/contents/lib/octokit',
90+
'git_url': 'https://api.github.com/repos/pengwynn/octokit/git/trees/a84d88e7554fc1fa21bcbc4efae3c782a70d2b9d',
91+
'html_url': 'https://github.com/pengwynn/octokit/tree/master/lib/octokit',
92+
'_links': {
93+
'self': 'https://api.github.com/repos/pengwynn/octokit/contents/lib/octokit',
94+
'git': 'https://api.github.com/repos/pengwynn/octokit/git/trees/a84d88e7554fc1fa21bcbc4efae3c782a70d2b9d',
95+
'html': 'https://github.com/pengwynn/octokit/tree/master/lib/octokit',
96+
},
97+
},
98+
]
99+
100+
101+
class TestGithubHelpers:
102+
103+
def test_build_repo_url(self, identity, provider):
104+
expected = provider.build_url('repos', identity['owner'], identity['repo'], 'contents')
105+
assert provider.build_repo_url('contents') == expected
106+
107+
def test_committer(self, auth, provider):
108+
expected = {
109+
'name': auth['name'],
110+
'email': auth['email'],
111+
}
112+
assert provider.committer == expected
113+
114+
115+
@async
116+
@pytest.mark.aiopretty
117+
def test_metadata(provider, repo_contents):
118+
path = 'snacks'
119+
url = provider.build_repo_url('contents', path)
120+
aiopretty.register_uri('GET', url, body=json.dumps(repo_contents).encode('utf-8'), headers={'Content-Type': 'application/json'})
121+
result = yield from provider.metadata(path)
122+
assert result == [provider._serialize_metadata(item) for item in repo_contents]
123+
124+
125+
@async
126+
@pytest.mark.aiopretty
127+
def test_upload_create(provider, repo_contents, file_content, file_wrapper):
128+
message = 'so hungry'
129+
path = repo_contents[0]['path'][::-1]
130+
metadata_url = provider.build_repo_url('contents', os.path.dirname(path))
131+
aiopretty.register_json_uri('GET', metadata_url, body=repo_contents)
132+
upload_url = provider.build_repo_url('contents', path)
133+
aiopretty.register_uri('PUT', upload_url)
134+
result = yield from provider.upload(file_wrapper, path, message)
135+
expected_data = {
136+
'path': path,
137+
'message': message,
138+
'content': base64.b64encode(file_content).decode('utf-8'),
139+
'committer': provider.committer,
140+
}
141+
assert aiopretty.has_call(method='GET', uri=metadata_url)
142+
assert aiopretty.has_call(method='PUT', uri=upload_url, data=json.dumps(expected_data))
143+
144+
145+
@async
146+
@pytest.mark.aiopretty
147+
def test_upload_update(provider, repo_contents, file_content, file_wrapper):
148+
path = repo_contents[0]['path']
149+
sha = repo_contents[0]['sha']
150+
message = 'so hungry'
151+
metadata_url = provider.build_repo_url('contents', os.path.dirname(path))
152+
aiopretty.register_json_uri('GET', metadata_url, body=repo_contents)
153+
upload_url = provider.build_repo_url('contents', path)
154+
aiopretty.register_uri('PUT', upload_url)
155+
result = yield from provider.upload(file_wrapper, path, message)
156+
expected_data = {
157+
'path': path,
158+
'message': message,
159+
'content': base64.b64encode(file_content).decode('utf-8'),
160+
'committer': provider.committer,
161+
'sha': sha,
162+
}
163+
assert aiopretty.has_call(method='GET', uri=metadata_url)
164+
assert aiopretty.has_call(method='PUT', uri=upload_url, data=json.dumps(expected_data))
165+
166+
167+
@async
168+
@pytest.mark.aiopretty
169+
def test_delete_with_branch(provider, repo_contents):
170+
path = repo_contents[0]['path']
171+
sha = repo_contents[0]['sha']
172+
branch = 'master'
173+
message = 'deleted'
174+
url = provider.build_repo_url('contents', path)
175+
aiopretty.register_json_uri('DELETE', url)
176+
result = yield from provider.delete(path, message, sha, branch=branch)
177+
expected_data = {
178+
'message': message,
179+
'sha': sha,
180+
'committer': provider.committer,
181+
'branch': branch,
182+
}
183+
assert aiopretty.has_call(method='DELETE', uri=url, data=json.dumps(expected_data))
184+
185+
186+
@async
187+
@pytest.mark.aiopretty
188+
def test_delete_without_branch(provider, repo_contents):
189+
path = repo_contents[0]['path']
190+
sha = repo_contents[0]['sha']
191+
message = 'deleted'
192+
url = provider.build_repo_url('contents', path)
193+
aiopretty.register_json_uri('DELETE', url)
194+
result = yield from provider.delete(path, message, sha)
195+
expected_data = {
196+
'message': message,
197+
'sha': sha,
198+
'committer': provider.committer,
199+
}
200+
assert aiopretty.has_call(method='DELETE', uri=url, data=json.dumps(expected_data))

tests/utils.py

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import asyncio
4+
5+
from decorator import decorator
6+
7+
8+
@decorator
9+
def async(func, *args, **kwargs):
10+
future = func(*args, **kwargs)
11+
asyncio.get_event_loop().run_until_complete(future)

0 commit comments

Comments
 (0)