Merge ~ines-almeida/launchpad:project-tokens/refactor-access-token-tests into launchpad:master
- Git
- lp:~ines-almeida/launchpad
- project-tokens/refactor-access-token-tests
- Merge into master
Proposed by
Ines Almeida
Status: | Merged |
---|---|
Approved by: | Ines Almeida |
Approved revision: | c9bebdc76044193499a10633181840a8e3159619 |
Merge reported by: | Otto Co-Pilot |
Merged at revision: | not available |
Proposed branch: | ~ines-almeida/launchpad:project-tokens/refactor-access-token-tests |
Merge into: | launchpad:master |
Diff against target: |
435 lines (+97/-63) 3 files modified
lib/lp/code/xmlrpc/tests/test_git.py (+14/-25) lib/lp/services/auth/tests/test_model.py (+57/-24) lib/lp/services/webapp/tests/test_servers.py (+26/-14) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson (community) | Approve | ||
Review via email: mp+451469@code.launchpad.net |
Commit message
Refactor auth model and webapp auth tests
Preparatory refactoring to make it cleaner to add tests for new access token targets
Description of the change
To post a comment you must log in.
- c9bebdc... by Ines Almeida
-
Minor refactoring of xmlrpc.
tests.test_ git
Revision history for this message
Colin Watson (cjwatson) : | # |
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/tests/test_git.py b/lib/lp/code/xmlrpc/tests/test_git.py |
2 | index 0467947..15e4f09 100644 |
3 | --- a/lib/lp/code/xmlrpc/tests/test_git.py |
4 | +++ b/lib/lp/code/xmlrpc/tests/test_git.py |
5 | @@ -1161,6 +1161,15 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
6 | |
7 | layer = LaunchpadFunctionalLayer |
8 | |
9 | + def _makeGitRepositoryWithRefs(self, **kwargs): |
10 | + """Helper method to create a git repository with a default branch""" |
11 | + repository = self.factory.makeGitRepository(**kwargs) |
12 | + self.factory.makeGitRefs( |
13 | + repository=repository, paths=["refs/heads/main"] |
14 | + ) |
15 | + removeSecurityProxy(repository).default_branch = "refs/heads/main" |
16 | + return repository |
17 | + |
18 | def test_confirm_git_repository_creation(self): |
19 | owner = self.factory.makePerson() |
20 | repo = removeSecurityProxy(self.factory.makeGitRepository(owner=owner)) |
21 | @@ -2821,11 +2830,7 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
22 | # pushed by a user that has their ordinary privileges on the |
23 | # corresponding repository. |
24 | requester_owner = self.factory.makePerson() |
25 | - repository = self.factory.makeGitRepository(owner=requester_owner) |
26 | - self.factory.makeGitRefs( |
27 | - repository=repository, paths=["refs/heads/master"] |
28 | - ) |
29 | - removeSecurityProxy(repository).default_branch = "refs/heads/master" |
30 | + repository = self._makeGitRepositoryWithRefs(owner=requester_owner) |
31 | pushed_branch = "branch1" |
32 | self.assertHasMergeProposalURL( |
33 | repository, pushed_branch, {"uid": requester_owner.id} |
34 | @@ -2857,12 +2862,8 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
35 | |
36 | self.pushConfig("codehosting", git_macaroon_secret_key="some-secret") |
37 | requester = self.factory.makePerson() |
38 | - repository = self.factory.makeGitRepository(owner=requester) |
39 | + repository = self._makeGitRepositoryWithRefs(owner=requester) |
40 | issuer = getUtility(IMacaroonIssuer, "git-repository") |
41 | - self.factory.makeGitRefs( |
42 | - repository=repository, paths=["refs/heads/master"] |
43 | - ) |
44 | - removeSecurityProxy(repository).default_branch = "refs/heads/master" |
45 | |
46 | pushed_branch = "branch1" |
47 | with person_logged_in(requester): |
48 | @@ -2890,11 +2891,7 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
49 | ) |
50 | requesters = [self.factory.makePerson() for _ in range(2)] |
51 | owner = self.factory.makeTeam(members=requesters) |
52 | - repository = self.factory.makeGitRepository(owner=owner) |
53 | - self.factory.makeGitRefs( |
54 | - repository=repository, paths=["refs/heads/master"] |
55 | - ) |
56 | - removeSecurityProxy(repository).default_branch = "refs/heads/master" |
57 | + repository = self._makeGitRepositoryWithRefs(owner=owner) |
58 | pushed_branch = "branch1" |
59 | macaroon = issuer.issueMacaroon(repository) |
60 | |
61 | @@ -2935,11 +2932,7 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
62 | # pushed by a user with a suitable access token that has their |
63 | # ordinary privileges on the corresponding repository. |
64 | requester = self.factory.makePerson() |
65 | - repository = self.factory.makeGitRepository(owner=requester) |
66 | - self.factory.makeGitRefs( |
67 | - repository=repository, paths=["refs/heads/main"] |
68 | - ) |
69 | - removeSecurityProxy(repository).default_branch = "refs/heads/main" |
70 | + repository = self._makeGitRepositoryWithRefs(owner=requester) |
71 | _, token = self.factory.makeAccessToken( |
72 | owner=requester, |
73 | target=repository, |
74 | @@ -2954,11 +2947,7 @@ class TestGitAPI(TestGitAPIMixin, TestCaseWithFactory): |
75 | # getMergeProposalURL refuses access tokens for a different |
76 | # repository. |
77 | requester = self.factory.makePerson() |
78 | - repository = self.factory.makeGitRepository(owner=requester) |
79 | - self.factory.makeGitRefs( |
80 | - repository=repository, paths=["refs/heads/main"] |
81 | - ) |
82 | - removeSecurityProxy(repository).default_branch = "refs/heads/main" |
83 | + repository = self._makeGitRepositoryWithRefs(owner=requester) |
84 | _, token = self.factory.makeAccessToken( |
85 | owner=requester, scopes=[AccessTokenScope.REPOSITORY_PUSH] |
86 | ) |
87 | diff --git a/lib/lp/services/auth/tests/test_model.py b/lib/lp/services/auth/tests/test_model.py |
88 | index b33ccf7..ca33eec 100644 |
89 | --- a/lib/lp/services/auth/tests/test_model.py |
90 | +++ b/lib/lp/services/auth/tests/test_model.py |
91 | @@ -46,31 +46,36 @@ from lp.testing.matchers import HasQueryCount |
92 | from lp.testing.pages import webservice_for_person |
93 | |
94 | |
95 | -class TestAccessToken(TestCaseWithFactory): |
96 | +class TestAccessTokenBase: |
97 | layer = DatabaseFunctionalLayer |
98 | |
99 | def test_owner_can_edit(self): |
100 | owner = self.factory.makePerson() |
101 | - _, token = self.factory.makeAccessToken(owner=owner) |
102 | + _, token = self.factory.makeAccessToken( |
103 | + owner=owner, target=self.makeTarget() |
104 | + ) |
105 | login_person(owner) |
106 | self.assertTrue(check_permission("launchpad.Edit", token)) |
107 | |
108 | def test_target_owner_can_edit(self): |
109 | target_owner = self.factory.makePerson() |
110 | - repository = self.factory.makeGitRepository(owner=target_owner) |
111 | - _, token = self.factory.makeAccessToken(target=repository) |
112 | + _, token = self.factory.makeAccessToken( |
113 | + target=self.makeTarget(target_owner) |
114 | + ) |
115 | login_person(target_owner) |
116 | self.assertTrue(check_permission("launchpad.Edit", token)) |
117 | |
118 | def test_other_user_cannot_edit(self): |
119 | - _, token = self.factory.makeAccessToken() |
120 | + _, token = self.factory.makeAccessToken(target=self.makeTarget()) |
121 | login_person(self.factory.makePerson()) |
122 | self.assertFalse(check_permission("launchpad.Edit", token)) |
123 | |
124 | def test_updateLastUsed_never_used(self): |
125 | # If the token has never been used, we update its last-used date. |
126 | owner = self.factory.makePerson() |
127 | - _, token = self.factory.makeAccessToken(owner=owner) |
128 | + _, token = self.factory.makeAccessToken( |
129 | + owner=owner, target=self.makeTarget() |
130 | + ) |
131 | login_person(owner) |
132 | self.assertIsNone(token.date_last_used) |
133 | transaction.commit() |
134 | @@ -82,7 +87,9 @@ class TestAccessToken(TestCaseWithFactory): |
135 | # If the token's last-used date was updated recently, we leave it |
136 | # alone. |
137 | owner = self.factory.makePerson() |
138 | - _, token = self.factory.makeAccessToken(owner=owner) |
139 | + _, token = self.factory.makeAccessToken( |
140 | + owner=owner, target=self.makeTarget() |
141 | + ) |
142 | login_person(owner) |
143 | recent = datetime.now(timezone.utc) - timedelta(minutes=1) |
144 | removeSecurityProxy(token).date_last_used = recent |
145 | @@ -94,7 +101,9 @@ class TestAccessToken(TestCaseWithFactory): |
146 | # If the token's last-used date is outside our update resolution, we |
147 | # update it. |
148 | owner = self.factory.makePerson() |
149 | - _, token = self.factory.makeAccessToken(owner=owner) |
150 | + _, token = self.factory.makeAccessToken( |
151 | + owner=owner, target=self.makeTarget() |
152 | + ) |
153 | login_person(owner) |
154 | recent = datetime.now(timezone.utc) - timedelta(hours=1) |
155 | removeSecurityProxy(token).date_last_used = recent |
156 | @@ -107,7 +116,9 @@ class TestAccessToken(TestCaseWithFactory): |
157 | # If the token is locked by another transaction, we leave it alone. |
158 | owner = self.factory.makePerson() |
159 | owner_email = removeSecurityProxy(owner.preferredemail).email |
160 | - secret, token = self.factory.makeAccessToken(owner=owner) |
161 | + secret, token = self.factory.makeAccessToken( |
162 | + owner=owner, target=self.makeTarget() |
163 | + ) |
164 | login_person(owner) |
165 | self.assertIsNone(token.date_last_used) |
166 | transaction.commit() |
167 | @@ -150,7 +161,9 @@ class TestAccessToken(TestCaseWithFactory): |
168 | def test_is_expired(self): |
169 | owner = self.factory.makePerson() |
170 | login_person(owner) |
171 | - _, current_token = self.factory.makeAccessToken(owner=owner) |
172 | + _, current_token = self.factory.makeAccessToken( |
173 | + owner=owner, target=self.makeTarget() |
174 | + ) |
175 | _, expired_token = self.factory.makeAccessToken( |
176 | owner=owner, |
177 | date_expires=datetime.now(timezone.utc) - timedelta(minutes=1), |
178 | @@ -161,7 +174,9 @@ class TestAccessToken(TestCaseWithFactory): |
179 | def test_revoke(self): |
180 | owner = self.factory.makePerson() |
181 | _, token = self.factory.makeAccessToken( |
182 | - owner=owner, scopes=[AccessTokenScope.REPOSITORY_BUILD_STATUS] |
183 | + owner=owner, |
184 | + scopes=[AccessTokenScope.REPOSITORY_BUILD_STATUS], |
185 | + target=self.makeTarget(), |
186 | ) |
187 | login_person(owner) |
188 | self.assertThat( |
189 | @@ -177,7 +192,12 @@ class TestAccessToken(TestCaseWithFactory): |
190 | ) |
191 | |
192 | |
193 | -class TestAccessTokenSet(TestCaseWithFactory): |
194 | +class TestAccessTokenGitRepository(TestAccessTokenBase, TestCaseWithFactory): |
195 | + def makeTarget(self, owner=None): |
196 | + return self.factory.makeGitRepository(owner=owner) |
197 | + |
198 | + |
199 | +class TestAccessTokenSetBase: |
200 | layer = DatabaseFunctionalLayer |
201 | |
202 | def test_new(self): |
203 | @@ -185,7 +205,7 @@ class TestAccessTokenSet(TestCaseWithFactory): |
204 | self.assertEqual(64, len(secret)) |
205 | owner = self.factory.makePerson() |
206 | description = "Test token" |
207 | - target = self.factory.makeGitRepository() |
208 | + target = self.makeTarget() |
209 | scopes = [AccessTokenScope.REPOSITORY_BUILD_STATUS] |
210 | _, token = self.factory.makeAccessToken( |
211 | secret=secret, |
212 | @@ -206,13 +226,13 @@ class TestAccessTokenSet(TestCaseWithFactory): |
213 | ) |
214 | |
215 | def test_getByID(self): |
216 | - secret, token = self.factory.makeAccessToken() |
217 | + secret, token = self.factory.makeAccessToken(target=self.makeTarget()) |
218 | token_id = removeSecurityProxy(token).id |
219 | self.assertEqual(token, getUtility(IAccessTokenSet).getByID(token_id)) |
220 | self.assertIsNone(getUtility(IAccessTokenSet).getByID(token_id + 1)) |
221 | |
222 | def test_getBySecret(self): |
223 | - secret, token = self.factory.makeAccessToken() |
224 | + secret, token = self.factory.makeAccessToken(target=self.makeTarget()) |
225 | self.assertEqual( |
226 | token, getUtility(IAccessTokenSet).getBySecret(secret) |
227 | ) |
228 | @@ -225,9 +245,15 @@ class TestAccessTokenSet(TestCaseWithFactory): |
229 | def test_findByOwner(self): |
230 | owners = [self.factory.makePerson() for _ in range(3)] |
231 | tokens = [ |
232 | - self.factory.makeAccessToken(owner=owners[0])[1], |
233 | - self.factory.makeAccessToken(owner=owners[0])[1], |
234 | - self.factory.makeAccessToken(owner=owners[1])[1], |
235 | + self.factory.makeAccessToken( |
236 | + owner=owners[0], target=self.makeTarget() |
237 | + )[1], |
238 | + self.factory.makeAccessToken( |
239 | + owner=owners[0], target=self.makeTarget() |
240 | + )[1], |
241 | + self.factory.makeAccessToken( |
242 | + owner=owners[1], target=self.makeTarget() |
243 | + )[1], |
244 | ] |
245 | self.assertContentEqual( |
246 | tokens[:2], getUtility(IAccessTokenSet).findByOwner(owners[0]) |
247 | @@ -240,7 +266,7 @@ class TestAccessTokenSet(TestCaseWithFactory): |
248 | ) |
249 | |
250 | def test_findByTarget(self): |
251 | - targets = [self.factory.makeGitRepository() for _ in range(3)] |
252 | + targets = [self.makeTarget() for _ in range(3)] |
253 | tokens = [ |
254 | self.factory.makeAccessToken(target=targets[0])[1], |
255 | self.factory.makeAccessToken(target=targets[0])[1], |
256 | @@ -257,7 +283,7 @@ class TestAccessTokenSet(TestCaseWithFactory): |
257 | ) |
258 | |
259 | def test_findByTarget_visible_by_user(self): |
260 | - targets = [self.factory.makeGitRepository() for _ in range(3)] |
261 | + targets = [self.makeTarget() for _ in range(3)] |
262 | owners = [self.factory.makePerson() for _ in range(3)] |
263 | tokens = [ |
264 | self.factory.makeAccessToken( |
265 | @@ -290,7 +316,7 @@ class TestAccessTokenSet(TestCaseWithFactory): |
266 | ) |
267 | |
268 | def test_findByTarget_excludes_expired(self): |
269 | - target = self.factory.makeGitRepository() |
270 | + target = self.makeTarget() |
271 | _, current_token = self.factory.makeAccessToken(target=target) |
272 | _, expires_soon_token = self.factory.makeAccessToken( |
273 | target=target, |
274 | @@ -312,7 +338,7 @@ class TestAccessTokenSet(TestCaseWithFactory): |
275 | ) |
276 | |
277 | def test_getByTargetAndID(self): |
278 | - targets = [self.factory.makeGitRepository() for _ in range(3)] |
279 | + targets = [self.makeTarget() for _ in range(3)] |
280 | tokens = [ |
281 | self.factory.makeAccessToken(target=targets[0])[1], |
282 | self.factory.makeAccessToken(target=targets[0])[1], |
283 | @@ -337,7 +363,7 @@ class TestAccessTokenSet(TestCaseWithFactory): |
284 | ) |
285 | |
286 | def test_getByTargetAndID_visible_by_user(self): |
287 | - targets = [self.factory.makeGitRepository() for _ in range(3)] |
288 | + targets = [self.makeTarget() for _ in range(3)] |
289 | owners = [self.factory.makePerson() for _ in range(3)] |
290 | tokens = [ |
291 | self.factory.makeAccessToken( |
292 | @@ -374,7 +400,7 @@ class TestAccessTokenSet(TestCaseWithFactory): |
293 | self.assertIsNone(fetched_token) |
294 | |
295 | def test_getByTargetAndID_excludes_expired(self): |
296 | - target = self.factory.makeGitRepository() |
297 | + target = self.makeTarget() |
298 | _, current_token = self.factory.makeAccessToken(target=target) |
299 | _, expires_soon_token = self.factory.makeAccessToken( |
300 | target=target, |
301 | @@ -403,6 +429,13 @@ class TestAccessTokenSet(TestCaseWithFactory): |
302 | ) |
303 | |
304 | |
305 | +class TestGitRepositoryAccessTokenSet( |
306 | + TestAccessTokenSetBase, TestCaseWithFactory |
307 | +): |
308 | + def makeTarget(self): |
309 | + return self.factory.makeGitRepository() |
310 | + |
311 | + |
312 | class TestAccessTokenTargetBase: |
313 | layer = DatabaseFunctionalLayer |
314 | |
315 | diff --git a/lib/lp/services/webapp/tests/test_servers.py b/lib/lp/services/webapp/tests/test_servers.py |
316 | index f865502..e777ac2 100644 |
317 | --- a/lib/lp/services/webapp/tests/test_servers.py |
318 | +++ b/lib/lp/services/webapp/tests/test_servers.py |
319 | @@ -778,7 +778,7 @@ class LoggingTransaction: |
320 | self.log.append("ABORT") |
321 | |
322 | |
323 | -class TestWebServiceAccessTokens(TestCaseWithFactory): |
324 | +class TestWebServiceAccessTokensBase: |
325 | """Test personal access tokens for the webservice. |
326 | |
327 | These are bearer tokens with an owner, a context, and some scopes. We |
328 | @@ -791,7 +791,9 @@ class TestWebServiceAccessTokens(TestCaseWithFactory): |
329 | def test_valid(self): |
330 | owner = self.factory.makePerson() |
331 | secret, token = self.factory.makeAccessToken( |
332 | - owner=owner, scopes=[AccessTokenScope.REPOSITORY_BUILD_STATUS] |
333 | + owner=owner, |
334 | + target=self.makeTarget(owner=owner), |
335 | + scopes=[AccessTokenScope.REPOSITORY_BUILD_STATUS], |
336 | ) |
337 | self.assertIsNone(removeSecurityProxy(token).date_last_used) |
338 | transaction.commit() |
339 | @@ -828,6 +830,7 @@ class TestWebServiceAccessTokens(TestCaseWithFactory): |
340 | owner = self.factory.makePerson() |
341 | secret, token = self.factory.makeAccessToken( |
342 | owner=owner, |
343 | + target=self.makeTarget(owner=owner), |
344 | date_expires=datetime.now(timezone.utc) - timedelta(days=1), |
345 | ) |
346 | transaction.commit() |
347 | @@ -859,7 +862,9 @@ class TestWebServiceAccessTokens(TestCaseWithFactory): |
348 | |
349 | def test_inactive_account(self): |
350 | owner = self.factory.makePerson(account_status=AccountStatus.SUSPENDED) |
351 | - secret, token = self.factory.makeAccessToken(owner=owner) |
352 | + secret, token = self.factory.makeAccessToken( |
353 | + owner=owner, target=self.makeTarget(owner=owner) |
354 | + ) |
355 | transaction.commit() |
356 | |
357 | request, publication = get_request_and_publication( |
358 | @@ -889,13 +894,13 @@ class TestWebServiceAccessTokens(TestCaseWithFactory): |
359 | request.setPrincipal(principal) |
360 | |
361 | def test_checkRequest_valid(self): |
362 | - repository = self.factory.makeGitRepository() |
363 | + target = self.makeTarget() |
364 | self._makeAccessTokenVerifiedRequest( |
365 | - target=repository, |
366 | + target=target, |
367 | scopes=[AccessTokenScope.REPOSITORY_BUILD_STATUS], |
368 | ) |
369 | getUtility(IWebServiceConfiguration).checkRequest( |
370 | - repository, ["repository:build_status", "repository:another_scope"] |
371 | + target, ["repository:build_status", "repository:another_scope"] |
372 | ) |
373 | |
374 | def test_checkRequest_contains_context(self): |
375 | @@ -909,9 +914,9 @@ class TestWebServiceAccessTokens(TestCaseWithFactory): |
376 | ) |
377 | |
378 | def test_checkRequest_bad_context(self): |
379 | - repository = self.factory.makeGitRepository() |
380 | + target = self.makeTarget() |
381 | self._makeAccessTokenVerifiedRequest( |
382 | - target=repository, |
383 | + target=target, |
384 | scopes=[AccessTokenScope.REPOSITORY_BUILD_STATUS], |
385 | ) |
386 | self.assertRaisesWithContent( |
387 | @@ -923,23 +928,23 @@ class TestWebServiceAccessTokens(TestCaseWithFactory): |
388 | ) |
389 | |
390 | def test_checkRequest_unscoped_method(self): |
391 | - repository = self.factory.makeGitRepository() |
392 | + target = self.makeTarget() |
393 | self._makeAccessTokenVerifiedRequest( |
394 | - target=repository, |
395 | + target=target, |
396 | scopes=[AccessTokenScope.REPOSITORY_BUILD_STATUS], |
397 | ) |
398 | self.assertRaisesWithContent( |
399 | Unauthorized, |
400 | "Current authentication only allows calling scoped methods.", |
401 | getUtility(IWebServiceConfiguration).checkRequest, |
402 | - repository, |
403 | + target, |
404 | None, |
405 | ) |
406 | |
407 | def test_checkRequest_wrong_scope(self): |
408 | - repository = self.factory.makeGitRepository() |
409 | + target = self.makeTarget() |
410 | self._makeAccessTokenVerifiedRequest( |
411 | - target=repository, |
412 | + target=target, |
413 | scopes=[ |
414 | AccessTokenScope.REPOSITORY_BUILD_STATUS, |
415 | AccessTokenScope.REPOSITORY_PUSH, |
416 | @@ -951,11 +956,18 @@ class TestWebServiceAccessTokens(TestCaseWithFactory): |
417 | "(one of these scopes is required: " |
418 | "'repository:scope_1', 'repository:scope_2').", |
419 | getUtility(IWebServiceConfiguration).checkRequest, |
420 | - repository, |
421 | + target, |
422 | ["repository:scope_1", "repository:scope_2"], |
423 | ) |
424 | |
425 | |
426 | +class TestWebServiceAccessTokensGitRepository( |
427 | + TestWebServiceAccessTokensBase, TestCaseWithFactory |
428 | +): |
429 | + def makeTarget(self, owner=None): |
430 | + return self.factory.makeGitRepository(owner=owner) |
431 | + |
432 | + |
433 | def test_suite(): |
434 | suite = unittest.TestSuite() |
435 | suite.addTest( |