Merge ~woutervb/snapstore-client:move_to_pytest into snapstore-client:master

Proposed by Wouter van Bommel
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)
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.

To post a comment you must log in.
Revision history for this message
Maximiliano Bertacchini (maxiberta) wrote : Posted in a previous version of this proposal

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.

Revision history for this message
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

Revision history for this message
Jonathan Hartley (tartley) : Posted in a previous version of this proposal
review: Approve
Revision history for this message
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:

=============================== warnings summary ===============================
snapstore_client/logic/tests/test_login.py: 16 warnings
  /home/jhartley/src/snapstore-client/env/lib/python3.8/site-packages/responses/__init__.py:334: DeprecationWarning: Argument 'match_querystring' is deprecated. Use 'responses.matchers.query_param_matcher' or 'responses.matchers.query_string_matcher'
    warn(

-- Docs: https://docs.pytest.org/en/stable/warnings.html

review: Approve
Revision history for this message
Jonathan Hartley (tartley) wrote :

Another thought...

Revision history for this message
Maximiliano Bertacchini (maxiberta) wrote :

+1 awesome. Check a couple of non-blocking comments below. Thanks!

review: Approve
Revision history for this message
Wouter van Bommel (woutervb) wrote :

Thanks for the responses, will update (most) things you both found.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/Makefile b/Makefile
index 76ddd0a..be1be80 100644
--- a/Makefile
+++ b/Makefile
@@ -28,7 +28,7 @@ snap:
28 snapcraft28 snapcraft
2929
30test: $(ENV)/dev30test: $(ENV)/dev
31 $(PYTHON3) -m unittest $(TESTS) 2>&131 $(PYTHON3) -m pytest
32 $(MAKE) --silent lint32 $(MAKE) --silent lint
3333
34/snap/bin/documentation-builder:34/snap/bin/documentation-builder:
@@ -39,7 +39,7 @@ docs: /snap/bin/documentation-builder
3939
4040
41lint: $(ENV)/dev41lint: $(ENV)/dev
42 $(FLAKE8) $(SERVICE_PACKAGE)42 $(FLAKE8) $(SERVICE_PACKAGE) conftest.py
43 $(BLACK) --check .43 $(BLACK) --check .
4444
45black: $(ENV)/dev45black: $(ENV)/dev
@@ -47,8 +47,9 @@ black: $(ENV)/dev
4747
48coverage: $(ENV)/dev48coverage: $(ENV)/dev
49 $(PYTHON3) -m coverage erase49 $(PYTHON3) -m coverage erase
50 $(PYTHON3) -m coverage run --include "$(SERVICE_PACKAGE)*" -m unittest $(TESTS)50 $(PYTHON3) -m coverage run --include "$(SERVICE_PACKAGE)*" -m pytest
51 $(PYTHON3) -m coverage html51 $(PYTHON3) -m coverage html
52 $(PYTHON3) -m coverage report
5253
53clean:54clean:
54 rm -rf $(TMPDIR)55 rm -rf $(TMPDIR)
diff --git a/conftest.py b/conftest.py
55new file mode 10064456new file mode 100644
index 0000000..06c7ca0
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,70 @@
1import os.path
2import tempfile
3from unittest import mock
4from unittest.mock import patch
5
6from pymacaroons import Macaroon
7import pytest
8
9from snapstore_client.config import Config
10
11
12@pytest.fixture(scope="function")
13def ftmpdir(tmpdir):
14 """Takes the tmpdir, but add a random path per function invocation"""
15 with tempfile.TemporaryDirectory(dir=tmpdir) as new_tmpdir:
16 yield new_tmpdir
17
18
19@pytest.fixture
20def mocked_xdgconfig(ftmpdir):
21 with patch("xdg.BaseDirectory.xdg_config_home", ftmpdir), patch(
22 "xdg.BaseDirectory.xdg_config_dirs", [ftmpdir]
23 ):
24 yield ftmpdir
25
26
27@pytest.fixture
28def mocked_config(mocked_xdgconfig):
29 app_config_path = os.path.join(mocked_xdgconfig, "snap-store-proxy-client")
30 os.makedirs(app_config_path)
31 root = Macaroon(key="random-key")
32 root.add_third_party_caveat("login.example.com", "sso-key", "payload")
33 unbound_discharge = Macaroon(
34 location="login.example.com", identifier="payload", key="sso-key"
35 )
36 with open(os.path.join(app_config_path, "config.ini"), "w") as f:
37 f.write(
38 (
39 "[store:default]\n"
40 "gw_url = {gw_url}\n"
41 "sso_url = {sso_url}\n"
42 "root = {root}\n"
43 "unbound_discharge = {unbound_discharge}\n"
44 ).format(
45 gw_url="http://store.local/",
46 sso_url="http://sso.local/",
47 root=root.serialize(),
48 unbound_discharge=unbound_discharge.serialize(),
49 )
50 )
51
52 yield Config()
53
54
55@pytest.fixture
56def mocked_empty_config(mocked_xdgconfig):
57 app_config_path = os.path.join(mocked_xdgconfig, "snap-store-proxy-client")
58 os.makedirs(app_config_path)
59
60
61@pytest.fixture
62def mock_input():
63 with mock.patch("builtins.input") as input:
64 yield input
65
66
67@pytest.fixture
68def mock_getpass():
69 with mock.patch("getpass.getpass") as getpass:
70 yield getpass
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 72daeb2..d9285a2 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,8 +1,7 @@
1acceptable>=0.91acceptable>=0.9
2coverage2coverage
3fixtures
4flake83flake8
5responses4responses
6testscenarios
7testtools
8black5black
6pytest
7testtools
9\ No newline at end of file8\ No newline at end of file
diff --git a/snapstore_client/logic/tests/test_login.py b/snapstore_client/logic/tests/test_login.py
index d61ac1b..7dbfea1 100644
--- a/snapstore_client/logic/tests/test_login.py
+++ b/snapstore_client/logic/tests/test_login.py
@@ -1,13 +1,13 @@
1# Copyright 2017 Canonical Ltd.1# Copyright 2017 Canonical Ltd.
22
3import pytest
3import json4import json
4from unittest import mock5from unittest import mock
5from urllib.parse import urljoin, urlparse6from urllib.parse import urljoin, urlparse
67
7import fixtures8
8from pymacaroons import Macaroon9from pymacaroons import Macaroon
9import responses10import responses
10from testtools import TestCase
11from testtools.matchers import (11from testtools.matchers import (
12 ContainsDict,12 ContainsDict,
13 Equals,13 Equals,
@@ -22,23 +22,11 @@ from snapstore_client.logic.login import login
22from snapstore_client.tests import factory22from snapstore_client.tests import factory
2323
2424
25class LoginTests(TestCase):25@pytest.mark.usefixtures("mocked_config")
26 def setUp(self):26class TestLogin:
27 super().setUp()27
28 self.default_gw_url = "http://store.local/"28 default_gw_url = "http://store.local/"
29 self.default_sso_url = "https://login.staging.ubuntu.com/"29 default_sso_url = "https://login.staging.ubuntu.com/"
30 self.logger = self.useFixture(fixtures.FakeLogger())
31 self.config_path = self.useFixture(fixtures.TempDir()).path
32 self.useFixture(
33 fixtures.MonkeyPatch("xdg.BaseDirectory.xdg_config_home", self.config_path)
34 )
35 self.useFixture(
36 fixtures.MonkeyPatch(
37 "xdg.BaseDirectory.xdg_config_dirs", [self.config_path]
38 )
39 )
40 self.mock_input = self.useFixture(fixtures.MockPatch("builtins.input")).mock
41 self.mock_getpass = self.useFixture(fixtures.MockPatch("getpass.getpass")).mock
4230
43 def make_responses_callback(self, response_templates):31 def make_responses_callback(self, response_templates):
44 full_responses = []32 full_responses = []
@@ -86,21 +74,21 @@ class LoginTests(TestCase):
86 return macaroon.serialize()74 return macaroon.serialize()
8775
88 @responses.activate76 @responses.activate
89 def test_login_sso_mismatch(self):77 def test_login_sso_mismatch(self, mock_input, mock_getpass):
90 self.mock_input.return_value = "user@example.org"78 mock_input.return_value = "user@example.org"
91 self.mock_getpass.return_value = "secret"79 mock_getpass.returnvalue = "secret"
92 macaroon = Macaroon()80 macaroon = Macaroon()
93 macaroon.add_third_party_caveat("another.example.com", "", "")81 macaroon.add_third_party_caveat("another.example.com", "", "")
94 self.add_issue_store_admin_response(82 self.add_issue_store_admin_response(
95 {"status": 200, "json": {"macaroon": macaroon.serialize()}}83 {"status": 200, "json": {"macaroon": macaroon.serialize()}}
96 )84 )
97 self.add_get_sso_discharge_response({"status": 401})85 self.add_get_sso_discharge_response({"status": 401})
98 self.assertRaises(exceptions.StoreMacaroonSSOMismatch, login, self.make_args())86 pytest.raises(exceptions.StoreMacaroonSSOMismatch, login, self.make_args())
9987
100 @responses.activate88 @responses.activate
101 def test_login_sso_bad_email(self):89 def test_login_sso_bad_email(self, mock_input, mock_getpass, caplog):
102 self.mock_input.return_value = ""90 mock_input.return_value = ""
103 self.mock_getpass.return_value = ""91 mock_getpass.return_value = ""
104 self.add_issue_store_admin_response(92 self.add_issue_store_admin_response(
105 {"status": 200, "json": {"macaroon": self.make_root_macaroon()}}93 {"status": 200, "json": {"macaroon": self.make_root_macaroon()}}
106 )94 )
@@ -111,19 +99,17 @@ class LoginTests(TestCase):
111 self.add_get_sso_discharge_response(99 self.add_get_sso_discharge_response(
112 {"status": 401, "json": {"error_list": [auth_error]}}100 {"status": 401, "json": {"error_list": [auth_error]}}
113 )101 )
114 self.assertEqual(1, login(self.make_args()))102 assert 1 == login(self.make_args())
115 self.assertEqual(103 assert [
116 "Enter your Ubuntu One SSO credentials.\n"104 "Login failed.",
117 "Login failed.\n"105 "Authentication error: Invalid request data",
118 "Authentication error: Invalid request data\n"106 "email: Enter a valid email address.",
119 "email: Enter a valid email address.\n",107 ] == caplog.messages
120 self.logger.output,
121 )
122108
123 @responses.activate109 @responses.activate
124 def test_login_sso_unauthorized(self):110 def test_login_sso_unauthorized(self, mock_input, mock_getpass, caplog):
125 self.mock_input.return_value = "user@example.org"111 mock_input.return_value = "user@example.org"
126 self.mock_getpass.return_value = "secret"112 mock_getpass.return_value = "secret"
127 self.add_issue_store_admin_response(113 self.add_issue_store_admin_response(
128 {"status": 200, "json": {"macaroon": self.make_root_macaroon()}}114 {"status": 200, "json": {"macaroon": self.make_root_macaroon()}}
129 )115 )
@@ -131,51 +117,41 @@ class LoginTests(TestCase):
131 self.add_get_sso_discharge_response(117 self.add_get_sso_discharge_response(
132 {"status": 401, "json": {"error_list": [auth_error]}}118 {"status": 401, "json": {"error_list": [auth_error]}}
133 )119 )
134 self.assertEqual(1, login(self.make_args()))120 assert 1 == login(self.make_args())
135 self.assertEqual(121 assert [
136 "Enter your Ubuntu One SSO credentials.\n"122 "Login failed.",
137 "Login failed.\n"123 "Authentication error: Provided email/password is not correct.",
138 "Authentication error: Provided email/password is not correct.\n",124 ] == caplog.messages
139 self.logger.output,
140 )
141125
142 @responses.activate126 @responses.activate
143 def test_login_twofactor_required(self):127 def test_login_twofactor_required(self, mock_input, mock_getpass):
144 self.mock_input.side_effect = ("user@example.org", "123456")128 mock_input.side_effect = ("user@example.org", "123456")
145 self.mock_getpass.return_value = "secret"129 mock_getpass.return_value = "secret"
146 root = self.make_root_macaroon()130 root = self.make_root_macaroon()
147 self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}})131 self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}})
148 self.add_get_sso_discharge_response(132 self.add_get_sso_discharge_response(
149 {"status": 401, "json": {"error_list": [{"code": "twofactor-required"}]}},133 {"status": 401, "json": {"error_list": [{"code": "twofactor-required"}]}},
150 {"status": 200, "json": {"discharge_macaroon": "dummy"}},134 {"status": 200, "json": {"discharge_macaroon": "dummy"}},
151 )135 )
152 self.assertEqual(0, login(self.make_args()))136 assert 0 == login(self.make_args())
153137
154 self.assertIn("Enter your Ubuntu One SSO credentials.", self.logger.output)138 mock_input.assert_has_calls(
155 self.mock_input.assert_has_calls(
156 [mock.call("Email: "), mock.call("Second-factor auth: ")]139 [mock.call("Email: "), mock.call("Second-factor auth: ")]
157 )140 )
158 self.mock_getpass.assert_called_once_with("Password: ")141 mock_getpass.assert_called_once_with("Password: ")
159 self.assertEqual(3, len(responses.calls))142 assert 3 == len(responses.calls)
160 self.assertEqual(143 assert {
161 {144 "email": "user@example.org",
162 "email": "user@example.org",145 "password": "secret",
163 "password": "secret",146 "caveat_id": "payload",
164 "caveat_id": "payload",147 } == json.loads(responses.calls[1].request.body.decode())
165 },148 assert {
166 json.loads(responses.calls[1].request.body.decode()),149 "email": "user@example.org",
167 )150 "password": "secret",
168 self.assertEqual(151 "caveat_id": "payload",
169 {152 "otp": "123456",
170 "email": "user@example.org",153 } == json.loads(responses.calls[2].request.body.decode())
171 "password": "secret",154 assert (
172 "caveat_id": "payload",
173 "otp": "123456",
174 },
175 json.loads(responses.calls[2].request.body.decode()),
176 )
177 self.assertThat(
178 config.Config().parser,
179 ContainsDict(155 ContainsDict(
180 {156 {
181 "store:default": MatchesDict(157 "store:default": MatchesDict(
@@ -188,13 +164,14 @@ class LoginTests(TestCase):
188 }164 }
189 ),165 ),
190 }166 }
191 ),167 ).match(config.Config().parser)
168 is None
192 )169 )
193170
194 @responses.activate171 @responses.activate
195 def test_login_twofactor_not_required(self):172 def test_login_twofactor_not_required(self, mock_input, mock_getpass):
196 self.mock_input.return_value = "user@example.org"173 mock_input.return_value = "user@example.org"
197 self.mock_getpass.return_value = "secret"174 mock_getpass.return_value = "secret"
198 root = self.make_root_macaroon()175 root = self.make_root_macaroon()
199 self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}})176 self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}})
200 self.add_get_sso_discharge_response(177 self.add_get_sso_discharge_response(
@@ -202,20 +179,15 @@ class LoginTests(TestCase):
202 )179 )
203 login(self.make_args())180 login(self.make_args())
204181
205 self.assertIn("Enter your Ubuntu One SSO credentials.", self.logger.output)182 mock_input.assert_called_once_with("Email: ")
206 self.mock_input.assert_called_once_with("Email: ")183 mock_getpass.assert_called_once_with("Password: ")
207 self.mock_getpass.assert_called_once_with("Password: ")184 assert 2 == len(responses.calls)
208 self.assertEqual(2, len(responses.calls))185 assert {
209 self.assertEqual(186 "email": "user@example.org",
210 {187 "password": "secret",
211 "email": "user@example.org",188 "caveat_id": "payload",
212 "password": "secret",189 } == json.loads(responses.calls[1].request.body.decode())
213 "caveat_id": "payload",190 assert (
214 },
215 json.loads(responses.calls[1].request.body.decode()),
216 )
217 self.assertThat(
218 config.Config().parser,
219 ContainsDict(191 ContainsDict(
220 {192 {
221 "store:default": MatchesDict(193 "store:default": MatchesDict(
@@ -228,13 +200,14 @@ class LoginTests(TestCase):
228 }200 }
229 ),201 ),
230 }202 }
231 ),203 ).match(config.Config().parser)
204 is None
232 )205 )
233206
234 @responses.activate207 @responses.activate
235 def test_login_with_email(self):208 def test_login_with_email(self, mock_input, mock_getpass):
236 self.mock_input.side_effect = Exception("shouldn't be called")209 mock_input.side_effect = Exception("shouldn't be called")
237 self.mock_getpass.return_value = "secret"210 mock_getpass.return_value = "secret"
238 root = self.make_root_macaroon()211 root = self.make_root_macaroon()
239 self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}})212 self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}})
240 self.add_get_sso_discharge_response(213 self.add_get_sso_discharge_response(
@@ -242,20 +215,15 @@ class LoginTests(TestCase):
242 )215 )
243 login(self.make_args(email="user@example.org"))216 login(self.make_args(email="user@example.org"))
244217
245 self.assertIn("Enter your Ubuntu One SSO credentials.", self.logger.output)218 mock_input.assert_not_called()
246 self.mock_input.assert_not_called()219 mock_getpass.assert_called_once_with("Password: ")
247 self.mock_getpass.assert_called_once_with("Password: ")220 assert 2 == len(responses.calls)
248 self.assertEqual(2, len(responses.calls))221 assert {
249 self.assertEqual(222 "email": "user@example.org",
250 {223 "password": "secret",
251 "email": "user@example.org",224 "caveat_id": "payload",
252 "password": "secret",225 } == json.loads(responses.calls[1].request.body.decode())
253 "caveat_id": "payload",226 assert (
254 },
255 json.loads(responses.calls[1].request.body.decode()),
256 )
257 self.assertThat(
258 config.Config().parser,
259 ContainsDict(227 ContainsDict(
260 {228 {
261 "store:default": MatchesDict(229 "store:default": MatchesDict(
@@ -268,15 +236,16 @@ class LoginTests(TestCase):
268 }236 }
269 ),237 ),
270 }238 }
271 ),239 ).match(config.Config().parser)
240 is None
272 )241 )
273242
274 @responses.activate243 @responses.activate
275 def test_store_url(self):244 def test_store_url(self, mock_input, mock_getpass):
276 gw_url = "http://otherstore.local:1234/"245 gw_url = "http://otherstore.local:1234/"
277246
278 self.mock_input.return_value = "user@example.org"247 mock_input.return_value = "user@example.org"
279 self.mock_getpass.return_value = "secret"248 mock_getpass.return_value = "secret"
280 root = self.make_root_macaroon()249 root = self.make_root_macaroon()
281 self.add_issue_store_admin_response(250 self.add_issue_store_admin_response(
282 {"status": 200, "json": {"macaroon": root}}, gw_url=gw_url251 {"status": 200, "json": {"macaroon": root}}, gw_url=gw_url
@@ -286,11 +255,10 @@ class LoginTests(TestCase):
286 )255 )
287 login(self.make_args(store_url=gw_url))256 login(self.make_args(store_url=gw_url))
288257
289 self.assertEqual(2, len(responses.calls))258 assert 2 == len(responses.calls)
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
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)
292 self.assertThat(261 assert (
293 config.Config().parser,
294 ContainsDict(262 ContainsDict(
295 {263 {
296 "store:default": ContainsDict(264 "store:default": ContainsDict(
@@ -300,15 +268,16 @@ class LoginTests(TestCase):
300 }268 }
301 ),269 ),
302 }270 }
303 ),271 ).match(config.Config().parser)
272 is None
304 )273 )
305274
306 @responses.activate275 @responses.activate
307 def test_sso_url(self):276 def test_sso_url(self, mock_input, mock_getpass):
308 sso_url = "https://othersso.local:1234/"277 sso_url = "https://othersso.local:1234/"
309278
310 self.mock_input.return_value = "user@example.org"279 mock_input.return_value = "user@example.org"
311 self.mock_getpass.return_value = "secret"280 mock_getpass.return_value = "secret"
312 root = self.make_root_macaroon(sso_url=sso_url)281 root = self.make_root_macaroon(sso_url=sso_url)
313 self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}})282 self.add_issue_store_admin_response({"status": 200, "json": {"macaroon": root}})
314 self.add_get_sso_discharge_response(283 self.add_get_sso_discharge_response(
@@ -316,11 +285,10 @@ class LoginTests(TestCase):
316 )285 )
317 login(self.make_args(sso_url=sso_url))286 login(self.make_args(sso_url=sso_url))
318287
319 self.assertEqual(2, len(responses.calls))288 assert 2 == len(responses.calls)
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)
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
322 self.assertThat(291 assert (
323 config.Config().parser,
324 ContainsDict(292 ContainsDict(
325 {293 {
326 "store:default": ContainsDict(294 "store:default": ContainsDict(
@@ -330,5 +298,6 @@ class LoginTests(TestCase):
330 }298 }
331 ),299 ),
332 }300 }
333 ),301 ).match(config.Config().parser)
302 is None
334 )303 )
diff --git a/snapstore_client/logic/tests/test_overrides.py b/snapstore_client/logic/tests/test_overrides.py
index 591b55d..c628810 100644
--- a/snapstore_client/logic/tests/test_overrides.py
+++ b/snapstore_client/logic/tests/test_overrides.py
@@ -1,11 +1,11 @@
1# Copyright 2017 Canonical Ltd.1# Copyright 2017 Canonical Ltd.
22
3import json3import json
4import logging
4from urllib.parse import urljoin5from urllib.parse import urljoin
56
6import fixtures
7import responses7import responses
8from testtools import TestCase8import pytest
99
10from snapstore_client import config10from snapstore_client import config
11from snapstore_client.logic.overrides import (11from snapstore_client.logic.overrides import (
@@ -15,26 +15,23 @@ from snapstore_client.logic.overrides import (
15)15)
16from snapstore_client.tests import (16from snapstore_client.tests import (
17 factory,17 factory,
18 testfixtures,
19)18)
2019
2120
22class OverridesTests(TestCase):21class TestOverrides:
23 def test_list_overrides_no_store_config(self):22 @pytest.mark.usefixtures("mocked_empty_config")
24 self.useFixture(testfixtures.ConfigFixture(empty=True))23 def test_list_overrides_no_store_config(self, caplog):
25 logger = self.useFixture(fixtures.FakeLogger())
26 rc = list_overrides(factory.Args(snap_name="some-snap", series="16"))24 rc = list_overrides(factory.Args(snap_name="some-snap", series="16"))
27 self.assertEqual(rc, 1)25 assert rc == 1
28 self.assertEqual(26 assert [
29 logger.output,
30 "No store configuration found. "27 "No store configuration found. "
31 'Have you run "snap-store-proxy-client login"?\n',28 'Have you run "snap-store-proxy-client login"?'
32 )29 ] == caplog.messages
3330
31 @pytest.mark.usefixtures("mocked_config")
34 @responses.activate32 @responses.activate
35 def test_list_overrides_online(self):33 def test_list_overrides_online(self, caplog):
36 self.useFixture(testfixtures.ConfigFixture())34 caplog.set_level(logging.DEBUG)
37 logger = self.useFixture(fixtures.FakeLogger())
38 snap_id = factory.generate_snap_id()35 snap_id = factory.generate_snap_id()
39 overrides = [36 overrides = [
40 factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"),37 factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"),
@@ -56,18 +53,17 @@ class OverridesTests(TestCase):
56 responses.add("GET", overrides_url, status=200, json={"overrides": overrides})53 responses.add("GET", overrides_url, status=200, json={"overrides": overrides})
5754
58 list_overrides(factory.Args(snap_name="mysnap", series="16", password=False))55 list_overrides(factory.Args(snap_name="mysnap", series="16", password=False))
59 self.assertEqual(56 assert [
60 "mysnap stable amd64 1 (upstream 2)\n"57 "mysnap stable amd64 1 (upstream 2)",
61 "mysnap foo/stable i386 3 (upstream 4)\n",58 "mysnap foo/stable i386 3 (upstream 4)",
62 logger.output,59 ] == caplog.messages
63 )
64 # We shouldn't have Basic Authorization headers, but Macaroon60 # We shouldn't have Basic Authorization headers, but Macaroon
65 self.assertNotIn("Basic", responses.calls[0].request.headers["Authorization"])61 assert "Basic" not in responses.calls[0].request.headers["Authorization"]
6662
63 @pytest.mark.usefixtures("mocked_config")
67 @responses.activate64 @responses.activate
68 def test_list_overrides_offline(self):65 def test_list_overrides_offline(self, caplog):
69 self.useFixture(testfixtures.ConfigFixture())66 caplog.set_level(logging.DEBUG)
70 logger = self.useFixture(fixtures.FakeLogger())
71 snap_id = factory.generate_snap_id()67 snap_id = factory.generate_snap_id()
72 overrides = [68 overrides = [
73 factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"),69 factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"),
@@ -89,19 +85,17 @@ class OverridesTests(TestCase):
89 responses.add("GET", overrides_url, status=200, json={"overrides": overrides})85 responses.add("GET", overrides_url, status=200, json={"overrides": overrides})
9086
91 list_overrides(factory.Args(snap_name="mysnap", series="16", password="test"))87 list_overrides(factory.Args(snap_name="mysnap", series="16", password="test"))
92 self.assertEqual(88 assert [
93 "mysnap stable amd64 1 (upstream 2)\n"89 "mysnap stable amd64 1 (upstream 2)",
94 "mysnap foo/stable i386 3 (upstream 4)\n",90 "mysnap foo/stable i386 3 (upstream 4)",
95 logger.output,91 ] == caplog.messages
96 )92 assert (
97 self.assertEqual(93 "Basic YWRtaW46dGVzdA=="
98 "Basic YWRtaW46dGVzdA==",94 == responses.calls[0].request.headers["Authorization"]
99 responses.calls[0].request.headers["Authorization"],
100 )95 )
10196
102 def test_override_no_store_config(self):97 @pytest.mark.usefixtures("mocked_empty_config")
103 self.useFixture(testfixtures.ConfigFixture(empty=True))98 def test_override_no_store_config(self, caplog):
104 logger = self.useFixture(fixtures.FakeLogger())
105 rc = override(99 rc = override(
106 factory.Args(100 factory.Args(
107 snap_name="some-snap",101 snap_name="some-snap",
@@ -110,17 +104,16 @@ class OverridesTests(TestCase):
110 password=False,104 password=False,
111 )105 )
112 )106 )
113 self.assertEqual(rc, 1)107 assert rc == 1
114 self.assertEqual(108 assert [
115 logger.output,
116 "No store configuration found. "109 "No store configuration found. "
117 'Have you run "snap-store-proxy-client login"?\n',110 'Have you run "snap-store-proxy-client login"?'
118 )111 ] == caplog.messages
119112
113 @pytest.mark.usefixtures("mocked_config")
120 @responses.activate114 @responses.activate
121 def test_override_online(self):115 def test_override_online(self, caplog):
122 self.useFixture(testfixtures.ConfigFixture())116 caplog.set_level(logging.DEBUG)
123 logger = self.useFixture(fixtures.FakeLogger())
124 snap_id = factory.generate_snap_id()117 snap_id = factory.generate_snap_id()
125 overrides = [118 overrides = [
126 factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"),119 factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"),
@@ -149,35 +142,32 @@ class OverridesTests(TestCase):
149 password=False,142 password=False,
150 )143 )
151 )144 )
152 self.assertEqual(145 assert [
153 [146 {
154 {147 "snap_name": "mysnap",
155 "snap_name": "mysnap",148 "revision": 1,
156 "revision": 1,149 "channel": "stable",
157 "channel": "stable",150 "series": "16",
158 "series": "16",151 },
159 },152 {
160 {153 "snap_name": "mysnap",
161 "snap_name": "mysnap",154 "revision": 3,
162 "revision": 3,155 "channel": "foo/stable",
163 "channel": "foo/stable",156 "series": "16",
164 "series": "16",157 },
165 },158 ] == json.loads(responses.calls[0].request.body.decode())
166 ],159 assert [
167 json.loads(responses.calls[0].request.body.decode()),160 "mysnap stable amd64 1 (upstream 2)",
168 )161 "mysnap foo/stable i386 3 (upstream 4)",
169 self.assertEqual(162 ] == caplog.messages
170 "mysnap stable amd64 1 (upstream 2)\n"163
171 "mysnap foo/stable i386 3 (upstream 4)\n",
172 logger.output,
173 )
174 # We shouldn't have Basic Authorization headers, but Macaroon164 # We shouldn't have Basic Authorization headers, but Macaroon
175 self.assertNotIn("Basic", responses.calls[0].request.headers["Authorization"])165 assert "Basic" not in responses.calls[0].request.headers["Authorization"]
176166
167 @pytest.mark.usefixtures("mocked_config")
177 @responses.activate168 @responses.activate
178 def test_override_offline(self):169 def test_override_offline(self, caplog):
179 self.useFixture(testfixtures.ConfigFixture())170 caplog.set_level(logging.DEBUG)
180 logger = self.useFixture(fixtures.FakeLogger())
181 snap_id = factory.generate_snap_id()171 snap_id = factory.generate_snap_id()
182 overrides = [172 overrides = [
183 factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"),173 factory.SnapDeviceGateway.Override(snap_id=snap_id, snap_name="mysnap"),
@@ -206,52 +196,46 @@ class OverridesTests(TestCase):
206 password="test",196 password="test",
207 )197 )
208 )198 )
209 self.assertEqual(199 assert [
210 [200 {
211 {201 "snap_name": "mysnap",
212 "snap_name": "mysnap",202 "revision": 1,
213 "revision": 1,203 "channel": "stable",
214 "channel": "stable",204 "series": "16",
215 "series": "16",205 },
216 },206 {
217 {207 "snap_name": "mysnap",
218 "snap_name": "mysnap",208 "revision": 3,
219 "revision": 3,209 "channel": "foo/stable",
220 "channel": "foo/stable",210 "series": "16",
221 "series": "16",211 },
222 },212 ] == json.loads(responses.calls[0].request.body.decode())
223 ],213 assert [
224 json.loads(responses.calls[0].request.body.decode()),214 "mysnap stable amd64 1 (upstream 2)",
225 )215 "mysnap foo/stable i386 3 (upstream 4)",
226 self.assertEqual(216 ] == caplog.messages
227 "mysnap stable amd64 1 (upstream 2)\n"217 assert (
228 "mysnap foo/stable i386 3 (upstream 4)\n",218 "Basic YWRtaW46dGVzdA=="
229 logger.output,219 == responses.calls[0].request.headers["Authorization"]
230 )
231 self.assertEqual(
232 "Basic YWRtaW46dGVzdA==",
233 responses.calls[0].request.headers["Authorization"],
234 )220 )
235221
236 def test_delete_override_no_store_config(self):222 @pytest.mark.usefixtures("mocked_empty_config")
237 self.useFixture(testfixtures.ConfigFixture(empty=True))223 def test_delete_override_no_store_config(self, caplog):
238 logger = self.useFixture(fixtures.FakeLogger())
239 rc = delete_override(224 rc = delete_override(
240 factory.Args(225 factory.Args(
241 snap_name="some-snap", channels=["stable"], series="16", password=False226 snap_name="some-snap", channels=["stable"], series="16", password=False
242 )227 )
243 )228 )
244 self.assertEqual(rc, 1)229 assert rc == 1
245 self.assertEqual(230 assert [
246 logger.output,
247 "No store configuration found. "231 "No store configuration found. "
248 'Have you run "snap-store-proxy-client login"?\n',232 'Have you run "snap-store-proxy-client login"?'
249 )233 ] == caplog.messages
250234
235 @pytest.mark.usefixtures("mocked_config")
251 @responses.activate236 @responses.activate
252 def test_delete_override_online(self):237 def test_delete_override_online(self, caplog):
253 self.useFixture(testfixtures.ConfigFixture())238 caplog.set_level(logging.DEBUG)
254 logger = self.useFixture(fixtures.FakeLogger())
255 snap_id = factory.generate_snap_id()239 snap_id = factory.generate_snap_id()
256 overrides = [240 overrides = [
257 factory.SnapDeviceGateway.Override(241 factory.SnapDeviceGateway.Override(
@@ -282,35 +266,31 @@ class OverridesTests(TestCase):
282 password=False,266 password=False,
283 )267 )
284 )268 )
285 self.assertEqual(269 assert [
286 [270 {
287 {271 "snap_name": "mysnap",
288 "snap_name": "mysnap",272 "revision": None,
289 "revision": None,273 "channel": "stable",
290 "channel": "stable",274 "series": "16",
291 "series": "16",275 },
292 },276 {
293 {277 "snap_name": "mysnap",
294 "snap_name": "mysnap",278 "revision": None,
295 "revision": None,279 "channel": "foo/stable",
296 "channel": "foo/stable",280 "series": "16",
297 "series": "16",281 },
298 },282 ] == json.loads(responses.calls[0].request.body.decode())
299 ],283 assert [
300 json.loads(responses.calls[0].request.body.decode()),284 "mysnap stable amd64 is tracking upstream (revision 2)",
301 )285 "mysnap foo/stable i386 is tracking upstream (revision 4)",
302 self.assertEqual(286 ] == caplog.messages
303 "mysnap stable amd64 is tracking upstream (revision 2)\n"
304 "mysnap foo/stable i386 is tracking upstream (revision 4)\n",
305 logger.output,
306 )
307 # We shouldn't have Basic Authorization headers, but Macaroon287 # We shouldn't have Basic Authorization headers, but Macaroon
308 self.assertNotIn("Basic", responses.calls[0].request.headers["Authorization"])288 assert "Basic" not in responses.calls[0].request.headers["Authorization"]
309289
290 @pytest.mark.usefixtures("mocked_config")
310 @responses.activate291 @responses.activate
311 def test_delete_override_offline(self):292 def test_delete_override_offline(self, caplog):
312 self.useFixture(testfixtures.ConfigFixture())293 caplog.set_level(logging.DEBUG)
313 logger = self.useFixture(fixtures.FakeLogger())
314 snap_id = factory.generate_snap_id()294 snap_id = factory.generate_snap_id()
315 overrides = [295 overrides = [
316 factory.SnapDeviceGateway.Override(296 factory.SnapDeviceGateway.Override(
@@ -341,29 +321,25 @@ class OverridesTests(TestCase):
341 password="test",321 password="test",
342 )322 )
343 )323 )
344 self.assertEqual(324 assert [
345 [325 {
346 {326 "snap_name": "mysnap",
347 "snap_name": "mysnap",327 "revision": None,
348 "revision": None,328 "channel": "stable",
349 "channel": "stable",329 "series": "16",
350 "series": "16",330 },
351 },331 {
352 {332 "snap_name": "mysnap",
353 "snap_name": "mysnap",333 "revision": None,
354 "revision": None,334 "channel": "foo/stable",
355 "channel": "foo/stable",335 "series": "16",
356 "series": "16",336 },
357 },337 ] == json.loads(responses.calls[0].request.body.decode())
358 ],338 assert [
359 json.loads(responses.calls[0].request.body.decode()),339 "mysnap stable amd64 is tracking upstream (revision 2)",
360 )340 "mysnap foo/stable i386 is tracking upstream (revision 4)",
361 self.assertEqual(341 ] == caplog.messages
362 "mysnap stable amd64 is tracking upstream (revision 2)\n"342 assert (
363 "mysnap foo/stable i386 is tracking upstream (revision 4)\n",343 "Basic YWRtaW46dGVzdA=="
364 logger.output,344 == responses.calls[0].request.headers["Authorization"]
365 )
366 self.assertEqual(
367 "Basic YWRtaW46dGVzdA==",
368 responses.calls[0].request.headers["Authorization"],
369 )345 )
diff --git a/snapstore_client/logic/tests/test_push.py b/snapstore_client/logic/tests/test_push.py
index e96088f..05b4cee 100644
--- a/snapstore_client/logic/tests/test_push.py
+++ b/snapstore_client/logic/tests/test_push.py
@@ -1,10 +1,9 @@
1import pytest
1import json2import json
2from pathlib import Path3from pathlib import Path
3from urllib.parse import urljoin4from urllib.parse import urljoin
45
5import fixtures
6import responses6import responses
7from testtools import TestCase
87
9from snapstore_client.logic.push import (8from snapstore_client.logic.push import (
10 ChannelMapExistsException,9 ChannelMapExistsException,
@@ -17,11 +16,9 @@ from snapstore_client.logic.push import (
17)16)
1817
1918
20class pushTests(TestCase):19class Testpush:
21 def setUp(self):20 def setup_method(self):
22 super().setUp()
23 self.default_gw_url = "http://store.local"21 self.default_gw_url = "http://store.local"
24 self.logger = self.useFixture(fixtures.FakeLogger())
25 current_path = Path(__file__).resolve().parent22 current_path = Path(__file__).resolve().parent
26 with (current_path / "test-snap-map.json").open() as fh:23 with (current_path / "test-snap-map.json").open() as fh:
27 self.snap_map = json.load(fh)24 self.snap_map = json.load(fh)
@@ -39,21 +36,15 @@ class pushTests(TestCase):
39 request = responses.calls[0].request36 request = responses.calls[0].request
40 payload = json.loads(request.body.decode("utf-8"))37 payload = json.loads(request.body.decode("utf-8"))
4138
42 self.assertEqual(39 assert payload["snaps"][0]["package_type"] == "snap"
43 payload["snaps"][0]["package_type"],40 assert "Basic YWRtaW46dGVzdA==" == request.headers["Authorization"]
44 "snap",
45 )
46 self.assertEqual(
47 "Basic YWRtaW46dGVzdA==",
48 request.headers["Authorization"],
49 )
5041
51 @responses.activate42 @responses.activate
52 def test_push_ident_failed(self):43 def test_push_ident_failed(self):
53 ident_url = urljoin(self.default_gw_url, "/snaps/update")44 ident_url = urljoin(self.default_gw_url, "/snaps/update")
54 responses.add("POST", ident_url, status=500)45 responses.add("POST", ident_url, status=500)
5546
56 self.assertRaises(pushException, _push_ident, self.store, "test", self.snap_map)47 pytest.raises(pushException, _push_ident, self.store, "test", self.snap_map)
5748
58 @responses.activate49 @responses.activate
59 def test_push_revs(self):50 def test_push_revs(self):
@@ -65,25 +56,22 @@ class pushTests(TestCase):
65 request = responses.calls[0].request56 request = responses.calls[0].request
66 payload = json.loads(request.body.decode("utf-8"))57 payload = json.loads(request.body.decode("utf-8"))
6758
68 self.assertEqual(payload[0]["package_type"], "snap")59 assert payload[0]["package_type"] == "snap"
69 self.assertEqual(60 assert "Basic YWRtaW46dGVzdA==" == request.headers["Authorization"]
70 "Basic YWRtaW46dGVzdA==",
71 request.headers["Authorization"],
72 )
7361
74 @responses.activate62 @responses.activate
75 def test_push_revs_unexpected_status_code(self):63 def test_push_revs_unexpected_status_code(self):
76 revs_url = urljoin(self.default_gw_url, "/revisions/create")64 revs_url = urljoin(self.default_gw_url, "/revisions/create")
77 responses.add("POST", revs_url, status=302)65 responses.add("POST", revs_url, status=302)
7866
79 self.assertRaises(pushException, _push_revs, self.store, "test", self.snap_map)67 pytest.raises(pushException, _push_revs, self.store, "test", self.snap_map)
8068
81 @responses.activate69 @responses.activate
82 def test_push_revs_failed(self):70 def test_push_revs_failed(self):
83 revs_url = urljoin(self.default_gw_url, "/revisions/create")71 revs_url = urljoin(self.default_gw_url, "/revisions/create")
84 responses.add("POST", revs_url, status=500)72 responses.add("POST", revs_url, status=500)
8573
86 self.assertRaises(pushException, _push_revs, self.store, "test", self.snap_map)74 pytest.raises(pushException, _push_revs, self.store, "test", self.snap_map)
8775
88 @responses.activate76 @responses.activate
89 def test_push_map(self):77 def test_push_map(self):
@@ -96,29 +84,20 @@ class pushTests(TestCase):
9684
97 filter_request = responses.calls[0].request85 filter_request = responses.calls[0].request
98 filter_payload = json.loads(filter_request.body.decode("utf-8"))86 filter_payload = json.loads(filter_request.body.decode("utf-8"))
99 self.assertEqual(87 assert filter_payload["filters"] == [
100 filter_payload["filters"],88 {
101 [89 "series": "16",
102 {90 "package_type": "snap",
103 "series": "16",91 "snap_id": self.snap_map["snap-id"],
104 "package_type": "snap",92 },
105 "snap_id": self.snap_map["snap-id"],93 ]
106 },94 assert "Basic YWRtaW46dGVzdA==" == filter_request.headers["Authorization"]
107 ],
108 )
109 self.assertEqual(
110 "Basic YWRtaW46dGVzdA==",
111 filter_request.headers["Authorization"],
112 )
11395
114 chanmap_update_request = responses.calls[1].request96 chanmap_update_request = responses.calls[1].request
115 chanmap_update_payload = json.loads(97 chanmap_update_payload = json.loads(
116 chanmap_update_request.body.decode("utf-8"),98 chanmap_update_request.body.decode("utf-8"),
117 )99 )
118 self.assertEqual(100 assert chanmap_update_payload["release_requests"][0]["package_type"] == "snap"
119 chanmap_update_payload["release_requests"][0]["package_type"],
120 "snap",
121 )
122101
123 @responses.activate102 @responses.activate
124 def test_push_map_failed(self):103 def test_push_map_failed(self):
@@ -127,7 +106,7 @@ class pushTests(TestCase):
127 responses.add("POST", filter_url, status=200, json={})106 responses.add("POST", filter_url, status=200, json={})
128 responses.add("POST", update_url, status=500)107 responses.add("POST", update_url, status=500)
129108
130 self.assertRaises(109 pytest.raises(
131 pushException, _push_channelmap, self.store, "test", self.snap_map110 pushException, _push_channelmap, self.store, "test", self.snap_map
132 )111 )
133112
@@ -139,7 +118,7 @@ class pushTests(TestCase):
139 responses.add("POST", filter_url, status=200, json=exists)118 responses.add("POST", filter_url, status=200, json=exists)
140 responses.add("POST", update_url, status=200)119 responses.add("POST", update_url, status=200)
141120
142 self.assertRaises(121 pytest.raises(
143 ChannelMapExistsException,122 ChannelMapExistsException,
144 _push_channelmap,123 _push_channelmap,
145 self.store,124 self.store,
@@ -157,9 +136,9 @@ class pushTests(TestCase):
157136
158 _push_channelmap(self.store, "test", self.snap_map, True)137 _push_channelmap(self.store, "test", self.snap_map, True)
159138
160 self.assertEqual(139 assert (
161 "Basic YWRtaW46dGVzdA==",140 "Basic YWRtaW46dGVzdA=="
162 responses.calls[0].request.headers["Authorization"],141 == responses.calls[0].request.headers["Authorization"]
163 )142 )
164143
165 def test_split_assertions(self):144 def test_split_assertions(self):
@@ -180,13 +159,12 @@ class pushTests(TestCase):
180 split_assertions = _split_assertions(self.snap_assert)159 split_assertions = _split_assertions(self.snap_assert)
181 _add_assertion_to_service(self.store, "test", split_assertions)160 _add_assertion_to_service(self.store, "test", split_assertions)
182161
183 self.assertIn(162 assert "display-name: Tom Wardill (Ω)" in responses.calls[
184 "display-name: Tom Wardill (Ω)",163 0
185 responses.calls[0].request.body.decode("utf-8"),164 ].request.body.decode("utf-8")
186 )165 assert (
187 self.assertEqual(166 "Basic YWRtaW46dGVzdA=="
188 "Basic YWRtaW46dGVzdA==",167 == responses.calls[0].request.headers["Authorization"]
189 responses.calls[0].request.headers["Authorization"],
190 )168 )
191169
192 @responses.activate170 @responses.activate
@@ -195,7 +173,7 @@ class pushTests(TestCase):
195 responses.add("POST", assert_url, status=302)173 responses.add("POST", assert_url, status=302)
196174
197 split_assertions = _split_assertions(self.snap_assert)175 split_assertions = _split_assertions(self.snap_assert)
198 self.assertRaises(176 pytest.raises(
199 pushException,177 pushException,
200 _add_assertion_to_service,178 _add_assertion_to_service,
201 self.store,179 self.store,
@@ -209,7 +187,7 @@ class pushTests(TestCase):
209 responses.add("POST", assert_url, status=500)187 responses.add("POST", assert_url, status=500)
210188
211 split_assertions = _split_assertions(self.snap_assert)189 split_assertions = _split_assertions(self.snap_assert)
212 self.assertRaises(190 pytest.raises(
213 pushException,191 pushException,
214 _add_assertion_to_service,192 _add_assertion_to_service,
215 self.store,193 self.store,
diff --git a/snapstore_client/tests/test_cli.py b/snapstore_client/tests/test_cli.py
216deleted file mode 100644194deleted file mode 100644
index 3ba9dc9..0000000
--- a/snapstore_client/tests/test_cli.py
+++ /dev/null
@@ -1,67 +0,0 @@
1# Copyright 2017 Canonical Ltd. This software is licensed under the
2# GNU General Public License version 3 (see the file LICENSE).
3
4import logging
5
6import fixtures
7from testtools import TestCase
8
9from snapstore_client import cli
10
11
12class ConfigureLoggingTests(TestCase):
13 def setUp(self):
14 super().setUp()
15 self.logger = logging.getLogger(__name__)
16 self.addCleanup(
17 self._restoreLogger,
18 self.logger,
19 self.logger.level,
20 list(self.logger.handlers),
21 )
22 self.stdout = self.useFixture(fixtures.StringStream("stdout")).stream
23 self.stdout.fileno = lambda: 1
24 self.useFixture(fixtures.MonkeyPatch("sys.stdout", self.stdout))
25 self.stderr = self.useFixture(fixtures.StringStream("stderr")).stream
26 self.useFixture(fixtures.MonkeyPatch("sys.stderr", self.stderr))
27
28 @staticmethod
29 def _restoreLogger(logger, level, handlers):
30 logger.setLevel(logger.level)
31 for handler in list(logger.handlers):
32 logger.removeHandler(handler)
33 for handler in handlers:
34 logger.addHandler(handler)
35
36 def test_log_levels(self):
37 self.useFixture(fixtures.MonkeyPatch("os.isatty", lambda fd: True))
38 cli.configure_logging(__name__)
39 self.assertEqual(logging.INFO, self.logger.level)
40 self.logger.debug("Debug")
41 self.logger.info("Info")
42 self.logger.warning("Warning: %s", "smoke")
43 self.logger.error("Error: %s", "fire")
44 self.stdout.seek(0)
45 self.assertEqual("Info\nWarning: smoke\n", self.stdout.read())
46 self.stderr.seek(0)
47 self.assertEqual("\033[0;31mError: fire\033[0m\n", self.stderr.read())
48
49 def test_requests_log_level_default(self):
50 cli.configure_logging(__name__)
51 self.assertEqual(logging.WARNING, logging.getLogger("requests").level)
52
53 def test_requests_log_level_debug(self):
54 cli.configure_logging(__name__, logging.DEBUG)
55 self.assertEqual(logging.DEBUG, logging.getLogger("requests").level)
56
57 def test_requests_log_level_error(self):
58 cli.configure_logging(__name__, logging.ERROR)
59 self.assertEqual(logging.ERROR, logging.getLogger("requests").level)
60
61 def test_no_tty(self):
62 self.useFixture(fixtures.MonkeyPatch("os.isatty", lambda fd: False))
63 self.useFixture(fixtures.EnvironmentVariable("TERM", "xterm"))
64 cli.configure_logging(__name__)
65 self.logger.error("Error: %s", "fire")
66 self.stderr.seek(0)
67 self.assertEqual("Error: fire\n", self.stderr.read())
diff --git a/snapstore_client/tests/test_config.py b/snapstore_client/tests/test_config.py
index 7243554..c2cca69 100644
--- a/snapstore_client/tests/test_config.py
+++ b/snapstore_client/tests/test_config.py
@@ -1,81 +1,57 @@
1# Copyright 2017 Canonical Ltd. This software is licensed under the1# Copyright 2017 Canonical Ltd. This software is licensed under the
2# GNU General Public License version 3 (see the file LICENSE).2# GNU General Public License version 3 (see the file LICENSE).
3import os
34
4import os.path5from snapstore_client.config import Config
5from textwrap import dedent
66
7from testtools import TestCase
87
9from snapstore_client.config import Config8def test_no_config(mocked_xdgconfig):
10from snapstore_client.tests.testfixtures import (9 config_ini = os.path.join(mocked_xdgconfig, Config.xdg_name, "config.ini")
11 ConfigFixture,10 assert not os.path.exists(config_ini)
12 XDGConfigDirFixture,11 Config()
13)12
1413
1514def test_init_loads(mocked_config):
16class ConfigTests(TestCase):15 cfg = Config()
17 def test_no_config(self):16 assert cfg.get("store:default", "gw_url")
18 xdg_path = self.useFixture(XDGConfigDirFixture()).path17
19 config_ini = os.path.join(xdg_path, Config.xdg_name, "config.ini")18
20 self.assertFalse(os.path.exists(config_ini))19def test_save(mocked_xdgconfig):
21 Config()20 config_ini = os.path.join(mocked_xdgconfig, "snap-store-proxy-client", "config.ini")
2221 assert not os.path.exists(config_ini)
23 def test_init_loads(self):22
24 self.useFixture(ConfigFixture())23 cfg = Config()
25 cfg = Config()24 cfg.set("s", "k", "v")
26 self.assertIsNotNone(cfg.get("store:default", "gw_url"))25 cfg.save()
2726
28 def test_save(self):27 assert os.path.exists(config_ini)
29 xdg_path = self.useFixture(XDGConfigDirFixture()).path28
30 config_ini = os.path.join(xdg_path, "snap-store-proxy-client", "config.ini")29 with open(config_ini) as f:
31 self.assertFalse(os.path.exists(config_ini))30 content = f.read()
3231 assert content == ("[s]\n" "k = v\n" "\n")
33 cfg = Config()32
34 cfg.set("s", "k", "v")33
35 cfg.save()34def test_get_missing(mocked_xdgconfig):
3635 cfg = Config()
37 self.assertTrue(os.path.exists(config_ini))36 assert not cfg.get("s", "k")
38 with open(config_ini) as f:37
39 content = f.read()38
40 self.assertEqual(39def test_get(mocked_xdgconfig):
41 content,40 cfg = Config()
42 dedent(41 cfg.set("s", "k", "v")
43 """\42 assert cfg.get("s", "k") == "v"
44 [s]43
45 k = v44
4645def test_section(mocked_xdgconfig):
47 """46 cfg = Config()
48 ),47 s = cfg.section("s")
49 )48 s.set("k", "v")
5049 assert s.get("k") == "v"
51 def test_get_missing(self):50 assert cfg.get("s", "k") == "v"
52 self.useFixture(XDGConfigDirFixture())51
53 cfg = Config()52
54 self.assertIsNone(cfg.get("s", "k"))53def test_store_section(mocked_xdgconfig):
5554 cfg = Config()
56 def test_get(self):55 store = cfg.store_section("foo")
57 self.useFixture(XDGConfigDirFixture())56 store.set("k", "v")
58 cfg = Config()57 assert cfg.get("store:foo", "k") == "v"
59 cfg.set("s", "k", "v")
60 self.assertEqual(cfg.get("s", "k"), "v")
61
62 def test_section(self):
63 self.useFixture(XDGConfigDirFixture())
64 cfg = Config()
65 s = cfg.section("s")
66 s.set("k", "v")
67 self.assertEqual(s.get("k"), "v")
68 self.assertEqual(cfg.get("s", "k"), "v")
69
70 def test_store_section(self):
71 self.useFixture(XDGConfigDirFixture())
72 cfg = Config()
73 store = cfg.store_section("foo")
74 store.set("k", "v")
75 self.assertEqual(cfg.get("store:foo", "k"), "v")
76
77
78def test_suite():
79 from unittest import TestLoader
80
81 return TestLoader().loadTestsFromName(__name__)
diff --git a/snapstore_client/tests/test_presentation_helpers.py b/snapstore_client/tests/test_presentation_helpers.py
index 7399f91..e58e9e9 100644
--- a/snapstore_client/tests/test_presentation_helpers.py
+++ b/snapstore_client/tests/test_presentation_helpers.py
@@ -1,7 +1,6 @@
1# Copyright 2017 Canonical Ltd.1# Copyright 2017 Canonical Ltd.
22
3from testtools import TestCase3import pytest
4from testscenarios import WithScenarios
54
6from snapstore_client.presentation_helpers import (5from snapstore_client.presentation_helpers import (
7 channel_map_string_to_tuple,6 channel_map_string_to_tuple,
@@ -9,129 +8,93 @@ from snapstore_client.presentation_helpers import (
9)8)
109
1110
12class ChannelMapStringToTupleScenarioTests(WithScenarios, TestCase):11@pytest.mark.parametrize(
1312 "channel_map,terms",
14 scenarios = [13 [
15 (14 ("stable=1", ("stable", 1)),
16 "with risk",15 ("2.1/stable=2", ("2.1/stable", 2)),
17 {16 ("stable/hot-fix=42", ("stable/hot-fix", 42)),
18 "channel_map": "stable=1",17 ("2.1/stable/hot-fix=123", ("2.1/stable/hot-fix", 123)),
19 "terms": ("stable", 1),18 ],
20 },19 ids=[
21 ),20 "with risk",
22 (21 "with track and risk",
23 "with track and risk",22 "with risk and branch",
24 {23 "with track, risk and branch",
25 "channel_map": "2.1/stable=2",24 ],
26 "terms": ("2.1/stable", 2),25)
27 },26def test_channel_map_string_to_tuple(channel_map, terms):
28 ),27 assert terms == channel_map_string_to_tuple(channel_map)
29 (
30 "with risk and branch",
31 {
32 "channel_map": "stable/hot-fix=42",
33 "terms": ("stable/hot-fix", 42),
34 },
35 ),
36 (
37 "with track, risk and branch",
38 {
39 "channel_map": "2.1/stable/hot-fix=123",
40 "terms": ("2.1/stable/hot-fix", 123),
41 },
42 ),
43 ]
44
45 def test_run_scenario(self):
46 self.assertEqual(self.terms, channel_map_string_to_tuple(self.channel_map))
47
48
49class ChannelMapStringToTupleErrorTests(WithScenarios, TestCase):
50
51 scenarios = [
52 (
53 "missing revision",
54 {
55 "channel_map": "stable",
56 "error_message": "Invalid channel map string: 'stable'",
57 },
58 ),
59 (
60 "non-integer revision",
61 {
62 "channel_map": "stable=nonsense",
63 "error_message": "Invalid revision string: 'nonsense'",
64 },
65 ),
66 ]
6728
68 def test_run_scenario(self):
69 error = self.assertRaises(
70 ValueError, channel_map_string_to_tuple, self.channel_map
71 )
72 self.assertEqual(self.error_message, str(error))
7329
30@pytest.mark.parametrize(
31 "channel_map,error_message",
32 [
33 ("stable", "Invalid channel map string: 'stable'"),
34 ("stable=nonsense", "Invalid revision string: 'nonsense'"),
35 ],
36 ids=["missing revision", "non-integer revision"],
37)
38def test_channel_map_string_to_tuple_exceptions(channel_map, error_message):
39 with pytest.raises(ValueError) as error:
40 channel_map_string_to_tuple(channel_map)
41 assert error_message == str(error.value)
7442
75class OverrideToStringTests(WithScenarios, TestCase):
7643
77 scenarios = [44@pytest.mark.parametrize(
45 "override,string",
46 [
78 (47 (
79 "without revision or upstream revision",
80 {48 {
81 "override": {49 "snap_id": "dummy",
82 "snap_id": "dummy",50 "snap_name": "mysnap",
83 "snap_name": "mysnap",51 "revision": None,
84 "revision": None,52 "upstream_revision": None,
85 "upstream_revision": None,53 "channel": "stable",
86 "channel": "stable",54 "architecture": "amd64",
87 "architecture": "amd64",
88 },
89 "string": "mysnap stable amd64 is tracking upstream",
90 },55 },
56 "mysnap stable amd64 is tracking upstream",
91 ),57 ),
92 (58 (
93 "without revision but with upstream revision",
94 {59 {
95 "override": {60 "snap_id": "dummy",
96 "snap_id": "dummy",61 "snap_name": "mysnap",
97 "snap_name": "mysnap",62 "revision": None,
98 "revision": None,63 "upstream_revision": 2,
99 "upstream_revision": 2,64 "channel": "stable",
100 "channel": "stable",65 "architecture": "amd64",
101 "architecture": "amd64",
102 },
103 "string": "mysnap stable amd64 is tracking upstream (revision 2)",
104 },66 },
67 "mysnap stable amd64 is tracking upstream (revision 2)",
105 ),68 ),
106 (69 (
107 "with revision but without upstream revision",
108 {70 {
109 "override": {71 "snap_id": "dummy",
110 "snap_id": "dummy",72 "snap_name": "mysnap",
111 "snap_name": "mysnap",73 "revision": 1,
112 "revision": 1,74 "upstream_revision": None,
113 "upstream_revision": None,75 "channel": "stable",
114 "channel": "stable",76 "architecture": "amd64",
115 "architecture": "amd64",
116 },
117 "string": "mysnap stable amd64 1",
118 },77 },
78 "mysnap stable amd64 1",
119 ),79 ),
120 (80 (
121 "with revision and upstream revision",
122 {81 {
123 "override": {82 "snap_id": "dummy",
124 "snap_id": "dummy",83 "snap_name": "mysnap",
125 "snap_name": "mysnap",84 "revision": 1,
126 "revision": 1,85 "upstream_revision": 2,
127 "upstream_revision": 2,86 "channel": "stable",
128 "channel": "stable",87 "architecture": "amd64",
129 "architecture": "amd64",
130 },
131 "string": "mysnap stable amd64 1 (upstream 2)",
132 },88 },
89 "mysnap stable amd64 1 (upstream 2)",
133 ),90 ),
134 ]91 ],
13592 ids=[
136 def test_run_scenario(self):93 "without revision or upstream revision",
137 self.assertEqual(self.string, override_to_string(self.override))94 "without revision but with upstream revision",
95 "with revision but without upstream revision",
96 "with revision and upstream revision",
97 ],
98)
99def test_override_to_string(override, string):
100 assert string == override_to_string(override)
diff --git a/snapstore_client/tests/test_webservices.py b/snapstore_client/tests/test_webservices.py
index ec2019d..3c667bb 100644
--- a/snapstore_client/tests/test_webservices.py
+++ b/snapstore_client/tests/test_webservices.py
@@ -1,13 +1,12 @@
1# Copyright 2017 Canonical Ltd.1# Copyright 2017 Canonical Ltd.
22
3import pytest
3import json4import json
4import sys5import sys
5from urllib.parse import urljoin6from urllib.parse import urljoin
67
7import fixtures
8from requests.exceptions import HTTPError8from requests.exceptions import HTTPError
9import responses9import responses
10from testtools import TestCase
1110
12from snapstore_client import (11from snapstore_client import (
13 config,12 config,
@@ -17,18 +16,14 @@ from snapstore_client import (
17from snapstore_client.tests import (16from snapstore_client.tests import (
18 factory,17 factory,
19 matchers,18 matchers,
20 testfixtures,
21)19)
2220
23if sys.version < "3.6":21if sys.version < "3.6":
24 import sha3 # noqa22 import sha3 # noqa
2523
2624
27class WebservicesTests(TestCase):25@pytest.mark.usefixtures("mocked_config")
28 def setUp(self):26class TestWebservices:
29 super().setUp()
30 self.config = self.useFixture(testfixtures.ConfigFixture())
31
32 @responses.activate27 @responses.activate
33 def test_issue_store_admin_success(self):28 def test_issue_store_admin_success(self):
34 gw_url = "http://store.local/"29 gw_url = "http://store.local/"
@@ -37,11 +32,10 @@ class WebservicesTests(TestCase):
37 "POST", issue_store_admin_url, status=200, json={"macaroon": "dummy"}32 "POST", issue_store_admin_url, status=200, json={"macaroon": "dummy"}
38 )33 )
3934
40 self.assertEqual("dummy", webservices.issue_store_admin(gw_url))35 assert "dummy" == webservices.issue_store_admin(gw_url)
4136
42 @responses.activate37 @responses.activate
43 def test_issue_store_admin_error(self):38 def test_issue_store_admin_error(self, caplog):
44 logger = self.useFixture(fixtures.FakeLogger())
45 gw_url = "http://store.local/"39 gw_url = "http://store.local/"
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")
47 responses.add(41 responses.add(
@@ -51,11 +45,11 @@ class WebservicesTests(TestCase):
51 json=factory.APIError.single("Something went wrong").to_dict(),45 json=factory.APIError.single("Something went wrong").to_dict(),
52 )46 )
5347
54 self.assertRaises(HTTPError, webservices.issue_store_admin, gw_url)48 pytest.raises(HTTPError, webservices.issue_store_admin, gw_url)
55 self.assertEqual(49 assert [
56 "Failed to issue store_admin macaroon:\nSomething went wrong\n",50 "Failed to issue store_admin macaroon:",
57 logger.output,51 "Something went wrong",
58 )52 ] == caplog.messages
5953
60 @responses.activate54 @responses.activate
61 def test_get_sso_discharge_success(self):55 def test_get_sso_discharge_success(self):
@@ -65,22 +59,16 @@ class WebservicesTests(TestCase):
65 "POST", discharge_url, status=200, json={"discharge_macaroon": "dummy"}59 "POST", discharge_url, status=200, json={"discharge_macaroon": "dummy"}
66 )60 )
6761
68 self.assertEqual(62 assert "dummy" == webservices.get_sso_discharge(
69 "dummy",63 sso_url, "user@example.org", "secret", "caveat"
70 webservices.get_sso_discharge(
71 sso_url, "user@example.org", "secret", "caveat"
72 ),
73 )64 )
74 request = responses.calls[0].request65 request = responses.calls[0].request
75 self.assertEqual("application/json", request.headers["Content-Type"])66 assert "application/json" == request.headers["Content-Type"]
76 self.assertEqual(67 assert {
77 {68 "email": "user@example.org",
78 "email": "user@example.org",69 "password": "secret",
79 "password": "secret",70 "caveat_id": "caveat",
80 "caveat_id": "caveat",71 } == json.loads(request.body.decode())
81 },
82 json.loads(request.body.decode()),
83 )
8472
85 @responses.activate73 @responses.activate
86 def test_get_sso_discharge_success_with_otp(self):74 def test_get_sso_discharge_success_with_otp(self):
@@ -90,27 +78,21 @@ class WebservicesTests(TestCase):
90 "POST", discharge_url, status=200, json={"discharge_macaroon": "dummy"}78 "POST", discharge_url, status=200, json={"discharge_macaroon": "dummy"}
91 )79 )
9280
93 self.assertEqual(81 assert "dummy" == webservices.get_sso_discharge(
94 "dummy",82 sso_url,
95 webservices.get_sso_discharge(83 "user@example.org",
96 sso_url,84 "secret",
97 "user@example.org",85 "caveat",
98 "secret",86 one_time_password="123456",
99 "caveat",
100 one_time_password="123456",
101 ),
102 )87 )
103 request = responses.calls[0].request88 request = responses.calls[0].request
104 self.assertEqual("application/json", request.headers["Content-Type"])89 assert "application/json" == request.headers["Content-Type"]
105 self.assertEqual(90 assert {
106 {91 "email": "user@example.org",
107 "email": "user@example.org",92 "password": "secret",
108 "password": "secret",93 "caveat_id": "caveat",
109 "caveat_id": "caveat",94 "otp": "123456",
110 "otp": "123456",95 } == json.loads(request.body.decode())
111 },
112 json.loads(request.body.decode()),
113 )
11496
115 @responses.activate97 @responses.activate
116 def test_get_sso_discharge_twofactor_required(self):98 def test_get_sso_discharge_twofactor_required(self):
@@ -123,7 +105,7 @@ class WebservicesTests(TestCase):
123 json={"error_list": [{"code": "twofactor-required"}]},105 json={"error_list": [{"code": "twofactor-required"}]},
124 )106 )
125107
126 self.assertRaises(108 pytest.raises(
127 exceptions.StoreTwoFactorAuthenticationRequired,109 exceptions.StoreTwoFactorAuthenticationRequired,
128 webservices.get_sso_discharge,110 webservices.get_sso_discharge,
129 sso_url,111 sso_url,
@@ -147,7 +129,7 @@ class WebservicesTests(TestCase):
147 },129 },
148 )130 )
149131
150 e = self.assertRaises(132 e = pytest.raises(
151 exceptions.StoreAuthenticationError,133 exceptions.StoreAuthenticationError,
152 webservices.get_sso_discharge,134 webservices.get_sso_discharge,
153 sso_url,135 sso_url,
@@ -155,16 +137,15 @@ class WebservicesTests(TestCase):
155 "secret",137 "secret",
156 "caveat",138 "caveat",
157 )139 )
158 self.assertEqual("Something went wrong", e.message)140 assert "Authentication error: Something went wrong" == str(e.value)
159141
160 @responses.activate142 @responses.activate
161 def test_get_sso_discharge_unstructured_error(self):143 def test_get_sso_discharge_unstructured_error(self, caplog):
162 logger = self.useFixture(fixtures.FakeLogger())
163 sso_url = "http://sso.local/"144 sso_url = "http://sso.local/"
164 discharge_url = urljoin(sso_url, "/api/v2/tokens/discharge")145 discharge_url = urljoin(sso_url, "/api/v2/tokens/discharge")
165 responses.add("POST", discharge_url, status=503, body="Try again later.")146 responses.add("POST", discharge_url, status=503, body="Try again later.")
166147
167 self.assertRaises(148 pytest.raises(
168 HTTPError,149 HTTPError,
169 webservices.get_sso_discharge,150 webservices.get_sso_discharge,
170 sso_url,151 sso_url,
@@ -172,17 +153,15 @@ class WebservicesTests(TestCase):
172 "secret",153 "secret",
173 "caveat",154 "caveat",
174 )155 )
175 self.assertEqual(156 assert [
176 "Failed to get SSO discharge:\n"157 "Failed to get SSO discharge:",
177 "====================\n"158 "====================",
178 "Try again later.\n"159 "Try again later.",
179 "====================\n",160 "====================",
180 logger.output,161 ] == caplog.messages
181 )
182162
183 @responses.activate163 @responses.activate
184 def test_get_overrides_success(self):164 def test_get_overrides_success(self, caplog):
185 logger = self.useFixture(fixtures.FakeLogger())
186 overrides = [factory.SnapDeviceGateway.Override()]165 overrides = [factory.SnapDeviceGateway.Override()]
187 # XXX cjwatson 2017-06-26: Use acceptable-generated double once it166 # XXX cjwatson 2017-06-26: Use acceptable-generated double once it
188 # exists.167 # exists.
@@ -190,17 +169,18 @@ class WebservicesTests(TestCase):
190 overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides/mysnap")169 overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides/mysnap")
191 responses.add("GET", overrides_url, status=200, json=overrides)170 responses.add("GET", overrides_url, status=200, json=overrides)
192171
193 self.assertEqual(overrides, webservices.get_overrides(store, "mysnap"))172 assert overrides == webservices.get_overrides(store, "mysnap")
194 request = responses.calls[0].request173 request = responses.calls[0].request
195 self.assertThat(174 assert (
196 request.headers["Authorization"],175 matchers.MacaroonHeaderVerifies("random-key").match(
197 matchers.MacaroonHeaderVerifies(self.config.key),176 request.headers["Authorization"]
177 )
178 is None
198 )179 )
199 self.assertNotIn("Failed to get overrides:", logger.output)180 assert "Failed to get overrides:" not in caplog.messages
200181
201 @responses.activate182 @responses.activate
202 def test_get_overrides_error(self):183 def test_get_overrides_error(self, caplog):
203 logger = self.useFixture(fixtures.FakeLogger())
204 store = config.Config().store_section("default")184 store = config.Config().store_section("default")
205 overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides/mysnap")185 overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides/mysnap")
206 responses.add(186 responses.add(
@@ -210,14 +190,11 @@ class WebservicesTests(TestCase):
210 json=factory.APIError.single("Something went wrong").to_dict(),190 json=factory.APIError.single("Something went wrong").to_dict(),
211 )191 )
212192
213 self.assertRaises(HTTPError, webservices.get_overrides, store, "mysnap")193 pytest.raises(HTTPError, webservices.get_overrides, store, "mysnap")
214 self.assertEqual(194 assert ["Failed to get overrides:", "Something went wrong"] == caplog.messages
215 "Failed to get overrides:\nSomething went wrong\n", logger.output
216 )
217195
218 @responses.activate196 @responses.activate
219 def test_set_overrides_success(self):197 def test_set_overrides_success(self, caplog):
220 logger = self.useFixture(fixtures.FakeLogger())
221 override = factory.SnapDeviceGateway.Override()198 override = factory.SnapDeviceGateway.Override()
222 # XXX cjwatson 2017-06-26: Use acceptable-generated double once it199 # XXX cjwatson 2017-06-26: Use acceptable-generated double once it
223 # exists.200 # exists.
@@ -225,26 +202,8 @@ class WebservicesTests(TestCase):
225 overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides")202 overrides_url = urljoin(store.get("gw_url"), "/v2/metadata/overrides")
226 responses.add("POST", overrides_url, status=200, json=[override])203 responses.add("POST", overrides_url, status=200, json=[override])
227204
228 self.assertEqual(205 assert [override] == webservices.set_overrides(
229 [override],206 store,
230 webservices.set_overrides(
231 store,
232 [
233 {
234 "snap_name": override["snap_name"],
235 "revision": override["revision"],
236 "channel": override["channel"],
237 "series": override["series"],
238 }
239 ],
240 ),
241 )
242 request = responses.calls[0].request
243 self.assertThat(
244 request.headers["Authorization"],
245 matchers.MacaroonHeaderVerifies(self.config.key),
246 )
247 self.assertEqual(
248 [207 [
249 {208 {
250 "snap_name": override["snap_name"],209 "snap_name": override["snap_name"],
@@ -253,13 +212,27 @@ class WebservicesTests(TestCase):
253 "series": override["series"],212 "series": override["series"],
254 }213 }
255 ],214 ],
256 json.loads(request.body.decode()),
257 )215 )
258 self.assertNotIn("Failed to set override:", logger.output)216 request = responses.calls[0].request
217 assert (
218 matchers.MacaroonHeaderVerifies("random-key").match(
219 request.headers["Authorization"]
220 )
221 is None
222 )
223
224 assert [
225 {
226 "snap_name": override["snap_name"],
227 "revision": override["revision"],
228 "channel": override["channel"],
229 "series": override["series"],
230 }
231 ] == json.loads(request.body.decode())
232 assert "Failed to set override:" not in caplog.messages
259233
260 @responses.activate234 @responses.activate
261 def test_set_overrides_error(self):235 def test_set_overrides_error(self, caplog):
262 logger = self.useFixture(fixtures.FakeLogger())
263 override = factory.SnapDeviceGateway.Override()236 override = factory.SnapDeviceGateway.Override()
264 # XXX cjwatson 2017-06-26: Use acceptable-generated double once it237 # XXX cjwatson 2017-06-26: Use acceptable-generated double once it
265 # exists.238 # exists.
@@ -272,7 +245,7 @@ class WebservicesTests(TestCase):
272 json=factory.APIError.single("Something went wrong").to_dict(),245 json=factory.APIError.single("Something went wrong").to_dict(),
273 )246 )
274247
275 self.assertRaises(248 pytest.raises(
276 HTTPError,249 HTTPError,
277 lambda: webservices.set_overrides(250 lambda: webservices.set_overrides(
278 store,251 store,
@@ -284,6 +257,4 @@ class WebservicesTests(TestCase):
284 },257 },
285 ),258 ),
286 )259 )
287 self.assertEqual(260 assert ["Failed to set override:", "Something went wrong"] == caplog.messages
288 "Failed to set override:\nSomething went wrong\n", logger.output
289 )

Subscribers

People subscribed via source and target branches

to all changes: