Merge ~ines-almeida/launchpad:project-tokens/refactor-access-token-tests into launchpad: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)
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

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
1diff --git a/lib/lp/code/xmlrpc/tests/test_git.py b/lib/lp/code/xmlrpc/tests/test_git.py
2index 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 )
87diff --git a/lib/lp/services/auth/tests/test_model.py b/lib/lp/services/auth/tests/test_model.py
88index 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
315diff --git a/lib/lp/services/webapp/tests/test_servers.py b/lib/lp/services/webapp/tests/test_servers.py
316index 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(

Subscribers

People subscribed via source and target branches

to status/vote changes: