Merge lp:~salgado/offspring/make-piston-handlers-privacy-aware into lp:offspring

Proposed by Guilherme Salgado
Status: Merged
Approved by: Kevin McDermott
Approved revision: 110
Merged at revision: 105
Proposed branch: lp:~salgado/offspring/make-piston-handlers-privacy-aware
Merge into: lp:offspring
Prerequisite: lp:~salgado/offspring/builds-view
Diff against target: 726 lines (+467/-57)
9 files modified
config/offspring.cfg (+4/-4)
lib/offspring/web/queuemanager/handlers.py (+35/-27)
lib/offspring/web/queuemanager/models.py (+4/-2)
lib/offspring/web/queuemanager/tests/factory.py (+3/-2)
lib/offspring/web/queuemanager/tests/helpers.py (+13/-0)
lib/offspring/web/queuemanager/tests/test_handlers.py (+404/-0)
lib/offspring/web/queuemanager/tests/test_views.py (+4/-16)
lib/offspring/web/settings.py (+0/-3)
lib/offspring/web/settings_production.py (+0/-3)
To merge this branch: bzr merge lp:~salgado/offspring/make-piston-handlers-privacy-aware
Reviewer Review Type Date Requested Status
Kevin McDermott Approve
Review via email: mp+84293@code.launchpad.net

Description of the change

Update the piston handlers to include private objects the user is allowed to see.

To post a comment you must log in.
105. By Guilherme Salgado

Plenty of docstrings for tests

Revision history for this message
Kevin McDermott (bigkevmcd) wrote :

Looks good with some small fixes...thanks for adding so many tests!

=== modified file 'lib/offspring/web/queuemanager/handlers.py'

#1

+ project = projects.get(pk=projectName)
+ build = BuildResult.all_objects.accessible_by_user(
+ request.user).get(project=project, name=buildName)
         except:
             return rc.NOT_FOUND

Please change this "bare except" to list the exceptions expected...there are couple of cases of this...

#2

+# TODO: Must be made privacy-aware.

Isn't that what this branch does, or is there something missing, that's not captured in the comment?

#3

+ p = Project.all_objects.accessible_by_user(request.user).get(
+ pk=projectName)

s/p/project/

#4

+from .test_handlers import *

"Relative imports for intra-package imports are highly discouraged."

In any case, merge from trunk, and you won't need to add the import, as nose will find your tests without the import (or run your tests twice if you add it ;-)

=== added file 'lib/offspring/web/queuemanager/tests/helpers.py'

#5

+from django.contrib.auth.models import (
+ Group,
+ Permission,
+ )

Will all fit on one line...

#6

+def grant_permission_to_user(user, permission):

No docstring...

#7

+def make_build(is_private):

No docstring...but, it'd be nicer if this was at the top of the file, so that you know what it does when you're reading the tests that use it?

The docstring should be fixed, but moving it in the file is a personal preference...

#8

+ """
+ Check that the builds of private projects are not visible to members
+ of the project's access group.
+ """
+ # If the user has no rights to see a given project, they'll get an
+ # empty list of builds, just like if the project didn't exist.

Wouldn't the comment be better as part of the docstring?

#9

+ def assert_build_is_visible_to_user(self, build, user):

I appreciate that this might appear to go against my PEP8 requirement, but...

"New modules and packages (including third party frameworks) should be written to these standards, but where an existing library has a different style, internal consistency is preferred."

So, this would look nicer if it was assertBuildIsVisibleToUser

Which would make it clearer that it was intended to be an extension to TestCase's assertions :-)

There are a few cases like this...

#10

assert_create_return_code

+ def assert_create_return_code(self, project, user, return_code):

How about:

+ def assertBuildHandlerCreation(self, project, user, return_code):

?

There are a couple of trivial conflicts with trunk...

review: Needs Fixing
Revision history for this message
Kevin McDermott (bigkevmcd) wrote :

#11

- return RC.NOT_FOUND
+ return rc.NOT_FOUND

There is no test for this change...

Revision history for this message
Guilherme Salgado (salgado) wrote :
Download full text (3.4 KiB)

On Wed, 2011-12-14 at 14:12 +0000, Kevin McDermott wrote:
> Review: Needs Fixing
>
> Looks good with some small fixes...thanks for adding so many tests!
>
> === modified file 'lib/offspring/web/queuemanager/handlers.py'
>
> #1
>
> + project = projects.get(pk=projectName)
> + build = BuildResult.all_objects.accessible_by_user(
> + request.user).get(project=project, name=buildName)
> except:
> return rc.NOT_FOUND
>
> Please change this "bare except" to list the exceptions expected...there are couple of cases of this...

Ok, fixed all of them.

> #2
>
> +# TODO: Must be made privacy-aware.
>
> Isn't that what this branch does, or is there something missing, that's not captured in the comment?

This one is a bit more involved and this branch was already big enough,
so I did it in a separate one.

> #3
>
> + p = Project.all_objects.accessible_by_user(request.user).get(
> + pk=projectName)
>
> s/p/project/

Fixed

>
> #4
>
> +from .test_handlers import *
>
> "Relative imports for intra-package imports are highly discouraged."
>
> In any case, merge from trunk, and you won't need to add the import, as nose will find your tests without the import (or run your tests twice if you add it ;-)

Oh, cool, got rid of that.

>
>
> === added file 'lib/offspring/web/queuemanager/tests/helpers.py'
>
> #5
>
> +from django.contrib.auth.models import (
> + Group,
> + Permission,
> + )
>
> Will all fit on one line...
>
> #6
>
> +def grant_permission_to_user(user, permission):
>
> No docstring...

Fixed both

>
> #7
>
> +def make_build(is_private):
>
> No docstring...but, it'd be nicer if this was at the top of the file,
> so that you know what it does when you're reading the tests that use
> it?

I prefer it at the bottom as I think one should not have to read the
implementation of helper methods to understand what a test does, and I
think in this case it's pretty clear what it does by its name alone.

>
> The docstring should be fixed, but moving it in the file is a personal
> preference...

Ok

>
> #8
>
> + """
> + Check that the builds of private projects are not visible to members
> + of the project's access group.
> + """
> + # If the user has no rights to see a given project, they'll get an
> + # empty list of builds, just like if the project didn't exist.
>
> Wouldn't the comment be better as part of the docstring?

Maybe; moved it there.

>
> #9
>
> + def assert_build_is_visible_to_user(self, build, user):
>
> I appreciate that this might appear to go against my PEP8 requirement, but...
>
> "New modules and packages (including third party frameworks) should be written to these standards, but where an existing library has a different style, internal consistency is preferred."
>
> So, this would look nicer if it was assertBuildIsVisibleToUser
>
> Which would make it clearer that it was intended to be an extension to TestCase's assertions :-)
>
> There are a few cases like this...

Makes sense; fixed them all

>
> #10
>
> assert_create_return_code
>
> + def assert_create_ret...

Read more...

Revision history for this message
Guilherme Salgado (salgado) wrote :

> #11
>
> - return RC.NOT_FOUND
> + return rc.NOT_FOUND
>
> There is no test for this change...

Indeed, but when I tried to write one I realized it's impossible to exercise this piece of code as the code in the try block is only filtering the results so it won't raise an exception if something is not found. I've removed the try/except around the code as the real protection against not found build requests is further down and that is tested.

106. By Guilherme Salgado

A few changes suggested by Kevin

107. By Guilherme Salgado

merge trunk

108. By Guilherme Salgado

A few more changes suggested by Kevin

109. By Guilherme Salgado

Remove base_dir from config/offspring.cfg or else the tree root will not be used

110. By Guilherme Salgado

Fix one last thing Keving mentioned

Revision history for this message
Kevin McDermott (bigkevmcd) wrote :

Thanks for the fixes.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'config/offspring.cfg'
2--- config/offspring.cfg 2011-11-30 14:15:52 +0000
3+++ config/offspring.cfg 2011-12-14 16:08:38 +0000
4@@ -7,7 +7,8 @@
5 #===============================================================================
6 # Offspring Global Defaults
7 #
8-# base_dir: Base directory of offspring install
9+# base_dir: Base directory of offspring install. If ommitted, use the root of
10+# the Offspring tree.
11 # bin_dir: Directory to offspring binaries
12 # config_dir: Directory to offspring configuration files
13 # admin_email: E-mail admin notifications should be sent to by default
14@@ -21,7 +22,6 @@
15 #===============================================================================
16
17 [DEFAULT]
18-base_dir: /srv
19 bin_dir: %(base_dir)s/bin
20 config_dir: %(base_dir)s/config
21 log_dir: %(base_dir)s/logs
22@@ -159,8 +159,8 @@
23
24 [publisher]
25 db: postgres://offspring:temp1234@localhost/offspring
26-development_pool: /srv/builds/
27-production_pool: /srv/partners/
28+development_pool: %(base_dir)s/builds/
29+production_pool: %(base_dir)s/partners/
30 signer_directory:
31 signer_files:
32 signer_results:
33
34=== modified file 'lib/offspring/web/queuemanager/handlers.py'
35--- lib/offspring/web/queuemanager/handlers.py 2011-08-13 07:24:06 +0000
36+++ lib/offspring/web/queuemanager/handlers.py 2011-12-14 16:08:38 +0000
37@@ -43,10 +43,12 @@
38 allowed_methods = ('GET')
39
40 def read(self, request, projectName, buildName):
41+ projects = Project.all_objects.accessible_by_user(request.user)
42 try:
43- project = Project.objects.get(pk=projectName)
44- build = BuildResult.objects.get(project=project, name=buildName)
45- except:
46+ project = projects.get(pk=projectName)
47+ build = BuildResult.all_objects.accessible_by_user(
48+ request.user).get(project=project, name=buildName)
49+ except (BuildResult.DoesNotExist, Project.DoesNotExist):
50 return rc.NOT_FOUND
51 manifestPath = glob.glob(build.result_directory + '/images/*/*.manifest')[0]
52 return [ {'package' : package, 'version' : version} for package, version in BuildManifest(manifestPath).packages.iteritems() ]
53@@ -56,15 +58,18 @@
54 allowed_methods = ('GET')
55
56 def read(self, request, projectName, buildName):
57+ projects = Project.all_objects.accessible_by_user(request.user)
58+ builds = BuildResult.all_objects.accessible_by_user(request.user)
59 try:
60- project = Project.objects.get(pk=projectName)
61- base_build = BuildResult.objects.get(project=project, name=buildName)
62- except:
63+ project = projects.get(pk=projectName)
64+ base_build = builds.get(project=project, name=buildName)
65+ except (BuildResult.DoesNotExist, Project.DoesNotExist):
66 return rc.NOT_FOUND
67 if request.GET.has_key('build'):
68 try:
69- target_build = BuildResult.objects.get(project=project, name=request.GET['build'])
70- except:
71+ target_build = builds.get(
72+ project=project, name=request.GET['build'])
73+ except BuildResult.DoesNotExist:
74 return rc.NOT_FOUND
75 else:
76 return rc.BAD_REQUEST
77@@ -81,6 +86,7 @@
78 return output
79
80
81+# TODO: Must be made privacy-aware.
82 class ProjectNotificationSubscriptionHandler(BaseHandler):
83 allowed_methods = ('GET', 'POST', 'DELETE')
84 model = ProjectNotificationSubscription
85@@ -138,7 +144,7 @@
86 'reason', 'result', 'notes', 'is_released')
87
88 def read(self, request, projectName=None, buildName=None, builderName=None):
89- results = BuildResult.objects.all()
90+ results = BuildResult.all_objects.accessible_by_user(request.user)
91 if projectName is not None:
92 results = results.filter(project__name=projectName)
93 if buildName is not None:
94@@ -155,11 +161,11 @@
95
96 def read(self, request, projectName=None, request_id=None):
97 try:
98+ requests = BuildRequest.all_objects.accessible_by_user(
99+ request.user)
100 if projectName is not None:
101- requests = BuildRequest.objects.filter(project__name=projectName)
102- else:
103- requests = BuildRequest.objects
104- except:
105+ requests = requests.filter(project__name=projectName)
106+ except BuildRequest.DoesNotExist:
107 return rc.NOT_FOUND
108 if request_id is not None:
109 return requests.get(pk=request_id)
110@@ -169,18 +175,15 @@
111 @method_decorator(login_required)
112 def delete(self, request, projectName=None, request_id=None):
113 if request.user.has_perm("queuemanager.can_build"):
114- try:
115- if projectName is not None:
116- requests = BuildRequest.objects.filter(project__name=projectName)
117- else:
118- requests = BuildRequest.objects
119- except:
120- return RC.NOT_FOUND
121+ requests = BuildRequest.all_objects.accessible_by_user(
122+ request.user)
123+ if projectName is not None:
124+ requests = requests.filter(project__name=projectName)
125 if request_id is not None:
126 try:
127 requests.get(pk=request_id).delete()
128 return rc.DELETED
129- except:
130+ except BuildRequest.DoesNotExist:
131 return rc.NOT_FOUND
132 else:
133 try:
134@@ -195,14 +198,18 @@
135 def create(self, request, projectName):
136 if request.user.has_perm("queuemanager.can_build"):
137 try:
138- p = Project.objects.get(pk=projectName)
139+ project = Project.all_objects.accessible_by_user(
140+ request.user).get(pk=projectName)
141 if request.POST.has_key('build_reason'):
142- request = BuildRequest.queue_build(p, request.user, reason=request.POST['build_reason'], scoreBonus=25)
143+ reason = request.POST['build_reason']
144+ request = BuildRequest.queue_build(
145+ project, request.user, reason=reason, scoreBonus=25)
146 else:
147- request = BuildRequest.queue_build(p, request.user, scoreBonus=25)
148+ request = BuildRequest.queue_build(
149+ project, request.user, scoreBonus=25)
150 request.save()
151 return rc.CREATED
152- except:
153+ except (Project.DoesNotExist, BuildRequest.DoesNotExist):
154 return rc.BAD_REQUEST
155 else:
156 return rc.FORBIDDEN
157@@ -217,8 +224,9 @@
158 'created_at', 'published_at', 'status', 'checklist_url', 'notes')
159
160 def read(self, request, projectName):
161+ objects = Release.all_objects.accessible_by_user(request.user)
162 try:
163- releases = Release.objects.filter(build__project__name=projectName)
164- except:
165+ releases = objects.filter(build__project__name=projectName)
166+ except Release.DoesNotExist:
167 return rc.NOT_FOUND
168 return releases
169
170=== modified file 'lib/offspring/web/queuemanager/models.py'
171--- lib/offspring/web/queuemanager/models.py 2011-12-13 08:53:34 +0000
172+++ lib/offspring/web/queuemanager/models.py 2011-12-14 16:08:38 +0000
173@@ -13,7 +13,6 @@
174 )
175 import math
176
177-from django.conf import settings
178 from django.contrib.auth.models import AnonymousUser, User
179 from django.db import (
180 connection,
181@@ -24,9 +23,12 @@
182 AccessGroup,
183 AccessGroupMixin,
184 AccessManager)
185+from offspring.config import get_configuration
186 from offspring.enums import ProjectBuildStates
187
188
189+config = get_configuration()
190+
191 ARCH_CHOICES = (
192 (u'i386', u'i386'),
193 (u'amd64', u'amd64'),
194@@ -422,7 +424,7 @@
195 def result_directory(self):
196 if self.name:
197 try:
198- base_dir = getattr(settings, 'BUILDRESULTS_DIRECTORY', '/srv/builds/')
199+ base_dir = config.get('builder', 'result_dir')
200 buildDate, buildId = self.name.rsplit('-', 1)
201 except:
202 return None
203
204=== modified file 'lib/offspring/web/queuemanager/tests/factory.py'
205--- lib/offspring/web/queuemanager/tests/factory.py 2011-12-02 13:55:56 +0000
206+++ lib/offspring/web/queuemanager/tests/factory.py 2011-12-14 16:08:38 +0000
207@@ -73,7 +73,8 @@
208 title = self.get_unique_string()
209 return ProjectGroup.objects.create(name=name, title=title)
210
211- def make_build_result(self, project=None, name=None, result=None):
212+ def make_build_result(self, project=None, name=None, result=None,
213+ builder=None):
214 """
215 Create a BuildResult with the given project or a newly created one if
216 none is given.
217@@ -83,7 +84,7 @@
218 if project is None:
219 project = self.make_project()
220 return BuildResult.objects.create(
221- project=project, name=name, result=result)
222+ project=project, name=name, result=result, builder=builder)
223
224 def make_release(self, build=None, name=None, creator=None):
225 """
226
227=== added file 'lib/offspring/web/queuemanager/tests/helpers.py'
228--- lib/offspring/web/queuemanager/tests/helpers.py 1970-01-01 00:00:00 +0000
229+++ lib/offspring/web/queuemanager/tests/helpers.py 2011-12-14 16:08:38 +0000
230@@ -0,0 +1,13 @@
231+from django.contrib.auth.models import Group, Permission
232+
233+
234+def grant_permission_to_user(user, permission_codename):
235+ """Grant the permission with the given codename to the given user."""
236+ group = Group(name=user.username)
237+ group.save()
238+ group.permissions.add(
239+ Permission.objects.get(codename=permission_codename))
240+ group.save()
241+ user.groups.add(group)
242+ user.save()
243+ return user
244
245=== added file 'lib/offspring/web/queuemanager/tests/test_handlers.py'
246--- lib/offspring/web/queuemanager/tests/test_handlers.py 1970-01-01 00:00:00 +0000
247+++ lib/offspring/web/queuemanager/tests/test_handlers.py 2011-12-14 16:08:38 +0000
248@@ -0,0 +1,404 @@
249+import errno
250+import os
251+
252+from django.contrib.auth import authenticate, login
253+from django.http import HttpRequest, HttpResponse
254+from django.test import TestCase
255+from django.contrib.sessions.middleware import SessionMiddleware
256+
257+from piston.utils import rc
258+
259+from offspring.web.queuemanager.models import BuildRequest
260+from offspring.web.queuemanager.handlers import (
261+ BuildManifestComparisonHandler,
262+ BuildManifestHandler,
263+ BuildRequestHandler,
264+ BuildResultHandler,
265+ ReleaseHandler,
266+ )
267+from offspring.web.queuemanager.tests.factory import factory
268+from offspring.web.queuemanager.tests.helpers import grant_permission_to_user
269+
270+
271+class BaseHandlerTestCase(TestCase):
272+ """
273+ A TestCase with some custom setup and helpers for testing Piston handlers.
274+ """
275+
276+ def setUp(self):
277+ super(BaseHandlerTestCase, self).setUp()
278+ self.request = HttpRequest()
279+ self.request.method = 'GET'
280+ # Set request.user and process the request manually using
281+ # SessionMiddleware so that we can use login(request, user) in tests.
282+ self.request.user = None
283+ SessionMiddleware().process_request(self.request)
284+
285+ def _login(self, user):
286+ user = authenticate(username=user.username, password=user.username)
287+ login(self.request, user)
288+ return user
289+
290+
291+class BuildResultHandlerTests(BaseHandlerTestCase):
292+
293+ def test_builds_for_inexistent_project(self):
294+ """
295+ Check that no builds are returned if there's no project with the given
296+ name.
297+ """
298+ results = BuildResultHandler().read(
299+ self.request, projectName='not-existent')
300+ self.assertEqual(0, results.count())
301+
302+ def test_inexistent_build(self):
303+ """
304+ Check that no builds are returned if there's no build with the given
305+ name.
306+ """
307+ results = BuildResultHandler().read(
308+ self.request, buildName='not-existent')
309+ self.assertEqual(0, results.count())
310+
311+ def test_public_project_builds(self):
312+ """
313+ Check that the builds of public projects are visible to anyone.
314+ """
315+ build = make_build(is_private=False)
316+ self.assertBuildIsVisibleToUser(build, factory.make_user())
317+
318+ def test_private_project_build_is_visible_to_project_owner(self):
319+ """
320+ Check that the builds of private projects are visible to the project's
321+ owner.
322+ """
323+ build = make_build(is_private=True)
324+ self.assertBuildIsVisibleToUser(build, build.project.owner)
325+
326+ def test_private_project_builds_invisible_to_user(self):
327+ """
328+ Check that the builds of private projects are not visible to members
329+ of the project's access group.
330+
331+ If the user has no rights to see a given project, they'll get an
332+ empty list of builds, just like if the project didn't exist.
333+ """
334+ build = make_build(is_private=True)
335+ self.assertBuildIsNotVisibleToUser(build, factory.make_user())
336+
337+ def assertBuildIsVisibleToUser(self, build, user):
338+ """
339+ Assert that the given build is visible to the given user.
340+ """
341+ user = self._login(user)
342+ handler = BuildResultHandler()
343+
344+ # It is visible when looking at all project builds...
345+ results = handler.read(self.request, projectName=build.project.name)
346+ self.assertEqual(1, results.count())
347+ self.assertEqual([build], list(results))
348+
349+ # when looking at one specific build ...
350+ results = handler.read(self.request, buildName=build.name)
351+ self.assertEqual(1, results.count())
352+ self.assertEqual([build], list(results))
353+
354+ # and when looking at all builds of a builder.
355+ results = handler.read(self.request, builderName=build.builder.name)
356+ self.assertEqual(1, results.count())
357+ self.assertEqual([build], list(results))
358+
359+ def assertBuildIsNotVisibleToUser(self, build, user):
360+ """
361+ Assert that the given build is not visible to the given user.
362+ """
363+ user = self._login(user)
364+ handler = BuildResultHandler()
365+
366+ # It is *not* visible when looking at all project builds...
367+ results = handler.read(self.request, projectName=build.project.name)
368+ self.assertEqual(0, results.count())
369+
370+ # when looking at one specific build ...
371+ results = handler.read(self.request, buildName=build.name)
372+ self.assertEqual(0, results.count())
373+
374+ # and when looking at all builds of a builder.
375+ results = handler.read(self.request, builderName=build.builder.name)
376+ self.assertEqual(0, results.count())
377+
378+
379+class BuildRequestHandlerTests(BaseHandlerTestCase):
380+
381+ def test_public_build_request_is_visible_to_anyone(self):
382+ """
383+ Check that build requests of public projects are visible to anyone.
384+ """
385+ job = factory.make_build_request(
386+ project=factory.make_project(is_private=False))
387+ results = BuildRequestHandler().read(
388+ self.request, projectName=job.project.name, request_id=job.id)
389+ self.assertEqual(job, results)
390+
391+ def test_private_build_request_is_visible_to_owner(self):
392+ """
393+ Check that build requests of private projects are visible to the
394+ project's owner.
395+ """
396+ job = factory.make_build_request(
397+ project=factory.make_project(is_private=True))
398+ self._login(job.project.owner)
399+ results = BuildRequestHandler().read(
400+ self.request, projectName=job.project.name, request_id=job.id)
401+ self.assertEqual(job, results)
402+
403+ def test_private_build_request_is_not_visible_to_others(self):
404+ """
405+ Check that build requests of private projects are not visible if the
406+ user doesn't have the rights to see the project.
407+ """
408+ job = factory.make_build_request(
409+ project=factory.make_project(is_private=True))
410+ self._login(factory.make_user())
411+ self.assertRaises(
412+ BuildRequest.DoesNotExist,
413+ BuildRequestHandler().read,
414+ self.request, projectName=job.project.name, request_id=job.id)
415+
416+ def test_new_build_request_on_public_project(self):
417+ """
418+ Check that anybody can request a build of a public project.
419+ """
420+ project = factory.make_project(is_private=False)
421+ self.assertBuildHandlerCreation(
422+ project, factory.make_user(), rc.CREATED.status_code)
423+
424+ def test_owner_requests_new_build_on_private_project(self):
425+ """
426+ Check that the project owner can request the build of a private
427+ project.
428+ """
429+ project = factory.make_project(is_private=True)
430+ self.assertBuildHandlerCreation(
431+ project, project.owner, rc.CREATED.status_code)
432+
433+ def test_other_user_requests_new_build_on_private_project(self):
434+ """
435+ Check that users who can't see a project cannot request a build
436+ of it.
437+ """
438+ project = factory.make_project(is_private=True)
439+ self.assertBuildHandlerCreation(
440+ project, factory.make_user(), rc.BAD_REQUEST.status_code)
441+
442+ def test_delete_build_request_on_public_project(self):
443+ """
444+ Check that anybody can cancel a build request of a public project.
445+ """
446+ job = factory.make_build_request(
447+ project=factory.make_project(is_private=False))
448+ self.assertBuildHandlerDeletion(
449+ job, factory.make_user(), rc.DELETED.status_code)
450+
451+ def test_owner_deletes_build_request_on_private_project(self):
452+ """
453+ Check that the project owner can cancel a build request of a private
454+ project.
455+ """
456+ job = factory.make_build_request(
457+ project=factory.make_project(is_private=True))
458+ self.assertBuildHandlerDeletion(
459+ job, job.project.owner, rc.DELETED.status_code)
460+
461+ def test_other_user_deletes_build_request_on_private_project(self):
462+ """
463+ Check that users who can't see a project cannot cancel a build
464+ request of it.
465+ """
466+ job = factory.make_build_request(
467+ project=factory.make_project(is_private=True))
468+ self.assertBuildHandlerDeletion(
469+ job, factory.make_user(), rc.NOT_FOUND.status_code)
470+
471+ def assertBuildHandlerCreation(self, project, user, return_code):
472+ """
473+ Assert that BuildRequestHandler.create() returns the given code.
474+
475+ Also grant the given user the 'can_build' permission and logs it in.
476+ """
477+ grant_permission_to_user(user, 'can_build')
478+ self._login(user)
479+ result = BuildRequestHandler().create(
480+ self.request, projectName=project.name)
481+ self.assertEqual(return_code, result.status_code)
482+
483+ def assertBuildHandlerDeletion(self, job, user, return_code):
484+ """
485+ Assert that BuildRequestHandler.delete() returns the given code.
486+
487+ Also grant the given user the 'can_build' permission and logs it in.
488+ """
489+ grant_permission_to_user(user, 'can_build')
490+ self._login(user)
491+ result = BuildRequestHandler().delete(
492+ self.request, projectName=job.project.name, request_id=job.id)
493+ self.assertEqual(return_code, result.status_code)
494+
495+
496+class ReleaseHandlerTests(BaseHandlerTestCase):
497+
498+ def test_public_project_release(self):
499+ """
500+ Check that releases of public projects are visible to anyone.
501+ """
502+ release = factory.make_release(make_build(is_private=False))
503+ results = ReleaseHandler().read(
504+ self.request, projectName=release.build.project.name)
505+ self.assertEqual(1, results.count())
506+ self.assertEqual([release], list(results))
507+
508+ def test_private_project_release_is_visible_to_owner(self):
509+ """
510+ Check that releases of private projects are visible to the project's
511+ owner.
512+ """
513+ release = factory.make_release(make_build(is_private=True))
514+ self._login(release.build.project.owner)
515+ results = ReleaseHandler().read(
516+ self.request, projectName=release.build.project.name)
517+ self.assertEqual(1, results.count())
518+ self.assertEqual([release], list(results))
519+
520+ def test_private_project_release_is_not_visible_to_others(self):
521+ """
522+ Check that releases of private projects are not visible to users who
523+ don't have the rights to see the project.
524+ """
525+ release = factory.make_release(make_build(is_private=True))
526+ user = self._login(factory.make_user())
527+ self.assertFalse(release.is_visible_to(user))
528+ results = ReleaseHandler().read(
529+ self.request, projectName=release.build.project.name)
530+ self.assertEqual(0, results.count())
531+
532+
533+class BuildManifestHandlerTests(BaseHandlerTestCase):
534+
535+ def test_public_project_build_manifest(self):
536+ """
537+ Check that build manifests of public projects are visible to anyone.
538+ """
539+ build = make_build(is_private=False)
540+ make_build_manifest_file(build)
541+ results = BuildManifestHandler().read(
542+ self.request, build.project.name, build.name)
543+ self.assertEqual([{'package': 'foo', 'version': '1.2'}], results)
544+
545+ def test_private_project_build_manifest_is_visible_to_owner(self):
546+ """
547+ Check that build manifests of private projects are visible to the
548+ project's owner.
549+ """
550+ build = make_build(is_private=True)
551+ make_build_manifest_file(build)
552+ self._login(build.project.owner)
553+ results = BuildManifestHandler().read(
554+ self.request, build.project.name, build.name)
555+ self.assertEqual([{'package': 'foo', 'version': '1.2'}], results)
556+
557+ def test_private_project_build_manifest_returns_404_for_others(self):
558+ """
559+ Check that build manifests of private projects are not visible to
560+ users who don't have the rights to see the project.
561+ """
562+ build = make_build(is_private=True)
563+ make_build_manifest_file(build)
564+ user = self._login(factory.make_user())
565+ self.assertFalse(build.project.is_visible_to(user))
566+ results = BuildManifestHandler().read(
567+ self.request, build.project.name, build.name)
568+ self.assertTrue(isinstance(results, HttpResponse))
569+ self.assertEqual(404, results.status_code)
570+
571+
572+class BuildManifestComparisonHandlerTests(BaseHandlerTestCase):
573+
574+ def test_public_projects_build_manifest_comparison(self):
575+ """
576+ Check that anyone can get a build comparison between builds of a
577+ public project.
578+ """
579+ build = make_build(is_private=False)
580+ build2 = factory.make_build_result(
581+ project=build.project, builder=factory.make_lexbuilder())
582+ make_build_manifest_file(build)
583+ make_build_manifest_file(build2)
584+ self.request.GET['build'] = build2.name
585+ results = BuildManifestComparisonHandler().read(
586+ self.request, build.project.name, build.name)
587+ self.assertEqual(
588+ [{'changeType': 'no_change', 'target_version': '1.2',
589+ 'base_version': '1.2', 'package': 'foo'}],
590+ results)
591+
592+ def test_private_build_manifest_comparison_is_visible_to_owner(self):
593+ """
594+ Check that the project owner can get a build comparison between builds
595+ of a private project.
596+ """
597+ build = make_build(is_private=True)
598+ build2 = factory.make_build_result(
599+ project=build.project, builder=factory.make_lexbuilder())
600+ make_build_manifest_file(build)
601+ make_build_manifest_file(build2)
602+ self._login(build.project.owner)
603+ self.request.GET['build'] = build2.name
604+ results = BuildManifestComparisonHandler().read(
605+ self.request, build.project.name, build.name)
606+ self.assertEqual(
607+ [{'changeType': 'no_change', 'target_version': '1.2',
608+ 'base_version': '1.2', 'package': 'foo'}],
609+ results)
610+
611+ def test_private_build_manifest_comparison_returns_404_for_others(self):
612+ """
613+ Check that those who don't have the rights to see a private project
614+ cannot get a build comparison between builds of that project.
615+ """
616+ build = make_build(is_private=True)
617+ build2 = factory.make_build_result(
618+ project=build.project, builder=factory.make_lexbuilder())
619+ make_build_manifest_file(build)
620+ make_build_manifest_file(build2)
621+ self._login(factory.make_user())
622+ self.request.GET['build'] = build2.name
623+ results = BuildManifestComparisonHandler().read(
624+ self.request, build.project.name, build.name)
625+ self.assertTrue(isinstance(results, HttpResponse))
626+ self.assertEqual(404, results.status_code)
627+
628+
629+def make_build(is_private):
630+ """Create a new BuildResult associated to a newly created project.
631+
632+ If is_private is True, the project is marked as private, otherwise it is
633+ marked as public.
634+ """
635+ return factory.make_build_result(
636+ project=factory.make_project(is_private=is_private),
637+ builder=factory.make_lexbuilder())
638+
639+
640+def make_build_manifest_file(build):
641+ """
642+ Create a manifest file where BuildManifestHandler expects to find it.
643+ """
644+ manifest_dir = os.path.join(build.result_directory, 'images', 'build')
645+ try:
646+ os.makedirs(manifest_dir)
647+ except OSError, exc:
648+ if exc.errno != errno.EEXIST:
649+ raise
650+ path = os.path.join(manifest_dir, 'build.manifest')
651+ with open(path, 'w') as fd:
652+ fd.write('foo 1.2')
653
654=== modified file 'lib/offspring/web/queuemanager/tests/test_views.py'
655--- lib/offspring/web/queuemanager/tests/test_views.py 2011-12-14 12:13:02 +0000
656+++ lib/offspring/web/queuemanager/tests/test_views.py 2011-12-14 16:08:38 +0000
657@@ -2,8 +2,7 @@
658 # GNU Affero General Public License version 3 (see the file LICENSE).
659 import urllib
660
661-from django.contrib.auth.models import AnonymousUser, Group, Permission
662-from django.contrib.contenttypes.models import ContentType
663+from django.contrib.auth.models import AnonymousUser
664 from django.core.urlresolvers import reverse
665 from django.http import Http404
666 from django.test import TestCase
667@@ -15,6 +14,7 @@
668 from offspring.web.queuemanager.forms import LaunchpadProjectForm
669
670 from offspring.web.queuemanager.tests.factory import factory
671+from offspring.web.queuemanager.tests.helpers import grant_permission_to_user
672
673
674 class TestGetPossiblyPrivateObject(TestCase):
675@@ -417,18 +417,6 @@
676 return client.login(username=user.username, password=user.username)
677
678
679-def grant_permission_to_user(user, permission_codename):
680- """Grant the permission with the given codename to the given user."""
681- group = Group(name=user.username)
682- group.save()
683- group.permissions.add(
684- Permission.objects.get(codename=permission_codename))
685- group.save()
686- user.groups.add(group)
687- user.save()
688- return user
689-
690-
691 class LaunchpadProjectViewsTests(TestCase):
692
693 def setUp(self):
694@@ -451,8 +439,8 @@
695
696 def test_get_launchpad_project_add_url_gets_correct_form(self):
697 """
698- If the logged in user has the correct permissions, a GET request should
699- get the LaunchpadProjectForm.
700+ If the logged in user has the correct permissions, a GET request
701+ should get the LaunchpadProjectForm.
702 """
703 grant_permission_to_user(self.user, "add_launchpadproject")
704 self.client.login(username=self.user.username,
705
706=== modified file 'lib/offspring/web/settings.py'
707--- lib/offspring/web/settings.py 2011-11-18 15:45:06 +0000
708+++ lib/offspring/web/settings.py 2011-12-14 16:08:38 +0000
709@@ -131,6 +131,3 @@
710 CELERY_RESULT_BACKEND = 'djcelery.backends.database.DatabaseBackend'
711 CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'
712 djcelery.setup_loader()
713-
714-# Path to build results
715-BUILDRESULTS_DIRECTORY = '/srv/builds/'
716
717=== modified file 'lib/offspring/web/settings_production.py'
718--- lib/offspring/web/settings_production.py 2011-11-30 14:15:52 +0000
719+++ lib/offspring/web/settings_production.py 2011-12-14 16:08:38 +0000
720@@ -16,6 +16,3 @@
721 EMAIL_HOST = 'localhost'
722
723 MEDIA_URL = 'https://offspring.com/assets/'
724-
725-# Path to build results
726-BUILDRESULTS_DIRECTORY = '/srv/offspring.com/www/builds/'

Subscribers

People subscribed via source and target branches