Merge ~cjwatson/launchpad:macaroon-verifies-matcher into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 6fc5d62a6c318cc406fd1c0e7742dbe6c426481e
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:macaroon-verifies-matcher
Merge into: launchpad:master
Diff against target: 141 lines (+50/-32)
2 files modified
lib/lp/code/model/tests/test_codeimportjob.py (+5/-17)
lib/lp/services/macaroons/testing.py (+45/-15)
Reviewer Review Type Date Requested Status
Ioana Lasc (community) Approve
Review via email: mp+401311@code.launchpad.net

Commit message

Add a new MacaroonVerifies matcher

Description of the change

I generalized this from CodeImportJobMacaroonVerifies. In some cases this is more convenient than the helper methods in MacaroonTestMixin, since it's easier to compose it with other matchers.

To post a comment you must log in.
Revision history for this message
Ioana Lasc (ilasc) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/code/model/tests/test_codeimportjob.py b/lib/lp/code/model/tests/test_codeimportjob.py
2index d31d32f..a5f3e6e 100644
3--- a/lib/lp/code/model/tests/test_codeimportjob.py
4+++ b/lib/lp/code/model/tests/test_codeimportjob.py
5@@ -18,10 +18,8 @@ from pymacaroons import Macaroon
6 from pytz import UTC
7 from testtools.matchers import (
8 Equals,
9- Matcher,
10 MatchesListwise,
11 MatchesStructure,
12- Mismatch,
13 )
14 import transaction
15 from zope.component import getUtility
16@@ -66,7 +64,10 @@ from lp.services.macaroons.interfaces import (
17 BadMacaroonContext,
18 IMacaroonIssuer,
19 )
20-from lp.services.macaroons.testing import MacaroonTestMixin
21+from lp.services.macaroons.testing import (
22+ MacaroonTestMixin,
23+ MacaroonVerifies,
24+ )
25 from lp.services.webapp import canonical_url
26 from lp.testing import (
27 ANONYMOUS,
28@@ -95,19 +96,6 @@ def login_for_code_imports():
29 return login_celebrity('vcs_imports')
30
31
32-class CodeImportJobMacaroonVerifies(Matcher):
33- """Matches if a code-import-job macaroon can be verified."""
34-
35- def __init__(self, code_import):
36- self.code_import = code_import
37-
38- def match(self, macaroon_raw):
39- issuer = getUtility(IMacaroonIssuer, 'code-import-job')
40- macaroon = Macaroon.deserialize(macaroon_raw)
41- if not issuer.verifyMacaroon(macaroon, self.code_import.import_job):
42- return Mismatch("Macaroon '%s' does not verify" % macaroon_raw)
43-
44-
45 class TestCodeImportJob(TestCaseWithFactory):
46
47 layer = DatabaseFunctionalLayer
48@@ -154,7 +142,7 @@ class TestCodeImportJob(TestCaseWithFactory):
49 Equals('git'), Equals('git'),
50 Equals('git://git.example.com/project.git'),
51 Equals('--macaroon'),
52- CodeImportJobMacaroonVerifies(code_import),
53+ MacaroonVerifies('code-import-job', code_import.import_job),
54 Equals('--exclude-host'), Equals('launchpad.test'),
55 ]),
56 # Start the job so that the macaroon can be verified.
57diff --git a/lib/lp/services/macaroons/testing.py b/lib/lp/services/macaroons/testing.py
58index ec987f0..c4952f2 100644
59--- a/lib/lp/services/macaroons/testing.py
60+++ b/lib/lp/services/macaroons/testing.py
61@@ -9,10 +9,19 @@ __metaclass__ = type
62 __all__ = [
63 'find_caveats_by_name',
64 'MacaroonTestMixin',
65+ 'MacaroonVerifies',
66 ]
67
68+from pymacaroons import Macaroon
69 from testtools.content import text_content
70-from testtools.matchers import MatchesStructure
71+from testtools.matchers import (
72+ Matcher,
73+ MatchesStructure,
74+ Mismatch,
75+ )
76+from zope.component import getUtility
77+
78+from lp.services.macaroons.interfaces import IMacaroonIssuer
79
80
81 def find_caveats_by_name(macaroon, caveat_name):
82@@ -21,24 +30,45 @@ def find_caveats_by_name(macaroon, caveat_name):
83 if caveat.caveat_id.startswith(caveat_name + " ")]
84
85
86+class MacaroonVerifies(Matcher):
87+ """Matches if a macaroon can be verified."""
88+
89+ def __init__(self, issuer_name, context, matcher=None, **verify_kwargs):
90+ super(MacaroonVerifies, self).__init__()
91+ self.issuer_name = issuer_name
92+ self.context = context
93+ self.matcher = matcher
94+ self.verify_kwargs = verify_kwargs
95+
96+ def match(self, macaroon_raw):
97+ issuer = getUtility(IMacaroonIssuer, self.issuer_name)
98+ macaroon = Macaroon.deserialize(macaroon_raw)
99+ errors = []
100+ verified = issuer.verifyMacaroon(
101+ macaroon, self.context, errors=errors, **self.verify_kwargs)
102+ if not verified:
103+ return Mismatch(
104+ "Macaroon '%s' does not verify" % macaroon_raw,
105+ {"errors": text_content("\n".join(errors))})
106+ mismatch = MatchesStructure.byEquality(
107+ issuer_name=self.issuer_name).match(verified)
108+ if mismatch is not None:
109+ return mismatch
110+ if self.matcher is not None:
111+ return self.matcher.match(verified)
112+
113+
114 class MacaroonTestMixin:
115
116 def assertMacaroonVerifies(self, issuer, macaroon, context, **kwargs):
117- errors = []
118- try:
119- verified = issuer.verifyMacaroon(
120- macaroon, context, errors=errors, **kwargs)
121- self.assertIsNotNone(verified)
122- self.assertThat(verified, MatchesStructure.byEquality(
123- issuer_name=issuer.identifier))
124- except Exception:
125- if errors:
126- self.addDetail("errors", text_content("\n".join(errors)))
127- raise
128+ self.assertThat(
129+ macaroon.serialize(),
130+ MacaroonVerifies(issuer.identifier, context, **kwargs))
131
132 def assertMacaroonDoesNotVerify(self, expected_errors, issuer, macaroon,
133 context, **kwargs):
134- errors = []
135- self.assertIsNone(issuer.verifyMacaroon(
136- macaroon, context, errors=errors, **kwargs))
137+ matcher = MacaroonVerifies(issuer.identifier, context, **kwargs)
138+ mismatch = matcher.match(macaroon.serialize())
139+ self.assertIsNotNone(mismatch)
140+ errors = mismatch.get_details()["errors"].as_text().splitlines()
141 self.assertEqual(expected_errors, errors)

Subscribers

People subscribed via source and target branches

to status/vote changes: