Merge ~woutervb/snapstore-client:move_to_pytest into snapstore-client:master
- Git
- lp:~woutervb/snapstore-client
- move_to_pytest
- Merge into master
Status: | Merged |
---|---|
Approved by: | Wouter van Bommel |
Approved revision: | 36af623c06a1bf7199697f412f5ce55450dfcdd7 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~woutervb/snapstore-client:move_to_pytest |
Merge into: | snapstore-client:master |
Diff against target: |
1820 lines (+534/-698) 10 files modified
Makefile (+4/-3) conftest.py (+70/-0) dev/null (+0/-67) requirements-dev.txt (+2/-3) snapstore_client/logic/tests/test_login.py (+95/-126) snapstore_client/logic/tests/test_overrides.py (+132/-156) snapstore_client/logic/tests/test_push.py (+32/-54) snapstore_client/tests/test_config.py (+52/-76) snapstore_client/tests/test_presentation_helpers.py (+71/-108) snapstore_client/tests/test_webservices.py (+76/-105) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Maximiliano Bertacchini | Approve | ||
Jonathan Hartley (community) | Approve | ||
Review via email: mp+414940@code.launchpad.net |
This proposal supersedes a proposal from 2022-01-28.
Commit message
Convert the testing to pytest
This will simplify changes in the future and resulted in a big cleanup.
The test_cli.py has been removed, as the cli will be replaced by click and has to be done completely.
Description of the change
Convert the testing to pytest
This will simplify changes in the future and resulted in a big cleanup.
The test_cli.py has been removed, as the cli will be replaced by click and has to be done completely.
Maximiliano Bertacchini (maxiberta) wrote : Posted in a previous version of this proposal | # |
Wouter van Bommel (woutervb) wrote : Posted in a previous version of this proposal | # |
Thanks for the feedback, will work on the comments.
Won't hide it for broader public, but it at least made me realize that we need some kind of styleguide on how / where to define fixtures
Jonathan Hartley (tartley) : Posted in a previous version of this proposal | # |
Jonathan Hartley (tartley) wrote : | # |
Bravely fought, tackling this churn!
I have one minor superficial FYI in the diffs, and one here. Ignore them if they are not today's problem:
Just FYI, when I run these test on a focal lxc, I get deprecation warnings from the use of 'responses' in test_login.py. I don't think this is due to your edits, but thought I'd mention it just in case you weren't aware and wanted to know:
=======
snapstore_
/home/
warn(
Jonathan Hartley (tartley) wrote : | # |
Another thought...
Maximiliano Bertacchini (maxiberta) wrote : | # |
+1 awesome. Check a couple of non-blocking comments below. Thanks!
Wouter van Bommel (woutervb) wrote : | # |
Thanks for the responses, will update (most) things you both found.
Preview Diff
1 | diff --git a/Makefile b/Makefile | |||
2 | index 76ddd0a..be1be80 100644 | |||
3 | --- a/Makefile | |||
4 | +++ b/Makefile | |||
5 | @@ -28,7 +28,7 @@ snap: | |||
6 | 28 | snapcraft | 28 | snapcraft |
7 | 29 | 29 | ||
8 | 30 | test: $(ENV)/dev | 30 | test: $(ENV)/dev |
10 | 31 | $(PYTHON3) -m unittest $(TESTS) 2>&1 | 31 | $(PYTHON3) -m pytest |
11 | 32 | $(MAKE) --silent lint | 32 | $(MAKE) --silent lint |
12 | 33 | 33 | ||
13 | 34 | /snap/bin/documentation-builder: | 34 | /snap/bin/documentation-builder: |
14 | @@ -39,7 +39,7 @@ docs: /snap/bin/documentation-builder | |||
15 | 39 | 39 | ||
16 | 40 | 40 | ||
17 | 41 | lint: $(ENV)/dev | 41 | lint: $(ENV)/dev |
19 | 42 | $(FLAKE8) $(SERVICE_PACKAGE) | 42 | $(FLAKE8) $(SERVICE_PACKAGE) conftest.py |
20 | 43 | $(BLACK) --check . | 43 | $(BLACK) --check . |
21 | 44 | 44 | ||
22 | 45 | black: $(ENV)/dev | 45 | black: $(ENV)/dev |
23 | @@ -47,8 +47,9 @@ black: $(ENV)/dev | |||
24 | 47 | 47 | ||
25 | 48 | coverage: $(ENV)/dev | 48 | coverage: $(ENV)/dev |
26 | 49 | $(PYTHON3) -m coverage erase | 49 | $(PYTHON3) -m coverage erase |
28 | 50 | $(PYTHON3) -m coverage run --include "$(SERVICE_PACKAGE)*" -m unittest $(TESTS) | 50 | $(PYTHON3) -m coverage run --include "$(SERVICE_PACKAGE)*" -m pytest |
29 | 51 | $(PYTHON3) -m coverage html | 51 | $(PYTHON3) -m coverage html |
30 | 52 | $(PYTHON3) -m coverage report | ||
31 | 52 | 53 | ||
32 | 53 | clean: | 54 | clean: |
33 | 54 | rm -rf $(TMPDIR) | 55 | rm -rf $(TMPDIR) |
34 | diff --git a/conftest.py b/conftest.py | |||
35 | 55 | new file mode 100644 | 56 | new file mode 100644 |
36 | index 0000000..06c7ca0 | |||
37 | --- /dev/null | |||
38 | +++ b/conftest.py | |||
39 | @@ -0,0 +1,70 @@ | |||
40 | 1 | import os.path | ||
41 | 2 | import tempfile | ||
42 | 3 | from unittest import mock | ||
43 | 4 | from unittest.mock import patch | ||
44 | 5 | |||
45 | 6 | from pymacaroons import Macaroon | ||
46 | 7 | import pytest | ||
47 | 8 | |||
48 | 9 | from snapstore_client.config import Config | ||
49 | 10 | |||
50 | 11 | |||
51 | 12 | @pytest.fixture(scope="function") | ||
52 | 13 | def ftmpdir(tmpdir): | ||
53 | 14 | """Takes the tmpdir, but add a random path per function invocation""" | ||
54 | 15 | with tempfile.TemporaryDirectory(dir=tmpdir) as new_tmpdir: | ||
55 | 16 | yield new_tmpdir | ||
56 | 17 | |||
57 | 18 | |||
58 | 19 | @pytest.fixture | ||
59 | 20 | def mocked_xdgconfig(ftmpdir): | ||
60 | 21 | with patch("xdg.BaseDirectory.xdg_config_home", ftmpdir), patch( | ||
61 | 22 | "xdg.BaseDirectory.xdg_config_dirs", [ftmpdir] | ||
62 | 23 | ): | ||
63 | 24 | yield ftmpdir | ||
64 | 25 | |||
65 | 26 | |||
66 | 27 | @pytest.fixture | ||
67 | 28 | def mocked_config(mocked_xdgconfig): | ||
68 | 29 | app_config_path = os.path.join(mocked_xdgconfig, "snap-store-proxy-client") | ||
69 | 30 | os.makedirs(app_config_path) | ||
70 | 31 | root = Macaroon(key="random-key") | ||
71 | 32 | root.add_third_party_caveat("login.example.com", "sso-key", "payload") | ||
72 | 33 | unbound_discharge = Macaroon( | ||
73 | 34 | location="login.example.com", identifier="payload", key="sso-key" | ||
74 | 35 | ) | ||
75 | 36 | with open(os.path.join(app_config_path, "config.ini"), "w") as f: | ||
76 | 37 | f.write( | ||
77 | 38 | ( | ||
78 | 39 | "[store:default]\n" | ||
79 | 40 | "gw_url = {gw_url}\n" | ||
80 | 41 | "sso_url = {sso_url}\n" | ||
81 | 42 | "root = {root}\n" | ||
82 | 43 | "unbound_discharge = {unbound_discharge}\n" | ||
83 | 44 | ).format( | ||
84 | 45 | gw_url="http://store.local/", | ||
85 | 46 | sso_url="http://sso.local/", | ||
86 | 47 | root=root.serialize(), | ||
87 | 48 | unbound_discharge=unbound_discharge.serialize(), | ||
88 | 49 | ) | ||
89 | 50 | ) | ||
90 | 51 | |||
91 | 52 | yield Config() | ||
92 | 53 | |||
93 | 54 | |||
94 | 55 | @pytest.fixture | ||
95 | 56 | def mocked_empty_config(mocked_xdgconfig): | ||
96 | 57 | app_config_path = os.path.join(mocked_xdgconfig, "snap-store-proxy-client") | ||
97 | 58 | os.makedirs(app_config_path) | ||
98 | 59 | |||
99 | 60 | |||
100 | 61 | @pytest.fixture | ||
101 | 62 | def mock_input(): | ||
102 | 63 | with mock.patch("builtins.input") as input: | ||
103 | 64 | yield input | ||
104 | 65 | |||
105 | 66 | |||
106 | 67 | @pytest.fixture | ||
107 | 68 | def mock_getpass(): | ||
108 | 69 | with mock.patch("getpass.getpass") as getpass: | ||
109 | 70 | yield getpass | ||
110 | diff --git a/requirements-dev.txt b/requirements-dev.txt | |||
111 | index 72daeb2..d9285a2 100644 | |||
112 | --- a/requirements-dev.txt | |||
113 | +++ b/requirements-dev.txt | |||
114 | @@ -1,8 +1,7 @@ | |||
115 | 1 | acceptable>=0.9 | 1 | acceptable>=0.9 |
116 | 2 | coverage | 2 | coverage |
117 | 3 | fixtures | ||
118 | 4 | flake8 | 3 | flake8 |
119 | 5 | responses | 4 | responses |
120 | 6 | testscenarios | ||
121 | 7 | testtools | ||
122 | 8 | black | 5 | black |
123 | 6 | pytest | ||
124 | 7 | testtools | ||
125 | 9 | \ No newline at end of file | 8 | \ No newline at end of file |
126 | diff --git a/snapstore_client/logic/tests/test_login.py b/snapstore_client/logic/tests/test_login.py | |||
127 | index d61ac1b..7dbfea1 100644 | |||
128 | --- a/snapstore_client/logic/tests/test_login.py | |||
129 | +++ b/snapstore_client/logic/tests/test_login.py | |||
130 | @@ -1,13 +1,13 @@ | |||
131 | 1 | # Copyright 2017 Canonical Ltd. | 1 | # Copyright 2017 Canonical Ltd. |
132 | 2 | 2 | ||
133 | 3 | import pytest | ||
134 | 3 | import json | 4 | import json |
135 | 4 | from unittest import mock | 5 | from unittest import mock |
136 | 5 | from urllib.parse import urljoin, urlparse | 6 | from urllib.parse import urljoin, urlparse |
137 | 6 | 7 | ||
139 | 7 | import fixtures | 8 | |
140 | 8 | from pymacaroons import Macaroon | 9 | from pymacaroons import Macaroon |
141 | 9 | import responses | 10 | import responses |
142 | 10 | from testtools import TestCase | ||
143 | 11 | from testtools.matchers import ( | 11 | from testtools.matchers import ( |
144 | 12 | ContainsDict, | 12 | ContainsDict, |
145 | 13 | Equals, | 13 | Equals, |
146 | @@ -22,23 +22,11 @@ from snapstore_client.logic.login import login | |||
147 | 22 | from snapstore_client.tests import factory | 22 | from snapstore_client.tests import factory |
148 | 23 | 23 | ||
149 | 24 | 24 | ||
167 | 25 | class LoginTests(TestCase): | 25 | @pytest.mark.usefixtures("mocked_config") |
168 | 26 | def setUp(self): | 26 | class TestLogin: |
169 | 27 | super().setUp() | 27 | |
170 | 28 | self.default_gw_url = "http://store.local/" | 28 | default_gw_url = "http://store.local/" |
171 | 29 | self.default_sso_url = "https://login.staging.ubuntu.com/" | 29 | default_sso_url = "https://login.staging.ubuntu.com/" |
155 | 30 | self.logger = self.useFixture(fixtures.FakeLogger()) | ||
156 | 31 | self.config_path = self.useFixture(fixtures.TempDir()).path | ||
157 | 32 | self.useFixture( | ||
158 | 33 | fixtures.MonkeyPatch("xdg.BaseDirectory.xdg_config_home", self.config_path) | ||
159 | 34 | ) | ||
160 | 35 | self.useFixture( | ||
161 | 36 | fixtures.MonkeyPatch( | ||
162 | 37 | "xdg.BaseDirectory.xdg_config_dirs", [self.config_path] | ||
163 | 38 | ) | ||
164 | 39 | ) | ||
165 | 40 | self.mock_input = self.useFixture(fixtures.MockPatch("builtins.input")).mock | ||
166 | 41 | self.mock_getpass = self.useFixture(fixtures.MockPatch("getpass.getpass")).mock | ||
172 | 42 | 30 | ||
173 | 43 | def make_responses_callback(self, response_templates): | 31 | def make_responses_callback(self, response_templates): |
174 | 44 | full_responses = [] | 32 | full_responses = [] |
175 | @@ -86,21 +74,21 @@ class LoginTests(TestCase): | |||
176 | 86 | return macaroon.serialize() | 74 | return macaroon.serialize() |
177 | 87 | 75 | ||
178 | 88 | @responses.activate | 76 | @responses.activate |
182 | 89 | def test_login_sso_mismatch(self): | 77 | def test_login_sso_mismatch(self, mock_input, mock_getpass): |
183 | 90 | self.mock_input.return_value = "user@example.org" | 78 | mock_input.return_value = "user@example.org" |
184 | 91 | self.mock_getpass.return_value = "secret" | 79 | mock_getpass.returnvalue = "secret" |
185 | 92 | macaroon = Macaroon() | 80 | macaroon = Macaroon() |
186 | 93 | macaroon.add_third_party_caveat("another.example.com", "", "") | 81 | macaroon.add_third_party_caveat("another.example.com", "", "") |
187 | 94 | self.add_issue_store_admin_response( | 82 | self.add_issue_store_admin_response( |
188 | 95 | {"status": 200, "json": {"macaroon": macaroon.serialize()}} | 83 | {"status": 200, "json": {"macaroon": macaroon.serialize()}} |
189 | 96 | ) | 84 | ) |
190 | 97 | self.add_get_sso_discharge_response({"status": 401}) | 85 | self.add_get_sso_discharge_response({"status": 401}) |
192 | 98 | self.assertRaises(exceptions.StoreMacaroonSSOMismatch, login, self.make_args()) | 86 | pytest.raises(exceptions.StoreMacaroonSSOMismatch, login, self.make_args()) |
193 | 99 | 87 | ||
194 | 100 | @responses.activate | 88 | @responses.activate |
198 | 101 | def test_login_sso_bad_email(self): | 89 | def test_login_sso_bad_email(self, mock_input, mock_getpass, caplog): |
199 | 102 | self.mock_input.return_value = "" | 90 | mock_input.return_value = "" |
200 | 103 | self.mock_getpass.return_value = "" | 91 | mock_getpass.return_value = "" |
201 | 104 | self.add_issue_store_admin_response( | 92 | self.add_issue_store_admin_response( |
202 | 105 | {"status": 200, "json": {"macaroon": self.make_root_macaroon()}} | 93 | {"status": 200, "json": {"macaroon": self.make_root_macaroon()}} |
203 | 106 | ) | 94 | ) |
204 | @@ -111,19 +99,17 @@ class LoginTests(TestCase): | |||
205 | 111 | self.add_get_sso_discharge_response( | 99 | self.add_get_sso_discharge_response( |
206 | 112 | {"status": 401, "json": {"error_list": [auth_error]}} | 100 | {"status": 401, "json": {"error_list": [auth_error]}} |
207 | 113 | ) | 101 | ) |
216 | 114 | self.assertEqual(1, login(self.make_args())) | 102 | assert 1 == login(self.make_args()) |
217 | 115 | self.assertEqual( | 103 | assert [ |
218 | 116 | "Enter your Ubuntu One SSO credentials.\n" | 104 | "Login failed.", |
219 | 117 | "Login failed.\n" | 105 | "Authentication error: Invalid request data", |
220 | 118 | "Authentication error: Invalid request data\n" | 106 | "email: Enter a valid email address.", |
221 | 119 | "email: Enter a valid email address.\n", | 107 | ] == caplog.messages |
214 | 120 | self.logger.output, | ||
215 | 121 | ) | ||
222 | 122 | 108 | ||
223 | 123 | @responses.activate | 109 | @responses.activate |
227 | 124 | def test_login_sso_unauthorized(self): | 110 | def test_login_sso_unauthorized(self, mock_input, mock_getpass, caplog): |
228 | 125 | self.mock_input.return_value = "user@example.org" | 111 | mock_input.return_value = "user@example.org" |
229 | 126 | self.mock_getpass.return_value = "secret" | 112 | mock_getpass.return_value = "secret" |
230 | 127 | self.add_issue_store_admin_response( | 113 | self.add_issue_store_admin_response( |
231 | 128 | {"status": 200, "json": {"macaroon": self.make_root_macaroon()}} | 114 | {"status": 200, "json": {"macaroon": self.make_root_macaroon()}} |
232 | 129 | ) | 115 | ) |
233 | @@ -131,51 +117,41 @@ class LoginTests(TestCase): | |||
234 | 131 | self.add_get_sso_discharge_response( | 117 | self.add_get_sso_discharge_response( |
235 | 132 | {"status": 401, "json": {"error_list": [auth_error]}} | 118 | {"status": 401, "json": {"error_list": [auth_error]}} |
236 | 133 | ) | 119 | ) |
244 | 134 | self.assertEqual(1, login(self.make_args())) | 120 | assert 1 == login(self.make_args()) |
245 | 135 | self.assertEqual( | 121 | assert [ |
246 | 136 | "Enter your Ubuntu One SSO credentials.\n" | 122 | "Login failed.", |
247 | 137 | "Login failed.\n" | 123 | "Authentication error: Provided email/password is not correct.", |
248 | 138 | "Authentication error: Provided email/password is not correct.\n", | 124 | ] == caplog.messages |
242 | 139 | self.logger.output, | ||
243 | 140 | ) | ||
249 | 141 | 125 | ||
250 | 142 | @responses.activate | 126 | @responses.activate |
254 | 143 | def test_login_twofactor_required(self): | 127 | def test_login_twofactor_required(self, mock_input, mock_getpass): |
255 | 144 | self.mock_input.side_effect = ("user@example.org", "123456") | 128 | mock_input.side_effect = ("user@example.org", "123456") |
256 | 145 | self.mock_getpass.return_value = "secret" | 129 | mock_getpass.return_value = "secret" |
257 | 146 | root = self.make_root_macaroon() | 130 | root = self.make_root_macaroon() |
258 | 147 | self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}}) | 131 | self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}}) |
259 | 148 | self.add_get_sso_discharge_response( | 132 | self.add_get_sso_discharge_response( |
260 | 149 | {"status": 401, "json": {"error_list": [{"code": "twofactor-required"}]}}, | 133 | {"status": 401, "json": {"error_list": [{"code": "twofactor-required"}]}}, |
261 | 150 | {"status": 200, "json": {"discharge_macaroon": "dummy"}}, | 134 | {"status": 200, "json": {"discharge_macaroon": "dummy"}}, |
262 | 151 | ) | 135 | ) |
264 | 152 | self.assertEqual(0, login(self.make_args())) | 136 | assert 0 == login(self.make_args()) |
265 | 153 | 137 | ||
268 | 154 | self.assertIn("Enter your Ubuntu One SSO credentials.", self.logger.output) | 138 | mock_input.assert_has_calls( |
267 | 155 | self.mock_input.assert_has_calls( | ||
269 | 156 | [mock.call("Email: "), mock.call("Second-factor auth: ")] | 139 | [mock.call("Email: "), mock.call("Second-factor auth: ")] |
270 | 157 | ) | 140 | ) |
292 | 158 | self.mock_getpass.assert_called_once_with("Password: ") | 141 | mock_getpass.assert_called_once_with("Password: ") |
293 | 159 | self.assertEqual(3, len(responses.calls)) | 142 | assert 3 == len(responses.calls) |
294 | 160 | self.assertEqual( | 143 | assert { |
295 | 161 | { | 144 | "email": "user@example.org", |
296 | 162 | "email": "user@example.org", | 145 | "password": "secret", |
297 | 163 | "password": "secret", | 146 | "caveat_id": "payload", |
298 | 164 | "caveat_id": "payload", | 147 | } == json.loads(responses.calls[1].request.body.decode()) |
299 | 165 | }, | 148 | assert { |
300 | 166 | json.loads(responses.calls[1].request.body.decode()), | 149 | "email": "user@example.org", |
301 | 167 | ) | 150 | "password": "secret", |
302 | 168 | self.assertEqual( | 151 | "caveat_id": "payload", |
303 | 169 | { | 152 | "otp": "123456", |
304 | 170 | "email": "user@example.org", | 153 | } == json.loads(responses.calls[2].request.body.decode()) |
305 | 171 | "password": "secret", | 154 | assert ( |
285 | 172 | "caveat_id": "payload", | ||
286 | 173 | "otp": "123456", | ||
287 | 174 | }, | ||
288 | 175 | json.loads(responses.calls[2].request.body.decode()), | ||
289 | 176 | ) | ||
290 | 177 | self.assertThat( | ||
291 | 178 | config.Config().parser, | ||
306 | 179 | ContainsDict( | 155 | ContainsDict( |
307 | 180 | { | 156 | { |
308 | 181 | "store:default": MatchesDict( | 157 | "store:default": MatchesDict( |
309 | @@ -188,13 +164,14 @@ class LoginTests(TestCase): | |||
310 | 188 | } | 164 | } |
311 | 189 | ), | 165 | ), |
312 | 190 | } | 166 | } |
314 | 191 | ), | 167 | ).match(config.Config().parser) |
315 | 168 | is None | ||
316 | 192 | ) | 169 | ) |
317 | 193 | 170 | ||
318 | 194 | @responses.activate | 171 | @responses.activate |
322 | 195 | def test_login_twofactor_not_required(self): | 172 | def test_login_twofactor_not_required(self, mock_input, mock_getpass): |
323 | 196 | self.mock_input.return_value = "user@example.org" | 173 | mock_input.return_value = "user@example.org" |
324 | 197 | self.mock_getpass.return_value = "secret" | 174 | mock_getpass.return_value = "secret" |
325 | 198 | root = self.make_root_macaroon() | 175 | root = self.make_root_macaroon() |
326 | 199 | self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}}) | 176 | self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}}) |
327 | 200 | self.add_get_sso_discharge_response( | 177 | self.add_get_sso_discharge_response( |
328 | @@ -202,20 +179,15 @@ class LoginTests(TestCase): | |||
329 | 202 | ) | 179 | ) |
330 | 203 | login(self.make_args()) | 180 | login(self.make_args()) |
331 | 204 | 181 | ||
346 | 205 | self.assertIn("Enter your Ubuntu One SSO credentials.", self.logger.output) | 182 | mock_input.assert_called_once_with("Email: ") |
347 | 206 | self.mock_input.assert_called_once_with("Email: ") | 183 | mock_getpass.assert_called_once_with("Password: ") |
348 | 207 | self.mock_getpass.assert_called_once_with("Password: ") | 184 | assert 2 == len(responses.calls) |
349 | 208 | self.assertEqual(2, len(responses.calls)) | 185 | assert { |
350 | 209 | self.assertEqual( | 186 | "email": "user@example.org", |
351 | 210 | { | 187 | "password": "secret", |
352 | 211 | "email": "user@example.org", | 188 | "caveat_id": "payload", |
353 | 212 | "password": "secret", | 189 | } == json.loads(responses.calls[1].request.body.decode()) |
354 | 213 | "caveat_id": "payload", | 190 | assert ( |
341 | 214 | }, | ||
342 | 215 | json.loads(responses.calls[1].request.body.decode()), | ||
343 | 216 | ) | ||
344 | 217 | self.assertThat( | ||
345 | 218 | config.Config().parser, | ||
355 | 219 | ContainsDict( | 191 | ContainsDict( |
356 | 220 | { | 192 | { |
357 | 221 | "store:default": MatchesDict( | 193 | "store:default": MatchesDict( |
358 | @@ -228,13 +200,14 @@ class LoginTests(TestCase): | |||
359 | 228 | } | 200 | } |
360 | 229 | ), | 201 | ), |
361 | 230 | } | 202 | } |
363 | 231 | ), | 203 | ).match(config.Config().parser) |
364 | 204 | is None | ||
365 | 232 | ) | 205 | ) |
366 | 233 | 206 | ||
367 | 234 | @responses.activate | 207 | @responses.activate |
371 | 235 | def test_login_with_email(self): | 208 | def test_login_with_email(self, mock_input, mock_getpass): |
372 | 236 | self.mock_input.side_effect = Exception("shouldn't be called") | 209 | mock_input.side_effect = Exception("shouldn't be called") |
373 | 237 | self.mock_getpass.return_value = "secret" | 210 | mock_getpass.return_value = "secret" |
374 | 238 | root = self.make_root_macaroon() | 211 | root = self.make_root_macaroon() |
375 | 239 | self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}}) | 212 | self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}}) |
376 | 240 | self.add_get_sso_discharge_response( | 213 | self.add_get_sso_discharge_response( |
377 | @@ -242,20 +215,15 @@ class LoginTests(TestCase): | |||
378 | 242 | ) | 215 | ) |
379 | 243 | login(self.make_args(email="user@example.org")) | 216 | login(self.make_args(email="user@example.org")) |
380 | 244 | 217 | ||
395 | 245 | self.assertIn("Enter your Ubuntu One SSO credentials.", self.logger.output) | 218 | mock_input.assert_not_called() |
396 | 246 | self.mock_input.assert_not_called() | 219 | mock_getpass.assert_called_once_with("Password: ") |
397 | 247 | self.mock_getpass.assert_called_once_with("Password: ") | 220 | assert 2 == len(responses.calls) |
398 | 248 | self.assertEqual(2, len(responses.calls)) | 221 | assert { |
399 | 249 | self.assertEqual( | 222 | "email": "user@example.org", |
400 | 250 | { | 223 | "password": "secret", |
401 | 251 | "email": "user@example.org", | 224 | "caveat_id": "payload", |
402 | 252 | "password": "secret", | 225 | } == json.loads(responses.calls[1].request.body.decode()) |
403 | 253 | "caveat_id": "payload", | 226 | assert ( |
390 | 254 | }, | ||
391 | 255 | json.loads(responses.calls[1].request.body.decode()), | ||
392 | 256 | ) | ||
393 | 257 | self.assertThat( | ||
394 | 258 | config.Config().parser, | ||
404 | 259 | ContainsDict( | 227 | ContainsDict( |
405 | 260 | { | 228 | { |
406 | 261 | "store:default": MatchesDict( | 229 | "store:default": MatchesDict( |
407 | @@ -268,15 +236,16 @@ class LoginTests(TestCase): | |||
408 | 268 | } | 236 | } |
409 | 269 | ), | 237 | ), |
410 | 270 | } | 238 | } |
412 | 271 | ), | 239 | ).match(config.Config().parser) |
413 | 240 | is None | ||
414 | 272 | ) | 241 | ) |
415 | 273 | 242 | ||
416 | 274 | @responses.activate | 243 | @responses.activate |
418 | 275 | def test_store_url(self): | 244 | def test_store_url(self, mock_input, mock_getpass): |
419 | 276 | gw_url = "http://otherstore.local:1234/" | 245 | gw_url = "http://otherstore.local:1234/" |
420 | 277 | 246 | ||
423 | 278 | self.mock_input.return_value = "user@example.org" | 247 | mock_input.return_value = "user@example.org" |
424 | 279 | self.mock_getpass.return_value = "secret" | 248 | mock_getpass.return_value = "secret" |
425 | 280 | root = self.make_root_macaroon() | 249 | root = self.make_root_macaroon() |
426 | 281 | self.add_issue_store_admin_response( | 250 | self.add_issue_store_admin_response( |
427 | 282 | {"status": 200, "json": {"macaroon": root}}, gw_url=gw_url | 251 | {"status": 200, "json": {"macaroon": root}}, gw_url=gw_url |
428 | @@ -286,11 +255,10 @@ class LoginTests(TestCase): | |||
429 | 286 | ) | 255 | ) |
430 | 287 | login(self.make_args(store_url=gw_url)) | 256 | login(self.make_args(store_url=gw_url)) |
431 | 288 | 257 | ||
437 | 289 | self.assertEqual(2, len(responses.calls)) | 258 | assert 2 == len(responses.calls) |
438 | 290 | self.assertEqual(responses.calls[0].request.url[: len(gw_url)], gw_url) | 259 | assert responses.calls[0].request.url[: len(gw_url)] == gw_url |
439 | 291 | self.assertTrue(responses.calls[1].request.url.startswith(self.default_sso_url)) | 260 | assert responses.calls[1].request.url.startswith(self.default_sso_url) |
440 | 292 | self.assertThat( | 261 | assert ( |
436 | 293 | config.Config().parser, | ||
441 | 294 | ContainsDict( | 262 | ContainsDict( |
442 | 295 | { | 263 | { |
443 | 296 | "store:default": ContainsDict( | 264 | "store:default": ContainsDict( |
444 | @@ -300,15 +268,16 @@ class LoginTests(TestCase): | |||
445 | 300 | } | 268 | } |
446 | 301 | ), | 269 | ), |
447 | 302 | } | 270 | } |
449 | 303 | ), | 271 | ).match(config.Config().parser) |
450 | 272 | is None | ||
451 | 304 | ) | 273 | ) |
452 | 305 | 274 | ||
453 | 306 | @responses.activate | 275 | @responses.activate |
455 | 307 | def test_sso_url(self): | 276 | def test_sso_url(self, mock_input, mock_getpass): |
456 | 308 | sso_url = "https://othersso.local:1234/" | 277 | sso_url = "https://othersso.local:1234/" |
457 | 309 | 278 | ||
460 | 310 | self.mock_input.return_value = "user@example.org" | 279 | mock_input.return_value = "user@example.org" |
461 | 311 | self.mock_getpass.return_value = "secret" | 280 | mock_getpass.return_value = "secret" |
462 | 312 | root = self.make_root_macaroon(sso_url=sso_url) | 281 | root = self.make_root_macaroon(sso_url=sso_url) |
463 | 313 | self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}}) | 282 | self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}}) |
464 | 314 | self.add_get_sso_discharge_response( | 283 | self.add_get_sso_discharge_response( |
465 | @@ -316,11 +285,10 @@ class LoginTests(TestCase): | |||
466 | 316 | ) | 285 | ) |
467 | 317 | login(self.make_args(sso_url=sso_url)) | 286 | login(self.make_args(sso_url=sso_url)) |
468 | 318 | 287 | ||
474 | 319 | self.assertEqual(2, len(responses.calls)) | 288 | assert 2 == len(responses.calls) |
475 | 320 | self.assertTrue(responses.calls[0].request.url.startswith(self.default_gw_url)) | 289 | assert responses.calls[0].request.url.startswith(self.default_gw_url) |
476 | 321 | self.assertEqual(responses.calls[1].request.url[: len(sso_url)], sso_url) | 290 | assert responses.calls[1].request.url[: len(sso_url)] == sso_url |
477 | 322 | self.assertThat( | 291 | assert ( |
473 | 323 | config.Config().parser, | ||
478 | 324 | ContainsDict( | 292 | ContainsDict( |
479 | 325 | { | 293 | { |
480 | 326 | "store:default": ContainsDict( | 294 | "store:default": ContainsDict( |
481 | @@ -330,5 +298,6 @@ class LoginTests(TestCase): | |||
482 | 330 | } | 298 | } |
483 | 331 | ), | 299 | ), |
484 | 332 | } | 300 | } |
486 | 333 | ), | 301 | ).match(config.Config().parser) |
487 | 302 | is None | ||
488 | 334 | ) | 303 | ) |
489 | diff --git a/snapstore_client/logic/tests/test_overrides.py b/snapstore_client/logic/tests/test_overrides.py | |||
490 | index 591b55d..c628810 100644 | |||
491 | --- a/snapstore_client/logic/tests/test_overrides.py | |||
492 | +++ b/snapstore_client/logic/tests/test_overrides.py | |||
493 | @@ -1,11 +1,11 @@ | |||
494 | 1 | # Copyright 2017 Canonical Ltd. | 1 | # Copyright 2017 Canonical Ltd. |
495 | 2 | 2 | ||
496 | 3 | import json | 3 | import json |
497 | 4 | import logging | ||
498 | 4 | from urllib.parse import urljoin | 5 | from urllib.parse import urljoin |
499 | 5 | 6 | ||
500 | 6 | import fixtures | ||
501 | 7 | import responses | 7 | import responses |
503 | 8 | from testtools import TestCase | 8 | import pytest |
504 | 9 | 9 | ||
505 | 10 | from snapstore_client import config | 10 | from snapstore_client import config |
506 | 11 | from snapstore_client.logic.overrides import ( | 11 | from snapstore_client.logic.overrides import ( |
507 | @@ -15,26 +15,23 @@ from snapstore_client.logic.overrides import ( | |||
508 | 15 | ) | 15 | ) |
509 | 16 | from snapstore_client.tests import ( | 16 | from snapstore_client.tests import ( |
510 | 17 | factory, | 17 | factory, |
511 | 18 | testfixtures, | ||
512 | 19 | ) | 18 | ) |
513 | 20 | 19 | ||
514 | 21 | 20 | ||
519 | 22 | class OverridesTests(TestCase): | 21 | class TestOverrides: |
520 | 23 | def test_list_overrides_no_store_config(self): | 22 | @pytest.mark.usefixtures("mocked_empty_config") |
521 | 24 | self.useFixture(testfixtures.ConfigFixture(empty=True)) | 23 | def test_list_overrides_no_store_config(self, caplog): |
518 | 25 | logger = self.useFixture(fixtures.FakeLogger()) | ||
522 | 26 | rc = list_overrides(factory.Args(snap_name="some-snap", series="16")) | 24 | rc = list_overrides(factory.Args(snap_name="some-snap", series="16")) |
526 | 27 | self.assertEqual(rc, 1) | 25 | assert rc == 1 |
527 | 28 | self.assertEqual( | 26 | assert [ |
525 | 29 | logger.output, | ||
528 | 30 | "No store configuration found. " | 27 | "No store configuration found. " |
531 | 31 | 'Have you run "snap-store-proxy-client login"?\n', | 28 | 'Have you run "snap-store-proxy-client login"?' |
532 | 32 | ) | 29 | ] == caplog.messages |
533 | 33 | 30 | ||
534 | 31 | @pytest.mark.usefixtures("mocked_config") | ||
535 | 34 | @responses.activate | 32 | @responses.activate |
539 | 35 | def test_list_overrides_online(self): | 33 | def test_list_overrides_online(self, caplog): |
540 | 36 | self.useFixture(testfixtures.ConfigFixture()) | 34 | caplog.set_level(logging.DEBUG) |
538 | 37 | logger = self.useFixture(fixtures.FakeLogger()) | ||
541 | 38 | snap_id = factory.generate_snap_id() | 35 | snap_id = factory.generate_snap_id() |
542 | 39 | overrides = [ | 36 | overrides = [ |
543 | 40 | factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"), | 37 | factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"), |
544 | @@ -56,18 +53,17 @@ class OverridesTests(TestCase): | |||
545 | 56 | responses.add("GET", overrides_url, status=200, json={"overrides": overrides}) | 53 | responses.add("GET", overrides_url, status=200, json={"overrides": overrides}) |
546 | 57 | 54 | ||
547 | 58 | list_overrides(factory.Args(snap_name="mysnap", series="16", password=False)) | 55 | list_overrides(factory.Args(snap_name="mysnap", series="16", password=False)) |
553 | 59 | self.assertEqual( | 56 | assert [ |
554 | 60 | "mysnap stable amd64 1 (upstream 2)\n" | 57 | "mysnap stable amd64 1 (upstream 2)", |
555 | 61 | "mysnap foo/stable i386 3 (upstream 4)\n", | 58 | "mysnap foo/stable i386 3 (upstream 4)", |
556 | 62 | logger.output, | 59 | ] == caplog.messages |
552 | 63 | ) | ||
557 | 64 | # We shouldn't have Basic Authorization headers, but Macaroon | 60 | # We shouldn't have Basic Authorization headers, but Macaroon |
559 | 65 | self.assertNotIn("Basic", responses.calls[0].request.headers["Authorization"]) | 61 | assert "Basic" not in responses.calls[0].request.headers["Authorization"] |
560 | 66 | 62 | ||
561 | 63 | @pytest.mark.usefixtures("mocked_config") | ||
562 | 67 | @responses.activate | 64 | @responses.activate |
566 | 68 | def test_list_overrides_offline(self): | 65 | def test_list_overrides_offline(self, caplog): |
567 | 69 | self.useFixture(testfixtures.ConfigFixture()) | 66 | caplog.set_level(logging.DEBUG) |
565 | 70 | logger = self.useFixture(fixtures.FakeLogger()) | ||
568 | 71 | snap_id = factory.generate_snap_id() | 67 | snap_id = factory.generate_snap_id() |
569 | 72 | overrides = [ | 68 | overrides = [ |
570 | 73 | factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"), | 69 | factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"), |
571 | @@ -89,19 +85,17 @@ class OverridesTests(TestCase): | |||
572 | 89 | responses.add("GET", overrides_url, status=200, json={"overrides": overrides}) | 85 | responses.add("GET", overrides_url, status=200, json={"overrides": overrides}) |
573 | 90 | 86 | ||
574 | 91 | list_overrides(factory.Args(snap_name="mysnap", series="16", password="test")) | 87 | list_overrides(factory.Args(snap_name="mysnap", series="16", password="test")) |
583 | 92 | self.assertEqual( | 88 | assert [ |
584 | 93 | "mysnap stable amd64 1 (upstream 2)\n" | 89 | "mysnap stable amd64 1 (upstream 2)", |
585 | 94 | "mysnap foo/stable i386 3 (upstream 4)\n", | 90 | "mysnap foo/stable i386 3 (upstream 4)", |
586 | 95 | logger.output, | 91 | ] == caplog.messages |
587 | 96 | ) | 92 | assert ( |
588 | 97 | self.assertEqual( | 93 | "Basic YWRtaW46dGVzdA==" |
589 | 98 | "Basic YWRtaW46dGVzdA==", | 94 | == responses.calls[0].request.headers["Authorization"] |
582 | 99 | responses.calls[0].request.headers["Authorization"], | ||
590 | 100 | ) | 95 | ) |
591 | 101 | 96 | ||
595 | 102 | def test_override_no_store_config(self): | 97 | @pytest.mark.usefixtures("mocked_empty_config") |
596 | 103 | self.useFixture(testfixtures.ConfigFixture(empty=True)) | 98 | def test_override_no_store_config(self, caplog): |
594 | 104 | logger = self.useFixture(fixtures.FakeLogger()) | ||
597 | 105 | rc = override( | 99 | rc = override( |
598 | 106 | factory.Args( | 100 | factory.Args( |
599 | 107 | snap_name="some-snap", | 101 | snap_name="some-snap", |
600 | @@ -110,17 +104,16 @@ class OverridesTests(TestCase): | |||
601 | 110 | password=False, | 104 | password=False, |
602 | 111 | ) | 105 | ) |
603 | 112 | ) | 106 | ) |
607 | 113 | self.assertEqual(rc, 1) | 107 | assert rc == 1 |
608 | 114 | self.assertEqual( | 108 | assert [ |
606 | 115 | logger.output, | ||
609 | 116 | "No store configuration found. " | 109 | "No store configuration found. " |
612 | 117 | 'Have you run "snap-store-proxy-client login"?\n', | 110 | 'Have you run "snap-store-proxy-client login"?' |
613 | 118 | ) | 111 | ] == caplog.messages |
614 | 119 | 112 | ||
615 | 113 | @pytest.mark.usefixtures("mocked_config") | ||
616 | 120 | @responses.activate | 114 | @responses.activate |
620 | 121 | def test_override_online(self): | 115 | def test_override_online(self, caplog): |
621 | 122 | self.useFixture(testfixtures.ConfigFixture()) | 116 | caplog.set_level(logging.DEBUG) |
619 | 123 | logger = self.useFixture(fixtures.FakeLogger()) | ||
622 | 124 | snap_id = factory.generate_snap_id() | 117 | snap_id = factory.generate_snap_id() |
623 | 125 | overrides = [ | 118 | overrides = [ |
624 | 126 | factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"), | 119 | factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"), |
625 | @@ -149,35 +142,32 @@ class OverridesTests(TestCase): | |||
626 | 149 | password=False, | 142 | password=False, |
627 | 150 | ) | 143 | ) |
628 | 151 | ) | 144 | ) |
651 | 152 | self.assertEqual( | 145 | assert [ |
652 | 153 | [ | 146 | { |
653 | 154 | { | 147 | "snap_name": "mysnap", |
654 | 155 | "snap_name": "mysnap", | 148 | "revision": 1, |
655 | 156 | "revision": 1, | 149 | "channel": "stable", |
656 | 157 | "channel": "stable", | 150 | "series": "16", |
657 | 158 | "series": "16", | 151 | }, |
658 | 159 | }, | 152 | { |
659 | 160 | { | 153 | "snap_name": "mysnap", |
660 | 161 | "snap_name": "mysnap", | 154 | "revision": 3, |
661 | 162 | "revision": 3, | 155 | "channel": "foo/stable", |
662 | 163 | "channel": "foo/stable", | 156 | "series": "16", |
663 | 164 | "series": "16", | 157 | }, |
664 | 165 | }, | 158 | ] == json.loads(responses.calls[0].request.body.decode()) |
665 | 166 | ], | 159 | assert [ |
666 | 167 | json.loads(responses.calls[0].request.body.decode()), | 160 | "mysnap stable amd64 1 (upstream 2)", |
667 | 168 | ) | 161 | "mysnap foo/stable i386 3 (upstream 4)", |
668 | 169 | self.assertEqual( | 162 | ] == caplog.messages |
669 | 170 | "mysnap stable amd64 1 (upstream 2)\n" | 163 | |
648 | 171 | "mysnap foo/stable i386 3 (upstream 4)\n", | ||
649 | 172 | logger.output, | ||
650 | 173 | ) | ||
670 | 174 | # We shouldn't have Basic Authorization headers, but Macaroon | 164 | # We shouldn't have Basic Authorization headers, but Macaroon |
672 | 175 | self.assertNotIn("Basic", responses.calls[0].request.headers["Authorization"]) | 165 | assert "Basic" not in responses.calls[0].request.headers["Authorization"] |
673 | 176 | 166 | ||
674 | 167 | @pytest.mark.usefixtures("mocked_config") | ||
675 | 177 | @responses.activate | 168 | @responses.activate |
679 | 178 | def test_override_offline(self): | 169 | def test_override_offline(self, caplog): |
680 | 179 | self.useFixture(testfixtures.ConfigFixture()) | 170 | caplog.set_level(logging.DEBUG) |
678 | 180 | logger = self.useFixture(fixtures.FakeLogger()) | ||
681 | 181 | snap_id = factory.generate_snap_id() | 171 | snap_id = factory.generate_snap_id() |
682 | 182 | overrides = [ | 172 | overrides = [ |
683 | 183 | factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"), | 173 | factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"), |
684 | @@ -206,52 +196,46 @@ class OverridesTests(TestCase): | |||
685 | 206 | password="test", | 196 | password="test", |
686 | 207 | ) | 197 | ) |
687 | 208 | ) | 198 | ) |
713 | 209 | self.assertEqual( | 199 | assert [ |
714 | 210 | [ | 200 | { |
715 | 211 | { | 201 | "snap_name": "mysnap", |
716 | 212 | "snap_name": "mysnap", | 202 | "revision": 1, |
717 | 213 | "revision": 1, | 203 | "channel": "stable", |
718 | 214 | "channel": "stable", | 204 | "series": "16", |
719 | 215 | "series": "16", | 205 | }, |
720 | 216 | }, | 206 | { |
721 | 217 | { | 207 | "snap_name": "mysnap", |
722 | 218 | "snap_name": "mysnap", | 208 | "revision": 3, |
723 | 219 | "revision": 3, | 209 | "channel": "foo/stable", |
724 | 220 | "channel": "foo/stable", | 210 | "series": "16", |
725 | 221 | "series": "16", | 211 | }, |
726 | 222 | }, | 212 | ] == json.loads(responses.calls[0].request.body.decode()) |
727 | 223 | ], | 213 | assert [ |
728 | 224 | json.loads(responses.calls[0].request.body.decode()), | 214 | "mysnap stable amd64 1 (upstream 2)", |
729 | 225 | ) | 215 | "mysnap foo/stable i386 3 (upstream 4)", |
730 | 226 | self.assertEqual( | 216 | ] == caplog.messages |
731 | 227 | "mysnap stable amd64 1 (upstream 2)\n" | 217 | assert ( |
732 | 228 | "mysnap foo/stable i386 3 (upstream 4)\n", | 218 | "Basic YWRtaW46dGVzdA==" |
733 | 229 | logger.output, | 219 | == responses.calls[0].request.headers["Authorization"] |
709 | 230 | ) | ||
710 | 231 | self.assertEqual( | ||
711 | 232 | "Basic YWRtaW46dGVzdA==", | ||
712 | 233 | responses.calls[0].request.headers["Authorization"], | ||
734 | 234 | ) | 220 | ) |
735 | 235 | 221 | ||
739 | 236 | def test_delete_override_no_store_config(self): | 222 | @pytest.mark.usefixtures("mocked_empty_config") |
740 | 237 | self.useFixture(testfixtures.ConfigFixture(empty=True)) | 223 | def test_delete_override_no_store_config(self, caplog): |
738 | 238 | logger = self.useFixture(fixtures.FakeLogger()) | ||
741 | 239 | rc = delete_override( | 224 | rc = delete_override( |
742 | 240 | factory.Args( | 225 | factory.Args( |
743 | 241 | snap_name="some-snap", channels=["stable"], series="16", password=False | 226 | snap_name="some-snap", channels=["stable"], series="16", password=False |
744 | 242 | ) | 227 | ) |
745 | 243 | ) | 228 | ) |
749 | 244 | self.assertEqual(rc, 1) | 229 | assert rc == 1 |
750 | 245 | self.assertEqual( | 230 | assert [ |
748 | 246 | logger.output, | ||
751 | 247 | "No store configuration found. " | 231 | "No store configuration found. " |
754 | 248 | 'Have you run "snap-store-proxy-client login"?\n', | 232 | 'Have you run "snap-store-proxy-client login"?' |
755 | 249 | ) | 233 | ] == caplog.messages |
756 | 250 | 234 | ||
757 | 235 | @pytest.mark.usefixtures("mocked_config") | ||
758 | 251 | @responses.activate | 236 | @responses.activate |
762 | 252 | def test_delete_override_online(self): | 237 | def test_delete_override_online(self, caplog): |
763 | 253 | self.useFixture(testfixtures.ConfigFixture()) | 238 | caplog.set_level(logging.DEBUG) |
761 | 254 | logger = self.useFixture(fixtures.FakeLogger()) | ||
764 | 255 | snap_id = factory.generate_snap_id() | 239 | snap_id = factory.generate_snap_id() |
765 | 256 | overrides = [ | 240 | overrides = [ |
766 | 257 | factory.SnapDeviceGateway.Override( | 241 | factory.SnapDeviceGateway.Override( |
767 | @@ -282,35 +266,31 @@ class OverridesTests(TestCase): | |||
768 | 282 | password=False, | 266 | password=False, |
769 | 283 | ) | 267 | ) |
770 | 284 | ) | 268 | ) |
793 | 285 | self.assertEqual( | 269 | assert [ |
794 | 286 | [ | 270 | { |
795 | 287 | { | 271 | "snap_name": "mysnap", |
796 | 288 | "snap_name": "mysnap", | 272 | "revision": None, |
797 | 289 | "revision": None, | 273 | "channel": "stable", |
798 | 290 | "channel": "stable", | 274 | "series": "16", |
799 | 291 | "series": "16", | 275 | }, |
800 | 292 | }, | 276 | { |
801 | 293 | { | 277 | "snap_name": "mysnap", |
802 | 294 | "snap_name": "mysnap", | 278 | "revision": None, |
803 | 295 | "revision": None, | 279 | "channel": "foo/stable", |
804 | 296 | "channel": "foo/stable", | 280 | "series": "16", |
805 | 297 | "series": "16", | 281 | }, |
806 | 298 | }, | 282 | ] == json.loads(responses.calls[0].request.body.decode()) |
807 | 299 | ], | 283 | assert [ |
808 | 300 | json.loads(responses.calls[0].request.body.decode()), | 284 | "mysnap stable amd64 is tracking upstream (revision 2)", |
809 | 301 | ) | 285 | "mysnap foo/stable i386 is tracking upstream (revision 4)", |
810 | 302 | self.assertEqual( | 286 | ] == caplog.messages |
789 | 303 | "mysnap stable amd64 is tracking upstream (revision 2)\n" | ||
790 | 304 | "mysnap foo/stable i386 is tracking upstream (revision 4)\n", | ||
791 | 305 | logger.output, | ||
792 | 306 | ) | ||
811 | 307 | # We shouldn't have Basic Authorization headers, but Macaroon | 287 | # We shouldn't have Basic Authorization headers, but Macaroon |
813 | 308 | self.assertNotIn("Basic", responses.calls[0].request.headers["Authorization"]) | 288 | assert "Basic" not in responses.calls[0].request.headers["Authorization"] |
814 | 309 | 289 | ||
815 | 290 | @pytest.mark.usefixtures("mocked_config") | ||
816 | 310 | @responses.activate | 291 | @responses.activate |
820 | 311 | def test_delete_override_offline(self): | 292 | def test_delete_override_offline(self, caplog): |
821 | 312 | self.useFixture(testfixtures.ConfigFixture()) | 293 | caplog.set_level(logging.DEBUG) |
819 | 313 | logger = self.useFixture(fixtures.FakeLogger()) | ||
822 | 314 | snap_id = factory.generate_snap_id() | 294 | snap_id = factory.generate_snap_id() |
823 | 315 | overrides = [ | 295 | overrides = [ |
824 | 316 | factory.SnapDeviceGateway.Override( | 296 | factory.SnapDeviceGateway.Override( |
825 | @@ -341,29 +321,25 @@ class OverridesTests(TestCase): | |||
826 | 341 | password="test", | 321 | password="test", |
827 | 342 | ) | 322 | ) |
828 | 343 | ) | 323 | ) |
854 | 344 | self.assertEqual( | 324 | assert [ |
855 | 345 | [ | 325 | { |
856 | 346 | { | 326 | "snap_name": "mysnap", |
857 | 347 | "snap_name": "mysnap", | 327 | "revision": None, |
858 | 348 | "revision": None, | 328 | "channel": "stable", |
859 | 349 | "channel": "stable", | 329 | "series": "16", |
860 | 350 | "series": "16", | 330 | }, |
861 | 351 | }, | 331 | { |
862 | 352 | { | 332 | "snap_name": "mysnap", |
863 | 353 | "snap_name": "mysnap", | 333 | "revision": None, |
864 | 354 | "revision": None, | 334 | "channel": "foo/stable", |
865 | 355 | "channel": "foo/stable", | 335 | "series": "16", |
866 | 356 | "series": "16", | 336 | }, |
867 | 357 | }, | 337 | ] == json.loads(responses.calls[0].request.body.decode()) |
868 | 358 | ], | 338 | assert [ |
869 | 359 | json.loads(responses.calls[0].request.body.decode()), | 339 | "mysnap stable amd64 is tracking upstream (revision 2)", |
870 | 360 | ) | 340 | "mysnap foo/stable i386 is tracking upstream (revision 4)", |
871 | 361 | self.assertEqual( | 341 | ] == caplog.messages |
872 | 362 | "mysnap stable amd64 is tracking upstream (revision 2)\n" | 342 | assert ( |
873 | 363 | "mysnap foo/stable i386 is tracking upstream (revision 4)\n", | 343 | "Basic YWRtaW46dGVzdA==" |
874 | 364 | logger.output, | 344 | == responses.calls[0].request.headers["Authorization"] |
850 | 365 | ) | ||
851 | 366 | self.assertEqual( | ||
852 | 367 | "Basic YWRtaW46dGVzdA==", | ||
853 | 368 | responses.calls[0].request.headers["Authorization"], | ||
875 | 369 | ) | 345 | ) |
876 | diff --git a/snapstore_client/logic/tests/test_push.py b/snapstore_client/logic/tests/test_push.py | |||
877 | index e96088f..05b4cee 100644 | |||
878 | --- a/snapstore_client/logic/tests/test_push.py | |||
879 | +++ b/snapstore_client/logic/tests/test_push.py | |||
880 | @@ -1,10 +1,9 @@ | |||
881 | 1 | import pytest | ||
882 | 1 | import json | 2 | import json |
883 | 2 | from pathlib import Path | 3 | from pathlib import Path |
884 | 3 | from urllib.parse import urljoin | 4 | from urllib.parse import urljoin |
885 | 4 | 5 | ||
886 | 5 | import fixtures | ||
887 | 6 | import responses | 6 | import responses |
888 | 7 | from testtools import TestCase | ||
889 | 8 | 7 | ||
890 | 9 | from snapstore_client.logic.push import ( | 8 | from snapstore_client.logic.push import ( |
891 | 10 | ChannelMapExistsException, | 9 | ChannelMapExistsException, |
892 | @@ -17,11 +16,9 @@ from snapstore_client.logic.push import ( | |||
893 | 17 | ) | 16 | ) |
894 | 18 | 17 | ||
895 | 19 | 18 | ||
899 | 20 | class pushTests(TestCase): | 19 | class Testpush: |
900 | 21 | def setUp(self): | 20 | def setup_method(self): |
898 | 22 | super().setUp() | ||
901 | 23 | self.default_gw_url = "http://store.local" | 21 | self.default_gw_url = "http://store.local" |
902 | 24 | self.logger = self.useFixture(fixtures.FakeLogger()) | ||
903 | 25 | current_path = Path(__file__).resolve().parent | 22 | current_path = Path(__file__).resolve().parent |
904 | 26 | with (current_path / "test-snap-map.json").open() as fh: | 23 | with (current_path / "test-snap-map.json").open() as fh: |
905 | 27 | self.snap_map = json.load(fh) | 24 | self.snap_map = json.load(fh) |
906 | @@ -39,21 +36,15 @@ class pushTests(TestCase): | |||
907 | 39 | request = responses.calls[0].request | 36 | request = responses.calls[0].request |
908 | 40 | payload = json.loads(request.body.decode("utf-8")) | 37 | payload = json.loads(request.body.decode("utf-8")) |
909 | 41 | 38 | ||
918 | 42 | self.assertEqual( | 39 | assert payload["snaps"][0]["package_type"] == "snap" |
919 | 43 | payload["snaps"][0]["package_type"], | 40 | assert "Basic YWRtaW46dGVzdA==" == request.headers["Authorization"] |
912 | 44 | "snap", | ||
913 | 45 | ) | ||
914 | 46 | self.assertEqual( | ||
915 | 47 | "Basic YWRtaW46dGVzdA==", | ||
916 | 48 | request.headers["Authorization"], | ||
917 | 49 | ) | ||
920 | 50 | 41 | ||
921 | 51 | @responses.activate | 42 | @responses.activate |
922 | 52 | def test_push_ident_failed(self): | 43 | def test_push_ident_failed(self): |
923 | 53 | ident_url = urljoin(self.default_gw_url, "/snaps/update") | 44 | ident_url = urljoin(self.default_gw_url, "/snaps/update") |
924 | 54 | responses.add("POST", ident_url, status=500) | 45 | responses.add("POST", ident_url, status=500) |
925 | 55 | 46 | ||
927 | 56 | self.assertRaises(pushException, _push_ident, self.store, "test", self.snap_map) | 47 | pytest.raises(pushException, _push_ident, self.store, "test", self.snap_map) |
928 | 57 | 48 | ||
929 | 58 | @responses.activate | 49 | @responses.activate |
930 | 59 | def test_push_revs(self): | 50 | def test_push_revs(self): |
931 | @@ -65,25 +56,22 @@ class pushTests(TestCase): | |||
932 | 65 | request = responses.calls[0].request | 56 | request = responses.calls[0].request |
933 | 66 | payload = json.loads(request.body.decode("utf-8")) | 57 | payload = json.loads(request.body.decode("utf-8")) |
934 | 67 | 58 | ||
940 | 68 | self.assertEqual(payload[0]["package_type"], "snap") | 59 | assert payload[0]["package_type"] == "snap" |
941 | 69 | self.assertEqual( | 60 | assert "Basic YWRtaW46dGVzdA==" == request.headers["Authorization"] |
937 | 70 | "Basic YWRtaW46dGVzdA==", | ||
938 | 71 | request.headers["Authorization"], | ||
939 | 72 | ) | ||
942 | 73 | 61 | ||
943 | 74 | @responses.activate | 62 | @responses.activate |
944 | 75 | def test_push_revs_unexpected_status_code(self): | 63 | def test_push_revs_unexpected_status_code(self): |
945 | 76 | revs_url = urljoin(self.default_gw_url, "/revisions/create") | 64 | revs_url = urljoin(self.default_gw_url, "/revisions/create") |
946 | 77 | responses.add("POST", revs_url, status=302) | 65 | responses.add("POST", revs_url, status=302) |
947 | 78 | 66 | ||
949 | 79 | self.assertRaises(pushException, _push_revs, self.store, "test", self.snap_map) | 67 | pytest.raises(pushException, _push_revs, self.store, "test", self.snap_map) |
950 | 80 | 68 | ||
951 | 81 | @responses.activate | 69 | @responses.activate |
952 | 82 | def test_push_revs_failed(self): | 70 | def test_push_revs_failed(self): |
953 | 83 | revs_url = urljoin(self.default_gw_url, "/revisions/create") | 71 | revs_url = urljoin(self.default_gw_url, "/revisions/create") |
954 | 84 | responses.add("POST", revs_url, status=500) | 72 | responses.add("POST", revs_url, status=500) |
955 | 85 | 73 | ||
957 | 86 | self.assertRaises(pushException, _push_revs, self.store, "test", self.snap_map) | 74 | pytest.raises(pushException, _push_revs, self.store, "test", self.snap_map) |
958 | 87 | 75 | ||
959 | 88 | @responses.activate | 76 | @responses.activate |
960 | 89 | def test_push_map(self): | 77 | def test_push_map(self): |
961 | @@ -96,29 +84,20 @@ class pushTests(TestCase): | |||
962 | 96 | 84 | ||
963 | 97 | filter_request = responses.calls[0].request | 85 | filter_request = responses.calls[0].request |
964 | 98 | filter_payload = json.loads(filter_request.body.decode("utf-8")) | 86 | filter_payload = json.loads(filter_request.body.decode("utf-8")) |
979 | 99 | self.assertEqual( | 87 | assert filter_payload["filters"] == [ |
980 | 100 | filter_payload["filters"], | 88 | { |
981 | 101 | [ | 89 | "series": "16", |
982 | 102 | { | 90 | "package_type": "snap", |
983 | 103 | "series": "16", | 91 | "snap_id": self.snap_map["snap-id"], |
984 | 104 | "package_type": "snap", | 92 | }, |
985 | 105 | "snap_id": self.snap_map["snap-id"], | 93 | ] |
986 | 106 | }, | 94 | assert "Basic YWRtaW46dGVzdA==" == filter_request.headers["Authorization"] |
973 | 107 | ], | ||
974 | 108 | ) | ||
975 | 109 | self.assertEqual( | ||
976 | 110 | "Basic YWRtaW46dGVzdA==", | ||
977 | 111 | filter_request.headers["Authorization"], | ||
978 | 112 | ) | ||
987 | 113 | 95 | ||
988 | 114 | chanmap_update_request = responses.calls[1].request | 96 | chanmap_update_request = responses.calls[1].request |
989 | 115 | chanmap_update_payload = json.loads( | 97 | chanmap_update_payload = json.loads( |
990 | 116 | chanmap_update_request.body.decode("utf-8"), | 98 | chanmap_update_request.body.decode("utf-8"), |
991 | 117 | ) | 99 | ) |
996 | 118 | self.assertEqual( | 100 | assert chanmap_update_payload["release_requests"][0]["package_type"] == "snap" |
993 | 119 | chanmap_update_payload["release_requests"][0]["package_type"], | ||
994 | 120 | "snap", | ||
995 | 121 | ) | ||
997 | 122 | 101 | ||
998 | 123 | @responses.activate | 102 | @responses.activate |
999 | 124 | def test_push_map_failed(self): | 103 | def test_push_map_failed(self): |
1000 | @@ -127,7 +106,7 @@ class pushTests(TestCase): | |||
1001 | 127 | responses.add("POST", filter_url, status=200, json={}) | 106 | responses.add("POST", filter_url, status=200, json={}) |
1002 | 128 | responses.add("POST", update_url, status=500) | 107 | responses.add("POST", update_url, status=500) |
1003 | 129 | 108 | ||
1005 | 130 | self.assertRaises( | 109 | pytest.raises( |
1006 | 131 | pushException, _push_channelmap, self.store, "test", self.snap_map | 110 | pushException, _push_channelmap, self.store, "test", self.snap_map |
1007 | 132 | ) | 111 | ) |
1008 | 133 | 112 | ||
1009 | @@ -139,7 +118,7 @@ class pushTests(TestCase): | |||
1010 | 139 | responses.add("POST", filter_url, status=200, json=exists) | 118 | responses.add("POST", filter_url, status=200, json=exists) |
1011 | 140 | responses.add("POST", update_url, status=200) | 119 | responses.add("POST", update_url, status=200) |
1012 | 141 | 120 | ||
1014 | 142 | self.assertRaises( | 121 | pytest.raises( |
1015 | 143 | ChannelMapExistsException, | 122 | ChannelMapExistsException, |
1016 | 144 | _push_channelmap, | 123 | _push_channelmap, |
1017 | 145 | self.store, | 124 | self.store, |
1018 | @@ -157,9 +136,9 @@ class pushTests(TestCase): | |||
1019 | 157 | 136 | ||
1020 | 158 | _push_channelmap(self.store, "test", self.snap_map, True) | 137 | _push_channelmap(self.store, "test", self.snap_map, True) |
1021 | 159 | 138 | ||
1025 | 160 | self.assertEqual( | 139 | assert ( |
1026 | 161 | "Basic YWRtaW46dGVzdA==", | 140 | "Basic YWRtaW46dGVzdA==" |
1027 | 162 | responses.calls[0].request.headers["Authorization"], | 141 | == responses.calls[0].request.headers["Authorization"] |
1028 | 163 | ) | 142 | ) |
1029 | 164 | 143 | ||
1030 | 165 | def test_split_assertions(self): | 144 | def test_split_assertions(self): |
1031 | @@ -180,13 +159,12 @@ class pushTests(TestCase): | |||
1032 | 180 | split_assertions = _split_assertions(self.snap_assert) | 159 | split_assertions = _split_assertions(self.snap_assert) |
1033 | 181 | _add_assertion_to_service(self.store, "test", split_assertions) | 160 | _add_assertion_to_service(self.store, "test", split_assertions) |
1034 | 182 | 161 | ||
1042 | 183 | self.assertIn( | 162 | assert "display-name: Tom Wardill (Ω)" in responses.calls[ |
1043 | 184 | "display-name: Tom Wardill (Ω)", | 163 | 0 |
1044 | 185 | responses.calls[0].request.body.decode("utf-8"), | 164 | ].request.body.decode("utf-8") |
1045 | 186 | ) | 165 | assert ( |
1046 | 187 | self.assertEqual( | 166 | "Basic YWRtaW46dGVzdA==" |
1047 | 188 | "Basic YWRtaW46dGVzdA==", | 167 | == responses.calls[0].request.headers["Authorization"] |
1041 | 189 | responses.calls[0].request.headers["Authorization"], | ||
1048 | 190 | ) | 168 | ) |
1049 | 191 | 169 | ||
1050 | 192 | @responses.activate | 170 | @responses.activate |
1051 | @@ -195,7 +173,7 @@ class pushTests(TestCase): | |||
1052 | 195 | responses.add("POST", assert_url, status=302) | 173 | responses.add("POST", assert_url, status=302) |
1053 | 196 | 174 | ||
1054 | 197 | split_assertions = _split_assertions(self.snap_assert) | 175 | split_assertions = _split_assertions(self.snap_assert) |
1056 | 198 | self.assertRaises( | 176 | pytest.raises( |
1057 | 199 | pushException, | 177 | pushException, |
1058 | 200 | _add_assertion_to_service, | 178 | _add_assertion_to_service, |
1059 | 201 | self.store, | 179 | self.store, |
1060 | @@ -209,7 +187,7 @@ class pushTests(TestCase): | |||
1061 | 209 | responses.add("POST", assert_url, status=500) | 187 | responses.add("POST", assert_url, status=500) |
1062 | 210 | 188 | ||
1063 | 211 | split_assertions = _split_assertions(self.snap_assert) | 189 | split_assertions = _split_assertions(self.snap_assert) |
1065 | 212 | self.assertRaises( | 190 | pytest.raises( |
1066 | 213 | pushException, | 191 | pushException, |
1067 | 214 | _add_assertion_to_service, | 192 | _add_assertion_to_service, |
1068 | 215 | self.store, | 193 | self.store, |
1069 | diff --git a/snapstore_client/tests/test_cli.py b/snapstore_client/tests/test_cli.py | |||
1070 | 216 | deleted file mode 100644 | 194 | deleted file mode 100644 |
1071 | index 3ba9dc9..0000000 | |||
1072 | --- a/snapstore_client/tests/test_cli.py | |||
1073 | +++ /dev/null | |||
1074 | @@ -1,67 +0,0 @@ | |||
1075 | 1 | # Copyright 2017 Canonical Ltd. This software is licensed under the | ||
1076 | 2 | # GNU General Public License version 3 (see the file LICENSE). | ||
1077 | 3 | |||
1078 | 4 | import logging | ||
1079 | 5 | |||
1080 | 6 | import fixtures | ||
1081 | 7 | from testtools import TestCase | ||
1082 | 8 | |||
1083 | 9 | from snapstore_client import cli | ||
1084 | 10 | |||
1085 | 11 | |||
1086 | 12 | class ConfigureLoggingTests(TestCase): | ||
1087 | 13 | def setUp(self): | ||
1088 | 14 | super().setUp() | ||
1089 | 15 | self.logger = logging.getLogger(__name__) | ||
1090 | 16 | self.addCleanup( | ||
1091 | 17 | self._restoreLogger, | ||
1092 | 18 | self.logger, | ||
1093 | 19 | self.logger.level, | ||
1094 | 20 | list(self.logger.handlers), | ||
1095 | 21 | ) | ||
1096 | 22 | self.stdout = self.useFixture(fixtures.StringStream("stdout")).stream | ||
1097 | 23 | self.stdout.fileno = lambda: 1 | ||
1098 | 24 | self.useFixture(fixtures.MonkeyPatch("sys.stdout", self.stdout)) | ||
1099 | 25 | self.stderr = self.useFixture(fixtures.StringStream("stderr")).stream | ||
1100 | 26 | self.useFixture(fixtures.MonkeyPatch("sys.stderr", self.stderr)) | ||
1101 | 27 | |||
1102 | 28 | @staticmethod | ||
1103 | 29 | def _restoreLogger(logger, level, handlers): | ||
1104 | 30 | logger.setLevel(logger.level) | ||
1105 | 31 | for handler in list(logger.handlers): | ||
1106 | 32 | logger.removeHandler(handler) | ||
1107 | 33 | for handler in handlers: | ||
1108 | 34 | logger.addHandler(handler) | ||
1109 | 35 | |||
1110 | 36 | def test_log_levels(self): | ||
1111 | 37 | self.useFixture(fixtures.MonkeyPatch("os.isatty", lambda fd: True)) | ||
1112 | 38 | cli.configure_logging(__name__) | ||
1113 | 39 | self.assertEqual(logging.INFO, self.logger.level) | ||
1114 | 40 | self.logger.debug("Debug") | ||
1115 | 41 | self.logger.info("Info") | ||
1116 | 42 | self.logger.warning("Warning: %s", "smoke") | ||
1117 | 43 | self.logger.error("Error: %s", "fire") | ||
1118 | 44 | self.stdout.seek(0) | ||
1119 | 45 | self.assertEqual("Info\nWarning: smoke\n", self.stdout.read()) | ||
1120 | 46 | self.stderr.seek(0) | ||
1121 | 47 | self.assertEqual("\033[0;31mError: fire\033[0m\n", self.stderr.read()) | ||
1122 | 48 | |||
1123 | 49 | def test_requests_log_level_default(self): | ||
1124 | 50 | cli.configure_logging(__name__) | ||
1125 | 51 | self.assertEqual(logging.WARNING, logging.getLogger("requests").level) | ||
1126 | 52 | |||
1127 | 53 | def test_requests_log_level_debug(self): | ||
1128 | 54 | cli.configure_logging(__name__, logging.DEBUG) | ||
1129 | 55 | self.assertEqual(logging.DEBUG, logging.getLogger("requests").level) | ||
1130 | 56 | |||
1131 | 57 | def test_requests_log_level_error(self): | ||
1132 | 58 | cli.configure_logging(__name__, logging.ERROR) | ||
1133 | 59 | self.assertEqual(logging.ERROR, logging.getLogger("requests").level) | ||
1134 | 60 | |||
1135 | 61 | def test_no_tty(self): | ||
1136 | 62 | self.useFixture(fixtures.MonkeyPatch("os.isatty", lambda fd: False)) | ||
1137 | 63 | self.useFixture(fixtures.EnvironmentVariable("TERM", "xterm")) | ||
1138 | 64 | cli.configure_logging(__name__) | ||
1139 | 65 | self.logger.error("Error: %s", "fire") | ||
1140 | 66 | self.stderr.seek(0) | ||
1141 | 67 | self.assertEqual("Error: fire\n", self.stderr.read()) | ||
1142 | diff --git a/snapstore_client/tests/test_config.py b/snapstore_client/tests/test_config.py | |||
1143 | index 7243554..c2cca69 100644 | |||
1144 | --- a/snapstore_client/tests/test_config.py | |||
1145 | +++ b/snapstore_client/tests/test_config.py | |||
1146 | @@ -1,81 +1,57 @@ | |||
1147 | 1 | # Copyright 2017 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2017 Canonical Ltd. This software is licensed under the |
1148 | 2 | # GNU General Public License version 3 (see the file LICENSE). | 2 | # GNU General Public License version 3 (see the file LICENSE). |
1149 | 3 | import os | ||
1150 | 3 | 4 | ||
1153 | 4 | import os.path | 5 | from snapstore_client.config import Config |
1152 | 5 | from textwrap import dedent | ||
1154 | 6 | 6 | ||
1155 | 7 | from testtools import TestCase | ||
1156 | 8 | 7 | ||
1230 | 9 | from snapstore_client.config import Config | 8 | def test_no_config(mocked_xdgconfig): |
1231 | 10 | from snapstore_client.tests.testfixtures import ( | 9 | config_ini = os.path.join(mocked_xdgconfig, Config.xdg_name, "config.ini") |
1232 | 11 | ConfigFixture, | 10 | assert not os.path.exists(config_ini) |
1233 | 12 | XDGConfigDirFixture, | 11 | Config() |
1234 | 13 | ) | 12 | |
1235 | 14 | 13 | ||
1236 | 15 | 14 | def test_init_loads(mocked_config): | |
1237 | 16 | class ConfigTests(TestCase): | 15 | cfg = Config() |
1238 | 17 | def test_no_config(self): | 16 | assert cfg.get("store:default", "gw_url") |
1239 | 18 | xdg_path = self.useFixture(XDGConfigDirFixture()).path | 17 | |
1240 | 19 | config_ini = os.path.join(xdg_path, Config.xdg_name, "config.ini") | 18 | |
1241 | 20 | self.assertFalse(os.path.exists(config_ini)) | 19 | def test_save(mocked_xdgconfig): |
1242 | 21 | Config() | 20 | config_ini = os.path.join(mocked_xdgconfig, "snap-store-proxy-client", "config.ini") |
1243 | 22 | 21 | assert not os.path.exists(config_ini) | |
1244 | 23 | def test_init_loads(self): | 22 | |
1245 | 24 | self.useFixture(ConfigFixture()) | 23 | cfg = Config() |
1246 | 25 | cfg = Config() | 24 | cfg.set("s", "k", "v") |
1247 | 26 | self.assertIsNotNone(cfg.get("store:default", "gw_url")) | 25 | cfg.save() |
1248 | 27 | 26 | ||
1249 | 28 | def test_save(self): | 27 | assert os.path.exists(config_ini) |
1250 | 29 | xdg_path = self.useFixture(XDGConfigDirFixture()).path | 28 | |
1251 | 30 | config_ini = os.path.join(xdg_path, "snap-store-proxy-client", "config.ini") | 29 | with open(config_ini) as f: |
1252 | 31 | self.assertFalse(os.path.exists(config_ini)) | 30 | content = f.read() |
1253 | 32 | 31 | assert content == ("[s]\n" "k = v\n" "\n") | |
1254 | 33 | cfg = Config() | 32 | |
1255 | 34 | cfg.set("s", "k", "v") | 33 | |
1256 | 35 | cfg.save() | 34 | def test_get_missing(mocked_xdgconfig): |
1257 | 36 | 35 | cfg = Config() | |
1258 | 37 | self.assertTrue(os.path.exists(config_ini)) | 36 | assert not cfg.get("s", "k") |
1259 | 38 | with open(config_ini) as f: | 37 | |
1260 | 39 | content = f.read() | 38 | |
1261 | 40 | self.assertEqual( | 39 | def test_get(mocked_xdgconfig): |
1262 | 41 | content, | 40 | cfg = Config() |
1263 | 42 | dedent( | 41 | cfg.set("s", "k", "v") |
1264 | 43 | """\ | 42 | assert cfg.get("s", "k") == "v" |
1265 | 44 | [s] | 43 | |
1266 | 45 | k = v | 44 | |
1267 | 46 | 45 | def test_section(mocked_xdgconfig): | |
1268 | 47 | """ | 46 | cfg = Config() |
1269 | 48 | ), | 47 | s = cfg.section("s") |
1270 | 49 | ) | 48 | s.set("k", "v") |
1271 | 50 | 49 | assert s.get("k") == "v" | |
1272 | 51 | def test_get_missing(self): | 50 | assert cfg.get("s", "k") == "v" |
1273 | 52 | self.useFixture(XDGConfigDirFixture()) | 51 | |
1274 | 53 | cfg = Config() | 52 | |
1275 | 54 | self.assertIsNone(cfg.get("s", "k")) | 53 | def test_store_section(mocked_xdgconfig): |
1276 | 55 | 54 | cfg = Config() | |
1277 | 56 | def test_get(self): | 55 | store = cfg.store_section("foo") |
1278 | 57 | self.useFixture(XDGConfigDirFixture()) | 56 | store.set("k", "v") |
1279 | 58 | cfg = Config() | 57 | assert cfg.get("store:foo", "k") == "v" |
1207 | 59 | cfg.set("s", "k", "v") | ||
1208 | 60 | self.assertEqual(cfg.get("s", "k"), "v") | ||
1209 | 61 | |||
1210 | 62 | def test_section(self): | ||
1211 | 63 | self.useFixture(XDGConfigDirFixture()) | ||
1212 | 64 | cfg = Config() | ||
1213 | 65 | s = cfg.section("s") | ||
1214 | 66 | s.set("k", "v") | ||
1215 | 67 | self.assertEqual(s.get("k"), "v") | ||
1216 | 68 | self.assertEqual(cfg.get("s", "k"), "v") | ||
1217 | 69 | |||
1218 | 70 | def test_store_section(self): | ||
1219 | 71 | self.useFixture(XDGConfigDirFixture()) | ||
1220 | 72 | cfg = Config() | ||
1221 | 73 | store = cfg.store_section("foo") | ||
1222 | 74 | store.set("k", "v") | ||
1223 | 75 | self.assertEqual(cfg.get("store:foo", "k"), "v") | ||
1224 | 76 | |||
1225 | 77 | |||
1226 | 78 | def test_suite(): | ||
1227 | 79 | from unittest import TestLoader | ||
1228 | 80 | |||
1229 | 81 | return TestLoader().loadTestsFromName(__name__) | ||
1280 | diff --git a/snapstore_client/tests/test_presentation_helpers.py b/snapstore_client/tests/test_presentation_helpers.py | |||
1281 | index 7399f91..e58e9e9 100644 | |||
1282 | --- a/snapstore_client/tests/test_presentation_helpers.py | |||
1283 | +++ b/snapstore_client/tests/test_presentation_helpers.py | |||
1284 | @@ -1,7 +1,6 @@ | |||
1285 | 1 | # Copyright 2017 Canonical Ltd. | 1 | # Copyright 2017 Canonical Ltd. |
1286 | 2 | 2 | ||
1289 | 3 | from testtools import TestCase | 3 | import pytest |
1288 | 4 | from testscenarios import WithScenarios | ||
1290 | 5 | 4 | ||
1291 | 6 | from snapstore_client.presentation_helpers import ( | 5 | from snapstore_client.presentation_helpers import ( |
1292 | 7 | channel_map_string_to_tuple, | 6 | channel_map_string_to_tuple, |
1293 | @@ -9,129 +8,93 @@ from snapstore_client.presentation_helpers import ( | |||
1294 | 9 | ) | 8 | ) |
1295 | 10 | 9 | ||
1296 | 11 | 10 | ||
1352 | 12 | class ChannelMapStringToTupleScenarioTests(WithScenarios, TestCase): | 11 | @pytest.mark.parametrize( |
1353 | 13 | 12 | "channel_map,terms", | |
1354 | 14 | scenarios = [ | 13 | [ |
1355 | 15 | ( | 14 | ("stable=1", ("stable", 1)), |
1356 | 16 | "with risk", | 15 | ("2.1/stable=2", ("2.1/stable", 2)), |
1357 | 17 | { | 16 | ("stable/hot-fix=42", ("stable/hot-fix", 42)), |
1358 | 18 | "channel_map": "stable=1", | 17 | ("2.1/stable/hot-fix=123", ("2.1/stable/hot-fix", 123)), |
1359 | 19 | "terms": ("stable", 1), | 18 | ], |
1360 | 20 | }, | 19 | ids=[ |
1361 | 21 | ), | 20 | "with risk", |
1362 | 22 | ( | 21 | "with track and risk", |
1363 | 23 | "with track and risk", | 22 | "with risk and branch", |
1364 | 24 | { | 23 | "with track, risk and branch", |
1365 | 25 | "channel_map": "2.1/stable=2", | 24 | ], |
1366 | 26 | "terms": ("2.1/stable", 2), | 25 | ) |
1367 | 27 | }, | 26 | def test_channel_map_string_to_tuple(channel_map, terms): |
1368 | 28 | ), | 27 | assert terms == channel_map_string_to_tuple(channel_map) |
1314 | 29 | ( | ||
1315 | 30 | "with risk and branch", | ||
1316 | 31 | { | ||
1317 | 32 | "channel_map": "stable/hot-fix=42", | ||
1318 | 33 | "terms": ("stable/hot-fix", 42), | ||
1319 | 34 | }, | ||
1320 | 35 | ), | ||
1321 | 36 | ( | ||
1322 | 37 | "with track, risk and branch", | ||
1323 | 38 | { | ||
1324 | 39 | "channel_map": "2.1/stable/hot-fix=123", | ||
1325 | 40 | "terms": ("2.1/stable/hot-fix", 123), | ||
1326 | 41 | }, | ||
1327 | 42 | ), | ||
1328 | 43 | ] | ||
1329 | 44 | |||
1330 | 45 | def test_run_scenario(self): | ||
1331 | 46 | self.assertEqual(self.terms, channel_map_string_to_tuple(self.channel_map)) | ||
1332 | 47 | |||
1333 | 48 | |||
1334 | 49 | class ChannelMapStringToTupleErrorTests(WithScenarios, TestCase): | ||
1335 | 50 | |||
1336 | 51 | scenarios = [ | ||
1337 | 52 | ( | ||
1338 | 53 | "missing revision", | ||
1339 | 54 | { | ||
1340 | 55 | "channel_map": "stable", | ||
1341 | 56 | "error_message": "Invalid channel map string: 'stable'", | ||
1342 | 57 | }, | ||
1343 | 58 | ), | ||
1344 | 59 | ( | ||
1345 | 60 | "non-integer revision", | ||
1346 | 61 | { | ||
1347 | 62 | "channel_map": "stable=nonsense", | ||
1348 | 63 | "error_message": "Invalid revision string: 'nonsense'", | ||
1349 | 64 | }, | ||
1350 | 65 | ), | ||
1351 | 66 | ] | ||
1369 | 67 | 28 | ||
1370 | 68 | def test_run_scenario(self): | ||
1371 | 69 | error = self.assertRaises( | ||
1372 | 70 | ValueError, channel_map_string_to_tuple, self.channel_map | ||
1373 | 71 | ) | ||
1374 | 72 | self.assertEqual(self.error_message, str(error)) | ||
1375 | 73 | 29 | ||
1376 | 30 | @pytest.mark.parametrize( | ||
1377 | 31 | "channel_map,error_message", | ||
1378 | 32 | [ | ||
1379 | 33 | ("stable", "Invalid channel map string: 'stable'"), | ||
1380 | 34 | ("stable=nonsense", "Invalid revision string: 'nonsense'"), | ||
1381 | 35 | ], | ||
1382 | 36 | ids=["missing revision", "non-integer revision"], | ||
1383 | 37 | ) | ||
1384 | 38 | def test_channel_map_string_to_tuple_exceptions(channel_map, error_message): | ||
1385 | 39 | with pytest.raises(ValueError) as error: | ||
1386 | 40 | channel_map_string_to_tuple(channel_map) | ||
1387 | 41 | assert error_message == str(error.value) | ||
1388 | 74 | 42 | ||
1389 | 75 | class OverrideToStringTests(WithScenarios, TestCase): | ||
1390 | 76 | 43 | ||
1392 | 77 | scenarios = [ | 44 | @pytest.mark.parametrize( |
1393 | 45 | "override,string", | ||
1394 | 46 | [ | ||
1395 | 78 | ( | 47 | ( |
1396 | 79 | "without revision or upstream revision", | ||
1397 | 80 | { | 48 | { |
1407 | 81 | "override": { | 49 | "snap_id": "dummy", |
1408 | 82 | "snap_id": "dummy", | 50 | "snap_name": "mysnap", |
1409 | 83 | "snap_name": "mysnap", | 51 | "revision": None, |
1410 | 84 | "revision": None, | 52 | "upstream_revision": None, |
1411 | 85 | "upstream_revision": None, | 53 | "channel": "stable", |
1412 | 86 | "channel": "stable", | 54 | "architecture": "amd64", |
1404 | 87 | "architecture": "amd64", | ||
1405 | 88 | }, | ||
1406 | 89 | "string": "mysnap stable amd64 is tracking upstream", | ||
1413 | 90 | }, | 55 | }, |
1414 | 56 | "mysnap stable amd64 is tracking upstream", | ||
1415 | 91 | ), | 57 | ), |
1416 | 92 | ( | 58 | ( |
1417 | 93 | "without revision but with upstream revision", | ||
1418 | 94 | { | 59 | { |
1428 | 95 | "override": { | 60 | "snap_id": "dummy", |
1429 | 96 | "snap_id": "dummy", | 61 | "snap_name": "mysnap", |
1430 | 97 | "snap_name": "mysnap", | 62 | "revision": None, |
1431 | 98 | "revision": None, | 63 | "upstream_revision": 2, |
1432 | 99 | "upstream_revision": 2, | 64 | "channel": "stable", |
1433 | 100 | "channel": "stable", | 65 | "architecture": "amd64", |
1425 | 101 | "architecture": "amd64", | ||
1426 | 102 | }, | ||
1427 | 103 | "string": "mysnap stable amd64 is tracking upstream (revision 2)", | ||
1434 | 104 | }, | 66 | }, |
1435 | 67 | "mysnap stable amd64 is tracking upstream (revision 2)", | ||
1436 | 105 | ), | 68 | ), |
1437 | 106 | ( | 69 | ( |
1438 | 107 | "with revision but without upstream revision", | ||
1439 | 108 | { | 70 | { |
1449 | 109 | "override": { | 71 | "snap_id": "dummy", |
1450 | 110 | "snap_id": "dummy", | 72 | "snap_name": "mysnap", |
1451 | 111 | "snap_name": "mysnap", | 73 | "revision": 1, |
1452 | 112 | "revision": 1, | 74 | "upstream_revision": None, |
1453 | 113 | "upstream_revision": None, | 75 | "channel": "stable", |
1454 | 114 | "channel": "stable", | 76 | "architecture": "amd64", |
1446 | 115 | "architecture": "amd64", | ||
1447 | 116 | }, | ||
1448 | 117 | "string": "mysnap stable amd64 1", | ||
1455 | 118 | }, | 77 | }, |
1456 | 78 | "mysnap stable amd64 1", | ||
1457 | 119 | ), | 79 | ), |
1458 | 120 | ( | 80 | ( |
1459 | 121 | "with revision and upstream revision", | ||
1460 | 122 | { | 81 | { |
1470 | 123 | "override": { | 82 | "snap_id": "dummy", |
1471 | 124 | "snap_id": "dummy", | 83 | "snap_name": "mysnap", |
1472 | 125 | "snap_name": "mysnap", | 84 | "revision": 1, |
1473 | 126 | "revision": 1, | 85 | "upstream_revision": 2, |
1474 | 127 | "upstream_revision": 2, | 86 | "channel": "stable", |
1475 | 128 | "channel": "stable", | 87 | "architecture": "amd64", |
1467 | 129 | "architecture": "amd64", | ||
1468 | 130 | }, | ||
1469 | 131 | "string": "mysnap stable amd64 1 (upstream 2)", | ||
1476 | 132 | }, | 88 | }, |
1477 | 89 | "mysnap stable amd64 1 (upstream 2)", | ||
1478 | 133 | ), | 90 | ), |
1483 | 134 | ] | 91 | ], |
1484 | 135 | 92 | ids=[ | |
1485 | 136 | def test_run_scenario(self): | 93 | "without revision or upstream revision", |
1486 | 137 | self.assertEqual(self.string, override_to_string(self.override)) | 94 | "without revision but with upstream revision", |
1487 | 95 | "with revision but without upstream revision", | ||
1488 | 96 | "with revision and upstream revision", | ||
1489 | 97 | ], | ||
1490 | 98 | ) | ||
1491 | 99 | def test_override_to_string(override, string): | ||
1492 | 100 | assert string == override_to_string(override) | ||
1493 | diff --git a/snapstore_client/tests/test_webservices.py b/snapstore_client/tests/test_webservices.py | |||
1494 | index ec2019d..3c667bb 100644 | |||
1495 | --- a/snapstore_client/tests/test_webservices.py | |||
1496 | +++ b/snapstore_client/tests/test_webservices.py | |||
1497 | @@ -1,13 +1,12 @@ | |||
1498 | 1 | # Copyright 2017 Canonical Ltd. | 1 | # Copyright 2017 Canonical Ltd. |
1499 | 2 | 2 | ||
1500 | 3 | import pytest | ||
1501 | 3 | import json | 4 | import json |
1502 | 4 | import sys | 5 | import sys |
1503 | 5 | from urllib.parse import urljoin | 6 | from urllib.parse import urljoin |
1504 | 6 | 7 | ||
1505 | 7 | import fixtures | ||
1506 | 8 | from requests.exceptions import HTTPError | 8 | from requests.exceptions import HTTPError |
1507 | 9 | import responses | 9 | import responses |
1508 | 10 | from testtools import TestCase | ||
1509 | 11 | 10 | ||
1510 | 12 | from snapstore_client import ( | 11 | from snapstore_client import ( |
1511 | 13 | config, | 12 | config, |
1512 | @@ -17,18 +16,14 @@ from snapstore_client import ( | |||
1513 | 17 | from snapstore_client.tests import ( | 16 | from snapstore_client.tests import ( |
1514 | 18 | factory, | 17 | factory, |
1515 | 19 | matchers, | 18 | matchers, |
1516 | 20 | testfixtures, | ||
1517 | 21 | ) | 19 | ) |
1518 | 22 | 20 | ||
1519 | 23 | if sys.version < "3.6": | 21 | if sys.version < "3.6": |
1520 | 24 | import sha3 # noqa | 22 | import sha3 # noqa |
1521 | 25 | 23 | ||
1522 | 26 | 24 | ||
1528 | 27 | class WebservicesTests(TestCase): | 25 | @pytest.mark.usefixtures("mocked_config") |
1529 | 28 | def setUp(self): | 26 | class TestWebservices: |
1525 | 29 | super().setUp() | ||
1526 | 30 | self.config = self.useFixture(testfixtures.ConfigFixture()) | ||
1527 | 31 | |||
1530 | 32 | @responses.activate | 27 | @responses.activate |
1531 | 33 | def test_issue_store_admin_success(self): | 28 | def test_issue_store_admin_success(self): |
1532 | 34 | gw_url = "http://store.local/" | 29 | gw_url = "http://store.local/" |
1533 | @@ -37,11 +32,10 @@ class WebservicesTests(TestCase): | |||
1534 | 37 | "POST", issue_store_admin_url, status=200, json={"macaroon": "dummy"} | 32 | "POST", issue_store_admin_url, status=200, json={"macaroon": "dummy"} |
1535 | 38 | ) | 33 | ) |
1536 | 39 | 34 | ||
1538 | 40 | self.assertEqual("dummy", webservices.issue_store_admin(gw_url)) | 35 | assert "dummy" == webservices.issue_store_admin(gw_url) |
1539 | 41 | 36 | ||
1540 | 42 | @responses.activate | 37 | @responses.activate |
1543 | 43 | def test_issue_store_admin_error(self): | 38 | def test_issue_store_admin_error(self, caplog): |
1542 | 44 | logger = self.useFixture(fixtures.FakeLogger()) | ||
1544 | 45 | gw_url = "http://store.local/" | 39 | gw_url = "http://store.local/" |
1545 | 46 | issue_store_admin_url = urljoin(gw_url, "/v2/auth/issue-store-admin") | 40 | issue_store_admin_url = urljoin(gw_url, "/v2/auth/issue-store-admin") |
1546 | 47 | responses.add( | 41 | responses.add( |
1547 | @@ -51,11 +45,11 @@ class WebservicesTests(TestCase): | |||
1548 | 51 | json=factory.APIError.single("Something went wrong").to_dict(), | 45 | json=factory.APIError.single("Something went wrong").to_dict(), |
1549 | 52 | ) | 46 | ) |
1550 | 53 | 47 | ||
1556 | 54 | self.assertRaises(HTTPError, webservices.issue_store_admin, gw_url) | 48 | pytest.raises(HTTPError, webservices.issue_store_admin, gw_url) |
1557 | 55 | self.assertEqual( | 49 | assert [ |
1558 | 56 | "Failed to issue store_admin macaroon:\nSomething went wrong\n", | 50 | "Failed to issue store_admin macaroon:", |
1559 | 57 | logger.output, | 51 | "Something went wrong", |
1560 | 58 | ) | 52 | ] == caplog.messages |
1561 | 59 | 53 | ||
1562 | 60 | @responses.activate | 54 | @responses.activate |
1563 | 61 | def test_get_sso_discharge_success(self): | 55 | def test_get_sso_discharge_success(self): |
1564 | @@ -65,22 +59,16 @@ class WebservicesTests(TestCase): | |||
1565 | 65 | "POST", discharge_url, status=200, json={"discharge_macaroon": "dummy"} | 59 | "POST", discharge_url, status=200, json={"discharge_macaroon": "dummy"} |
1566 | 66 | ) | 60 | ) |
1567 | 67 | 61 | ||
1573 | 68 | self.assertEqual( | 62 | assert "dummy" == webservices.get_sso_discharge( |
1574 | 69 | "dummy", | 63 | sso_url, "user@example.org", "secret", "caveat" |
1570 | 70 | webservices.get_sso_discharge( | ||
1571 | 71 | sso_url, "user@example.org", "secret", "caveat" | ||
1572 | 72 | ), | ||
1575 | 73 | ) | 64 | ) |
1576 | 74 | request = responses.calls[0].request | 65 | request = responses.calls[0].request |
1586 | 75 | self.assertEqual("application/json", request.headers["Content-Type"]) | 66 | assert "application/json" == request.headers["Content-Type"] |
1587 | 76 | self.assertEqual( | 67 | assert { |
1588 | 77 | { | 68 | "email": "user@example.org", |
1589 | 78 | "email": "user@example.org", | 69 | "password": "secret", |
1590 | 79 | "password": "secret", | 70 | "caveat_id": "caveat", |
1591 | 80 | "caveat_id": "caveat", | 71 | } == json.loads(request.body.decode()) |
1583 | 81 | }, | ||
1584 | 82 | json.loads(request.body.decode()), | ||
1585 | 83 | ) | ||
1592 | 84 | 72 | ||
1593 | 85 | @responses.activate | 73 | @responses.activate |
1594 | 86 | def test_get_sso_discharge_success_with_otp(self): | 74 | def test_get_sso_discharge_success_with_otp(self): |
1595 | @@ -90,27 +78,21 @@ class WebservicesTests(TestCase): | |||
1596 | 90 | "POST", discharge_url, status=200, json={"discharge_macaroon": "dummy"} | 78 | "POST", discharge_url, status=200, json={"discharge_macaroon": "dummy"} |
1597 | 91 | ) | 79 | ) |
1598 | 92 | 80 | ||
1608 | 93 | self.assertEqual( | 81 | assert "dummy" == webservices.get_sso_discharge( |
1609 | 94 | "dummy", | 82 | sso_url, |
1610 | 95 | webservices.get_sso_discharge( | 83 | "user@example.org", |
1611 | 96 | sso_url, | 84 | "secret", |
1612 | 97 | "user@example.org", | 85 | "caveat", |
1613 | 98 | "secret", | 86 | one_time_password="123456", |
1605 | 99 | "caveat", | ||
1606 | 100 | one_time_password="123456", | ||
1607 | 101 | ), | ||
1614 | 102 | ) | 87 | ) |
1615 | 103 | request = responses.calls[0].request | 88 | request = responses.calls[0].request |
1626 | 104 | self.assertEqual("application/json", request.headers["Content-Type"]) | 89 | assert "application/json" == request.headers["Content-Type"] |
1627 | 105 | self.assertEqual( | 90 | assert { |
1628 | 106 | { | 91 | "email": "user@example.org", |
1629 | 107 | "email": "user@example.org", | 92 | "password": "secret", |
1630 | 108 | "password": "secret", | 93 | "caveat_id": "caveat", |
1631 | 109 | "caveat_id": "caveat", | 94 | "otp": "123456", |
1632 | 110 | "otp": "123456", | 95 | } == json.loads(request.body.decode()) |
1623 | 111 | }, | ||
1624 | 112 | json.loads(request.body.decode()), | ||
1625 | 113 | ) | ||
1633 | 114 | 96 | ||
1634 | 115 | @responses.activate | 97 | @responses.activate |
1635 | 116 | def test_get_sso_discharge_twofactor_required(self): | 98 | def test_get_sso_discharge_twofactor_required(self): |
1636 | @@ -123,7 +105,7 @@ class WebservicesTests(TestCase): | |||
1637 | 123 | json={"error_list": [{"code": "twofactor-required"}]}, | 105 | json={"error_list": [{"code": "twofactor-required"}]}, |
1638 | 124 | ) | 106 | ) |
1639 | 125 | 107 | ||
1641 | 126 | self.assertRaises( | 108 | pytest.raises( |
1642 | 127 | exceptions.StoreTwoFactorAuthenticationRequired, | 109 | exceptions.StoreTwoFactorAuthenticationRequired, |
1643 | 128 | webservices.get_sso_discharge, | 110 | webservices.get_sso_discharge, |
1644 | 129 | sso_url, | 111 | sso_url, |
1645 | @@ -147,7 +129,7 @@ class WebservicesTests(TestCase): | |||
1646 | 147 | }, | 129 | }, |
1647 | 148 | ) | 130 | ) |
1648 | 149 | 131 | ||
1650 | 150 | e = self.assertRaises( | 132 | e = pytest.raises( |
1651 | 151 | exceptions.StoreAuthenticationError, | 133 | exceptions.StoreAuthenticationError, |
1652 | 152 | webservices.get_sso_discharge, | 134 | webservices.get_sso_discharge, |
1653 | 153 | sso_url, | 135 | sso_url, |
1654 | @@ -155,16 +137,15 @@ class WebservicesTests(TestCase): | |||
1655 | 155 | "secret", | 137 | "secret", |
1656 | 156 | "caveat", | 138 | "caveat", |
1657 | 157 | ) | 139 | ) |
1659 | 158 | self.assertEqual("Something went wrong", e.message) | 140 | assert "Authentication error: Something went wrong" == str(e.value) |
1660 | 159 | 141 | ||
1661 | 160 | @responses.activate | 142 | @responses.activate |
1664 | 161 | def test_get_sso_discharge_unstructured_error(self): | 143 | def test_get_sso_discharge_unstructured_error(self, caplog): |
1663 | 162 | logger = self.useFixture(fixtures.FakeLogger()) | ||
1665 | 163 | sso_url = "http://sso.local/" | 144 | sso_url = "http://sso.local/" |
1666 | 164 | discharge_url = urljoin(sso_url, "/api/v2/tokens/discharge") | 145 | discharge_url = urljoin(sso_url, "/api/v2/tokens/discharge") |
1667 | 165 | responses.add("POST", discharge_url, status=503, body="Try again later.") | 146 | responses.add("POST", discharge_url, status=503, body="Try again later.") |
1668 | 166 | 147 | ||
1670 | 167 | self.assertRaises( | 148 | pytest.raises( |
1671 | 168 | HTTPError, | 149 | HTTPError, |
1672 | 169 | webservices.get_sso_discharge, | 150 | webservices.get_sso_discharge, |
1673 | 170 | sso_url, | 151 | sso_url, |
1674 | @@ -172,17 +153,15 @@ class WebservicesTests(TestCase): | |||
1675 | 172 | "secret", | 153 | "secret", |
1676 | 173 | "caveat", | 154 | "caveat", |
1677 | 174 | ) | 155 | ) |
1685 | 175 | self.assertEqual( | 156 | assert [ |
1686 | 176 | "Failed to get SSO discharge:\n" | 157 | "Failed to get SSO discharge:", |
1687 | 177 | "====================\n" | 158 | "====================", |
1688 | 178 | "Try again later.\n" | 159 | "Try again later.", |
1689 | 179 | "====================\n", | 160 | "====================", |
1690 | 180 | logger.output, | 161 | ] == caplog.messages |
1684 | 181 | ) | ||
1691 | 182 | 162 | ||
1692 | 183 | @responses.activate | 163 | @responses.activate |
1695 | 184 | def test_get_overrides_success(self): | 164 | def test_get_overrides_success(self, caplog): |
1694 | 185 | logger = self.useFixture(fixtures.FakeLogger()) | ||
1696 | 186 | overrides = [factory.SnapDeviceGateway.Override()] | 165 | overrides = [factory.SnapDeviceGateway.Override()] |
1697 | 187 | # XXX cjwatson 2017-06-26: Use acceptable-generated double once it | 166 | # XXX cjwatson 2017-06-26: Use acceptable-generated double once it |
1698 | 188 | # exists. | 167 | # exists. |
1699 | @@ -190,17 +169,18 @@ class WebservicesTests(TestCase): | |||
1700 | 190 | overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides/mysnap") | 169 | overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides/mysnap") |
1701 | 191 | responses.add("GET", overrides_url, status=200, json=overrides) | 170 | responses.add("GET", overrides_url, status=200, json=overrides) |
1702 | 192 | 171 | ||
1704 | 193 | self.assertEqual(overrides, webservices.get_overrides(store, "mysnap")) | 172 | assert overrides == webservices.get_overrides(store, "mysnap") |
1705 | 194 | request = responses.calls[0].request | 173 | request = responses.calls[0].request |
1709 | 195 | self.assertThat( | 174 | assert ( |
1710 | 196 | request.headers["Authorization"], | 175 | matchers.MacaroonHeaderVerifies("random-key").match( |
1711 | 197 | matchers.MacaroonHeaderVerifies(self.config.key), | 176 | request.headers["Authorization"] |
1712 | 177 | ) | ||
1713 | 178 | is None | ||
1714 | 198 | ) | 179 | ) |
1716 | 199 | self.assertNotIn("Failed to get overrides:", logger.output) | 180 | assert "Failed to get overrides:" not in caplog.messages |
1717 | 200 | 181 | ||
1718 | 201 | @responses.activate | 182 | @responses.activate |
1721 | 202 | def test_get_overrides_error(self): | 183 | def test_get_overrides_error(self, caplog): |
1720 | 203 | logger = self.useFixture(fixtures.FakeLogger()) | ||
1722 | 204 | store = config.Config().store_section("default") | 184 | store = config.Config().store_section("default") |
1723 | 205 | overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides/mysnap") | 185 | overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides/mysnap") |
1724 | 206 | responses.add( | 186 | responses.add( |
1725 | @@ -210,14 +190,11 @@ class WebservicesTests(TestCase): | |||
1726 | 210 | json=factory.APIError.single("Something went wrong").to_dict(), | 190 | json=factory.APIError.single("Something went wrong").to_dict(), |
1727 | 211 | ) | 191 | ) |
1728 | 212 | 192 | ||
1733 | 213 | self.assertRaises(HTTPError, webservices.get_overrides, store, "mysnap") | 193 | pytest.raises(HTTPError, webservices.get_overrides, store, "mysnap") |
1734 | 214 | self.assertEqual( | 194 | assert ["Failed to get overrides:", "Something went wrong"] == caplog.messages |
1731 | 215 | "Failed to get overrides:\nSomething went wrong\n", logger.output | ||
1732 | 216 | ) | ||
1735 | 217 | 195 | ||
1736 | 218 | @responses.activate | 196 | @responses.activate |
1739 | 219 | def test_set_overrides_success(self): | 197 | def test_set_overrides_success(self, caplog): |
1738 | 220 | logger = self.useFixture(fixtures.FakeLogger()) | ||
1740 | 221 | override = factory.SnapDeviceGateway.Override() | 198 | override = factory.SnapDeviceGateway.Override() |
1741 | 222 | # XXX cjwatson 2017-06-26: Use acceptable-generated double once it | 199 | # XXX cjwatson 2017-06-26: Use acceptable-generated double once it |
1742 | 223 | # exists. | 200 | # exists. |
1743 | @@ -225,26 +202,8 @@ class WebservicesTests(TestCase): | |||
1744 | 225 | overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides") | 202 | overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides") |
1745 | 226 | responses.add("POST", overrides_url, status=200, json=[override]) | 203 | responses.add("POST", overrides_url, status=200, json=[override]) |
1746 | 227 | 204 | ||
1767 | 228 | self.assertEqual( | 205 | assert [override] == webservices.set_overrides( |
1768 | 229 | [override], | 206 | store, |
1749 | 230 | webservices.set_overrides( | ||
1750 | 231 | store, | ||
1751 | 232 | [ | ||
1752 | 233 | { | ||
1753 | 234 | "snap_name": override["snap_name"], | ||
1754 | 235 | "revision": override["revision"], | ||
1755 | 236 | "channel": override["channel"], | ||
1756 | 237 | "series": override["series"], | ||
1757 | 238 | } | ||
1758 | 239 | ], | ||
1759 | 240 | ), | ||
1760 | 241 | ) | ||
1761 | 242 | request = responses.calls[0].request | ||
1762 | 243 | self.assertThat( | ||
1763 | 244 | request.headers["Authorization"], | ||
1764 | 245 | matchers.MacaroonHeaderVerifies(self.config.key), | ||
1765 | 246 | ) | ||
1766 | 247 | self.assertEqual( | ||
1769 | 248 | [ | 207 | [ |
1770 | 249 | { | 208 | { |
1771 | 250 | "snap_name": override["snap_name"], | 209 | "snap_name": override["snap_name"], |
1772 | @@ -253,13 +212,27 @@ class WebservicesTests(TestCase): | |||
1773 | 253 | "series": override["series"], | 212 | "series": override["series"], |
1774 | 254 | } | 213 | } |
1775 | 255 | ], | 214 | ], |
1776 | 256 | json.loads(request.body.decode()), | ||
1777 | 257 | ) | 215 | ) |
1779 | 258 | self.assertNotIn("Failed to set override:", logger.output) | 216 | request = responses.calls[0].request |
1780 | 217 | assert ( | ||
1781 | 218 | matchers.MacaroonHeaderVerifies("random-key").match( | ||
1782 | 219 | request.headers["Authorization"] | ||
1783 | 220 | ) | ||
1784 | 221 | is None | ||
1785 | 222 | ) | ||
1786 | 223 | |||
1787 | 224 | assert [ | ||
1788 | 225 | { | ||
1789 | 226 | "snap_name": override["snap_name"], | ||
1790 | 227 | "revision": override["revision"], | ||
1791 | 228 | "channel": override["channel"], | ||
1792 | 229 | "series": override["series"], | ||
1793 | 230 | } | ||
1794 | 231 | ] == json.loads(request.body.decode()) | ||
1795 | 232 | assert "Failed to set override:" not in caplog.messages | ||
1796 | 259 | 233 | ||
1797 | 260 | @responses.activate | 234 | @responses.activate |
1800 | 261 | def test_set_overrides_error(self): | 235 | def test_set_overrides_error(self, caplog): |
1799 | 262 | logger = self.useFixture(fixtures.FakeLogger()) | ||
1801 | 263 | override = factory.SnapDeviceGateway.Override() | 236 | override = factory.SnapDeviceGateway.Override() |
1802 | 264 | # XXX cjwatson 2017-06-26: Use acceptable-generated double once it | 237 | # XXX cjwatson 2017-06-26: Use acceptable-generated double once it |
1803 | 265 | # exists. | 238 | # exists. |
1804 | @@ -272,7 +245,7 @@ class WebservicesTests(TestCase): | |||
1805 | 272 | json=factory.APIError.single("Something went wrong").to_dict(), | 245 | json=factory.APIError.single("Something went wrong").to_dict(), |
1806 | 273 | ) | 246 | ) |
1807 | 274 | 247 | ||
1809 | 275 | self.assertRaises( | 248 | pytest.raises( |
1810 | 276 | HTTPError, | 249 | HTTPError, |
1811 | 277 | lambda: webservices.set_overrides( | 250 | lambda: webservices.set_overrides( |
1812 | 278 | store, | 251 | store, |
1813 | @@ -284,6 +257,4 @@ class WebservicesTests(TestCase): | |||
1814 | 284 | }, | 257 | }, |
1815 | 285 | ), | 258 | ), |
1816 | 286 | ) | 259 | ) |
1820 | 287 | self.assertEqual( | 260 | assert ["Failed to set override:", "Something went wrong"] == caplog.messages |
1818 | 288 | "Failed to set override:\nSomething went wrong\n", logger.output | ||
1819 | 289 | ) |
Looks great to me, thanks. But, I think it'd be wise to announce this migration publicly to the team, given the relative backlash in December's monthly meeting. That said, I'm +1! But please first check comments below.