Merge ~cjwatson/launchpad:git-xmlrpc-log-request-id into launchpad:master
- Git
- lp:~cjwatson/launchpad
- git-xmlrpc-log-request-id
- Merge into master
Proposed by
Colin Watson
Status: | Merged |
---|---|
Approved by: | Colin Watson |
Approved revision: | 9ceac6cba62b9792ff04c8e056e36649fca3328e |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~cjwatson/launchpad:git-xmlrpc-log-request-id |
Merge into: | launchpad:master |
Diff against target: |
479 lines (+168/-65) 2 files modified
lib/lp/code/xmlrpc/git.py (+73/-10) lib/lp/code/xmlrpc/tests/test_git.py (+95/-55) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tom Wardill (community) | Approve | ||
Review via email: mp+374221@code.launchpad.net |
Commit message
Log requests and responses in Git XML-RPC endpoint
LP: #1846313
Description of the change
I don't love the test changes here, but I've tried to make them as clean as possible.
To post a comment you must log in.
Revision history for this message
Tom Wardill (twom) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/lib/lp/code/xmlrpc/git.py b/lib/lp/code/xmlrpc/git.py |
2 | index d85aae7..cbf9205 100644 |
3 | --- a/lib/lp/code/xmlrpc/git.py |
4 | +++ b/lib/lp/code/xmlrpc/git.py |
5 | @@ -8,7 +8,9 @@ __all__ = [ |
6 | 'GitAPI', |
7 | ] |
8 | |
9 | +import logging |
10 | import sys |
11 | +import uuid |
12 | |
13 | from pymacaroons import Macaroon |
14 | import six |
15 | @@ -109,6 +111,15 @@ def _can_internal_issuer_write(verified): |
16 | return verified.issuer_name == "code-import-job" |
17 | |
18 | |
19 | +class GitLoggerAdapter(logging.LoggerAdapter): |
20 | + """A logger adapter that adds request-id information.""" |
21 | + |
22 | + def process(self, msg, kwargs): |
23 | + if self.extra is not None and self.extra.get("request-id"): |
24 | + msg = "[request-id=%s] %s" % (self.extra["request-id"], msg) |
25 | + return msg, kwargs |
26 | + |
27 | + |
28 | @implementer(IGitAPI) |
29 | class GitAPI(LaunchpadXMLRPCView): |
30 | """See `IGitAPI`.""" |
31 | @@ -300,6 +311,14 @@ class GitAPI(LaunchpadXMLRPCView): |
32 | getUtility(IErrorReportingUtility).raising(sys.exc_info(), request) |
33 | raise faults.OopsOccurred("creating a Git repository", request.oopsid) |
34 | |
35 | + def _getLogger(self, request_id=None): |
36 | + # XXX cjwatson 2019-10-16: Ideally we'd always have a request-id, |
37 | + # but since that isn't yet the case, generate one if necessary. |
38 | + if request_id is None: |
39 | + request_id = uuid.uuid4() |
40 | + return GitLoggerAdapter( |
41 | + logging.getLogger(__name__), {"request-id": request_id}) |
42 | + |
43 | def _createRepository(self, requester, path, clone_from=None): |
44 | try: |
45 | namespace, repository_name, target_default, owner_default = ( |
46 | @@ -376,18 +395,36 @@ class GitAPI(LaunchpadXMLRPCView): |
47 | |
48 | def translatePath(self, path, permission, auth_params): |
49 | """See `IGitAPI`.""" |
50 | - return run_with_login( |
51 | - _get_requester_id(auth_params), self._translatePath, |
52 | + logger = self._getLogger(auth_params.get("request-id")) |
53 | + requester_id = _get_requester_id(auth_params) |
54 | + logger.info( |
55 | + "Request received: translatePath('%s', '%s') for %s", |
56 | + path, permission, requester_id) |
57 | + result = run_with_login( |
58 | + requester_id, self._translatePath, |
59 | six.ensure_text(path).strip("/"), permission, auth_params) |
60 | + if isinstance(result, xmlrpc_client.Fault): |
61 | + logger.error("translatePath failed: %r", result) |
62 | + else: |
63 | + # The results of path translation are not sensitive for logging |
64 | + # purposes (they may refer to private artifacts, but contain no |
65 | + # credentials). |
66 | + logger.info("translatePath succeeded: %s", result) |
67 | + return result |
68 | |
69 | def notify(self, translated_path): |
70 | """See `IGitAPI`.""" |
71 | + logger = self._getLogger() |
72 | + logger.info("Request received: notify('%s')", translated_path) |
73 | repository = getUtility(IGitLookup).getByHostingPath(translated_path) |
74 | if repository is None: |
75 | - return faults.NotFound( |
76 | + fault = faults.NotFound( |
77 | "No repository found for '%s'." % translated_path) |
78 | + logger.error("notify failed: %r", fault) |
79 | + return fault |
80 | getUtility(IGitRefScanJobSource).create( |
81 | removeSecurityProxy(repository)) |
82 | + logger.info("notify succeeded") |
83 | |
84 | @return_fault |
85 | def _authenticateWithPassword(self, username, password): |
86 | @@ -411,7 +448,19 @@ class GitAPI(LaunchpadXMLRPCView): |
87 | |
88 | def authenticateWithPassword(self, username, password): |
89 | """See `IGitAPI`.""" |
90 | - return self._authenticateWithPassword(username, password) |
91 | + logger = self._getLogger() |
92 | + logger.info( |
93 | + "Request received: authenticateWithPassword('%s')", username) |
94 | + result = self._authenticateWithPassword(username, password) |
95 | + if isinstance(result, xmlrpc_client.Fault): |
96 | + logger.error("authenticateWithPassword failed: %r", result) |
97 | + else: |
98 | + # The results of authentication may be sensitive, but we can at |
99 | + # least log the authenticated user. |
100 | + logger.info( |
101 | + "authenticateWithPassword succeeded: %s", |
102 | + result.get("uid", result.get("user"))) |
103 | + return result |
104 | |
105 | def _renderPermissions(self, set_of_permissions): |
106 | """Render a set of permission strings for XML-RPC output.""" |
107 | @@ -470,9 +519,23 @@ class GitAPI(LaunchpadXMLRPCView): |
108 | |
109 | def checkRefPermissions(self, translated_path, ref_paths, auth_params): |
110 | """See `IGitAPI`.""" |
111 | - return run_with_login( |
112 | - _get_requester_id(auth_params), |
113 | - self._checkRefPermissions, |
114 | - translated_path, |
115 | - ref_paths, |
116 | - auth_params) |
117 | + logger = self._getLogger(auth_params.get("request-id")) |
118 | + requester_id = _get_requester_id(auth_params) |
119 | + logger.info( |
120 | + "Request received: checkRefPermissions('%s', %s) for %s", |
121 | + translated_path, [ref_path.data for ref_path in ref_paths], |
122 | + requester_id) |
123 | + result = run_with_login( |
124 | + requester_id, self._checkRefPermissions, |
125 | + translated_path, ref_paths, auth_params) |
126 | + if isinstance(result, xmlrpc_client.Fault): |
127 | + logger.error("checkRefPermissions failed: %r", result) |
128 | + else: |
129 | + # The results of ref permission checks are not sensitive for |
130 | + # logging purposes (they may refer to private artifacts, but |
131 | + # contain no credentials). |
132 | + logger.info( |
133 | + "checkRefPermissions succeeded: %s", |
134 | + [(ref_path.data, permissions) |
135 | + for ref_path, permissions in result]) |
136 | + return result |
137 | diff --git a/lib/lp/code/xmlrpc/tests/test_git.py b/lib/lp/code/xmlrpc/tests/test_git.py |
138 | index 757443a..f1846e7 100644 |
139 | --- a/lib/lp/code/xmlrpc/tests/test_git.py |
140 | +++ b/lib/lp/code/xmlrpc/tests/test_git.py |
141 | @@ -5,6 +5,9 @@ |
142 | |
143 | __metaclass__ = type |
144 | |
145 | +import uuid |
146 | + |
147 | +from fixtures import FakeLogger |
148 | from pymacaroons import Macaroon |
149 | import six |
150 | from six.moves import xmlrpc_client |
151 | @@ -13,6 +16,7 @@ from testtools.matchers import ( |
152 | IsInstance, |
153 | MatchesAll, |
154 | MatchesListwise, |
155 | + MatchesRegex, |
156 | MatchesSetwise, |
157 | MatchesStructure, |
158 | ) |
159 | @@ -67,7 +71,10 @@ from lp.xmlrpc import faults |
160 | |
161 | |
162 | def _make_auth_params(requester, can_authenticate=False, macaroon_raw=None): |
163 | - auth_params = {"can-authenticate": can_authenticate} |
164 | + auth_params = { |
165 | + "can-authenticate": can_authenticate, |
166 | + "request-id": str(uuid.uuid4()), |
167 | + } |
168 | if requester == LAUNCHPAD_SERVICES: |
169 | auth_params["user"] = LAUNCHPAD_SERVICES |
170 | elif requester is not None: |
171 | @@ -146,21 +153,42 @@ class TestGitAPIMixin: |
172 | self.hosting_fixture = self.useFixture(GitHostingFixture()) |
173 | self.repository_set = getUtility(IGitRepositorySet) |
174 | |
175 | - def assertFault(self, expected_fault, func, *args, **kwargs): |
176 | + def assertFault(self, expected_fault, request_id, func_name, |
177 | + *args, **kwargs): |
178 | """Assert that a call raises the expected fault.""" |
179 | - fault = self.assertRaises(xmlrpc_client.Fault, func, *args, **kwargs) |
180 | - self.assertThat(fault, MatchesFault(expected_fault)) |
181 | + with FakeLogger() as logger: |
182 | + fault = self.assertRaises( |
183 | + xmlrpc_client.Fault, getattr(self.git_api, func_name), |
184 | + *args, **kwargs) |
185 | + self.assertThat(fault, MatchesFault(expected_fault)) |
186 | + self.assertThat(logger.output, MatchesRegex( |
187 | + r"\[request-id=%s\] Request received: %s.*\n" |
188 | + r"\[request-id=%s\] %s failed: .* %s:.*" % ( |
189 | + request_id or ".*", func_name, |
190 | + request_id or ".*", func_name, fault.faultCode))) |
191 | return fault |
192 | |
193 | + def assertDoesNotFault(self, request_id, func_name, *args, **kwargs): |
194 | + """Assert that a call does not raise a fault.""" |
195 | + with FakeLogger() as logger: |
196 | + results = getattr(self.git_api, func_name)(*args, **kwargs) |
197 | + self.assertThat(logger.output, MatchesRegex( |
198 | + r"\[request-id=%s\] Request received: %s.*\n" |
199 | + r"\[request-id=%s\] %s succeeded.*" % ( |
200 | + request_id or ".*", func_name, |
201 | + request_id or ".*", func_name))) |
202 | + return results |
203 | + |
204 | def assertGitRepositoryNotFound(self, requester, path, permission="read", |
205 | can_authenticate=False, macaroon_raw=None): |
206 | """Assert that the given path cannot be translated.""" |
207 | auth_params = _make_auth_params( |
208 | requester, can_authenticate=can_authenticate, |
209 | macaroon_raw=macaroon_raw) |
210 | + request_id = auth_params["request-id"] |
211 | self.assertFault( |
212 | - faults.GitRepositoryNotFound(path.strip("/")), |
213 | - self.git_api.translatePath, path, permission, auth_params) |
214 | + faults.GitRepositoryNotFound(path.strip("/")), request_id, |
215 | + "translatePath", path, permission, auth_params) |
216 | |
217 | def assertPermissionDenied(self, requester, path, |
218 | message="Permission denied.", |
219 | @@ -170,9 +198,10 @@ class TestGitAPIMixin: |
220 | auth_params = _make_auth_params( |
221 | requester, can_authenticate=can_authenticate, |
222 | macaroon_raw=macaroon_raw) |
223 | + request_id = auth_params["request-id"] |
224 | self.assertFault( |
225 | - faults.PermissionDenied(message), |
226 | - self.git_api.translatePath, path, permission, auth_params) |
227 | + faults.PermissionDenied(message), request_id, |
228 | + "translatePath", path, permission, auth_params) |
229 | |
230 | def assertUnauthorized(self, requester, path, |
231 | message="Authorisation required.", |
232 | @@ -182,18 +211,20 @@ class TestGitAPIMixin: |
233 | auth_params = _make_auth_params( |
234 | requester, can_authenticate=can_authenticate, |
235 | macaroon_raw=macaroon_raw) |
236 | + request_id = auth_params["request-id"] |
237 | self.assertFault( |
238 | - faults.Unauthorized(message), |
239 | - self.git_api.translatePath, path, permission, auth_params) |
240 | + faults.Unauthorized(message), request_id, |
241 | + "translatePath", path, permission, auth_params) |
242 | |
243 | def assertNotFound(self, requester, path, message, permission="read", |
244 | can_authenticate=False): |
245 | """Assert that looking at the given path returns NotFound.""" |
246 | auth_params = _make_auth_params( |
247 | requester, can_authenticate=can_authenticate) |
248 | + request_id = auth_params["request-id"] |
249 | self.assertFault( |
250 | - faults.NotFound(message), |
251 | - self.git_api.translatePath, path, permission, auth_params) |
252 | + faults.NotFound(message), request_id, |
253 | + "translatePath", path, permission, auth_params) |
254 | |
255 | def assertInvalidSourcePackageName(self, requester, path, name, |
256 | permission="read", |
257 | @@ -202,27 +233,30 @@ class TestGitAPIMixin: |
258 | InvalidSourcePackageName.""" |
259 | auth_params = _make_auth_params( |
260 | requester, can_authenticate=can_authenticate) |
261 | + request_id = auth_params["request-id"] |
262 | self.assertFault( |
263 | - faults.InvalidSourcePackageName(name), |
264 | - self.git_api.translatePath, path, permission, auth_params) |
265 | + faults.InvalidSourcePackageName(name), request_id, |
266 | + "translatePath", path, permission, auth_params) |
267 | |
268 | def assertInvalidBranchName(self, requester, path, message, |
269 | permission="read", can_authenticate=False): |
270 | """Assert that looking at the given path returns InvalidBranchName.""" |
271 | auth_params = _make_auth_params( |
272 | requester, can_authenticate=can_authenticate) |
273 | + request_id = auth_params["request-id"] |
274 | self.assertFault( |
275 | - faults.InvalidBranchName(Exception(message)), |
276 | - self.git_api.translatePath, path, permission, auth_params) |
277 | + faults.InvalidBranchName(Exception(message)), request_id, |
278 | + "translatePath", path, permission, auth_params) |
279 | |
280 | def assertOopsOccurred(self, requester, path, |
281 | permission="read", can_authenticate=False): |
282 | """Assert that looking at the given path OOPSes.""" |
283 | auth_params = _make_auth_params( |
284 | requester, can_authenticate=can_authenticate) |
285 | + request_id = auth_params["request-id"] |
286 | fault = self.assertFault( |
287 | - faults.OopsOccurred, |
288 | - self.git_api.translatePath, path, permission, auth_params) |
289 | + faults.OopsOccurred, request_id, |
290 | + "translatePath", path, permission, auth_params) |
291 | prefix = ( |
292 | "An unexpected error has occurred while creating a Git " |
293 | "repository. Please report a Launchpad bug and quote: ") |
294 | @@ -235,7 +269,9 @@ class TestGitAPIMixin: |
295 | auth_params = _make_auth_params( |
296 | requester, can_authenticate=can_authenticate, |
297 | macaroon_raw=macaroon_raw) |
298 | - translation = self.git_api.translatePath(path, permission, auth_params) |
299 | + request_id = auth_params["request-id"] |
300 | + translation = self.assertDoesNotFault( |
301 | + request_id, "translatePath", path, permission, auth_params) |
302 | login(ANONYMOUS) |
303 | self.assertEqual( |
304 | {"path": removeSecurityProxy(repository).getInternalPath(), |
305 | @@ -246,7 +282,9 @@ class TestGitAPIMixin: |
306 | private=False): |
307 | auth_params = _make_auth_params( |
308 | requester, can_authenticate=can_authenticate) |
309 | - translation = self.git_api.translatePath(path, "write", auth_params) |
310 | + request_id = auth_params["request-id"] |
311 | + translation = self.assertDoesNotFault( |
312 | + request_id, "translatePath", path, "write", auth_params) |
313 | login(ANONYMOUS) |
314 | repository = getUtility(IGitRepositorySet).getByPath( |
315 | requester, path.lstrip("/")) |
316 | @@ -272,10 +310,12 @@ class TestGitAPIMixin: |
317 | def assertHasRefPermissions(self, requester, repository, ref_paths, |
318 | permissions, macaroon_raw=None): |
319 | auth_params = _make_auth_params(requester, macaroon_raw=macaroon_raw) |
320 | + request_id = auth_params["request-id"] |
321 | translated_path = removeSecurityProxy(repository).getInternalPath() |
322 | ref_paths = [ |
323 | xmlrpc_client.Binary(ref_path) for ref_path in ref_paths] |
324 | - results = self.git_api.checkRefPermissions( |
325 | + results = self.assertDoesNotFault( |
326 | + request_id, "checkRefPermissions", |
327 | translated_path, ref_paths, auth_params) |
328 | self.assertThat(results, MatchesSetwise(*( |
329 | MatchesListwise([ |
330 | @@ -593,9 +633,8 @@ class TestGitAPIMixin: |
331 | def test_checkRefPermissions_nonexistent_repository(self): |
332 | requester = self.factory.makePerson() |
333 | self.assertFault( |
334 | - faults.GitRepositoryNotFound("nonexistent"), |
335 | - self.git_api.checkRefPermissions, |
336 | - "nonexistent", [], {"uid": requester.id}) |
337 | + faults.GitRepositoryNotFound("nonexistent"), None, |
338 | + "checkRefPermissions", "nonexistent", [], {"uid": requester.id}) |
339 | |
340 | |
341 | class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
342 | @@ -1169,7 +1208,8 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
343 | def test_notify(self): |
344 | # The notify call creates a GitRefScanJob. |
345 | repository = self.factory.makeGitRepository() |
346 | - self.assertIsNone(self.git_api.notify(repository.getInternalPath())) |
347 | + self.assertIsNone(self.assertDoesNotFault( |
348 | + None, "notify", repository.getInternalPath())) |
349 | job_source = getUtility(IGitRefScanJobSource) |
350 | [job] = list(job_source.iterReady()) |
351 | self.assertEqual(repository, job.repository) |
352 | @@ -1177,7 +1217,7 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
353 | def test_notify_missing_repository(self): |
354 | # A notify call on a non-existent repository returns a fault and |
355 | # does not create a job. |
356 | - self.assertFault(faults.NotFound, self.git_api.notify, "10000") |
357 | + self.assertFault(faults.NotFound, None, "notify", "10000") |
358 | job_source = getUtility(IGitRefScanJobSource) |
359 | self.assertEqual([], list(job_source.iterReady())) |
360 | |
361 | @@ -1187,15 +1227,15 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
362 | repository = self.factory.makeGitRepository( |
363 | information_type=InformationType.PRIVATESECURITY) |
364 | path = repository.getInternalPath() |
365 | - self.assertIsNone(self.git_api.notify(path)) |
366 | + self.assertIsNone(self.assertDoesNotFault(None, "notify", path)) |
367 | job_source = getUtility(IGitRefScanJobSource) |
368 | [job] = list(job_source.iterReady()) |
369 | self.assertEqual(repository, job.repository) |
370 | |
371 | def test_authenticateWithPassword(self): |
372 | self.assertFault( |
373 | - faults.Unauthorized, |
374 | - self.git_api.authenticateWithPassword, "foo", "bar") |
375 | + faults.Unauthorized, None, |
376 | + "authenticateWithPassword", "foo", "bar") |
377 | |
378 | def test_authenticateWithPassword_code_import(self): |
379 | self.pushConfig( |
380 | @@ -1208,15 +1248,15 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
381 | macaroon = removeSecurityProxy(issuer).issueMacaroon(job) |
382 | self.assertEqual( |
383 | {"macaroon": macaroon.serialize(), "user": "+launchpad-services"}, |
384 | - self.git_api.authenticateWithPassword("", macaroon.serialize())) |
385 | + self.assertDoesNotFault( |
386 | + None, "authenticateWithPassword", "", macaroon.serialize())) |
387 | other_macaroon = Macaroon(identifier="another", key="another-secret") |
388 | self.assertFault( |
389 | - faults.Unauthorized, |
390 | - self.git_api.authenticateWithPassword, |
391 | - "", other_macaroon.serialize()) |
392 | + faults.Unauthorized, None, |
393 | + "authenticateWithPassword", "", other_macaroon.serialize()) |
394 | self.assertFault( |
395 | - faults.Unauthorized, |
396 | - self.git_api.authenticateWithPassword, "", "nonsense") |
397 | + faults.Unauthorized, None, |
398 | + "authenticateWithPassword", "", "nonsense") |
399 | |
400 | def test_authenticateWithPassword_private_snap_build(self): |
401 | self.useFixture(FeatureFixture(SNAP_TESTING_FLAGS)) |
402 | @@ -1231,15 +1271,15 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
403 | macaroon = removeSecurityProxy(issuer).issueMacaroon(build) |
404 | self.assertEqual( |
405 | {"macaroon": macaroon.serialize(), "user": "+launchpad-services"}, |
406 | - self.git_api.authenticateWithPassword("", macaroon.serialize())) |
407 | + self.assertDoesNotFault( |
408 | + None, "authenticateWithPassword", "", macaroon.serialize())) |
409 | other_macaroon = Macaroon(identifier="another", key="another-secret") |
410 | self.assertFault( |
411 | - faults.Unauthorized, |
412 | - self.git_api.authenticateWithPassword, |
413 | - "", other_macaroon.serialize()) |
414 | + faults.Unauthorized, None, |
415 | + "authenticateWithPassword", "", other_macaroon.serialize()) |
416 | self.assertFault( |
417 | - faults.Unauthorized, |
418 | - self.git_api.authenticateWithPassword, "", "nonsense") |
419 | + faults.Unauthorized, None, |
420 | + "authenticateWithPassword", "", "nonsense") |
421 | |
422 | def test_authenticateWithPassword_user_macaroon(self): |
423 | # A user with a suitable macaroon can authenticate using it, in |
424 | @@ -1252,23 +1292,23 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
425 | self.factory.makeGitRepository(owner=requester), user=requester) |
426 | self.assertEqual( |
427 | {"macaroon": macaroon.serialize(), "uid": requester.id}, |
428 | - self.git_api.authenticateWithPassword( |
429 | + self.assertDoesNotFault( |
430 | + None, "authenticateWithPassword", |
431 | requester.name, macaroon.serialize())) |
432 | self.assertFault( |
433 | - faults.Unauthorized, |
434 | - self.git_api.authenticateWithPassword, "", macaroon.serialize()) |
435 | + faults.Unauthorized, None, |
436 | + "authenticateWithPassword", "", macaroon.serialize()) |
437 | self.assertFault( |
438 | - faults.Unauthorized, |
439 | - self.git_api.authenticateWithPassword, |
440 | - "nonexistent", macaroon.serialize()) |
441 | + faults.Unauthorized, None, |
442 | + "authenticateWithPassword", "nonexistent", macaroon.serialize()) |
443 | other_macaroon = Macaroon(identifier="another", key="another-secret") |
444 | self.assertFault( |
445 | - faults.Unauthorized, |
446 | - self.git_api.authenticateWithPassword, |
447 | + faults.Unauthorized, None, |
448 | + "authenticateWithPassword", |
449 | requester.name, other_macaroon.serialize()) |
450 | self.assertFault( |
451 | - faults.Unauthorized, |
452 | - self.git_api.authenticateWithPassword, requester.name, "nonsense") |
453 | + faults.Unauthorized, None, |
454 | + "authenticateWithPassword", requester.name, "nonsense") |
455 | |
456 | def test_authenticateWithPassword_user_mismatch(self): |
457 | # authenticateWithPassword refuses macaroons in the case where the |
458 | @@ -1300,7 +1340,8 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
459 | expected_auth_params["uid"] = requester.id |
460 | self.assertEqual( |
461 | expected_auth_params, |
462 | - self.git_api.authenticateWithPassword( |
463 | + self.assertDoesNotFault( |
464 | + None, "authenticateWithPassword", |
465 | name, macaroon.serialize())) |
466 | for requester in unauthorized: |
467 | login(ANONYMOUS) |
468 | @@ -1308,9 +1349,8 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
469 | requester if requester == LAUNCHPAD_SERVICES |
470 | else requester.name) |
471 | self.assertFault( |
472 | - faults.Unauthorized, |
473 | - self.git_api.authenticateWithPassword, |
474 | - name, macaroon.serialize()) |
475 | + faults.Unauthorized, None, |
476 | + "authenticateWithPassword", name, macaroon.serialize()) |
477 | |
478 | def test_checkRefPermissions_code_import(self): |
479 | # A code import worker with a suitable macaroon has repository owner |