Merge lp:~sinzui/launchpad/bug-contacts-1 into lp:launchpad
- bug-contacts-1
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | 10918 |
Proposed branch: | lp:~sinzui/launchpad/bug-contacts-1 |
Merge into: | lp:launchpad |
Diff against target: |
980 lines (+510/-335) 9 files modified
lib/lp/bugs/browser/bugrole.py (+127/-0) lib/lp/bugs/browser/bugsupervisor.py (+24/-96) lib/lp/bugs/browser/securitycontact.py (+20/-24) lib/lp/bugs/browser/tests/test_bugsupervisor.py (+167/-0) lib/lp/bugs/browser/tests/test_securitycontact.py (+154/-0) lib/lp/bugs/stories/bugs/xx-malone-security-contacts.txt (+6/-8) lib/lp/bugs/stories/initial-bug-contacts/05-set-distribution-bugcontact.txt (+8/-71) lib/lp/bugs/stories/initial-bug-contacts/10-set-upstream-bugcontact.txt (+0/-131) lib/lp/bugs/stories/initial-bug-contacts/25-file-distribution-bug.txt (+4/-5) |
To merge this branch: | bzr merge lp:~sinzui/launchpad/bug-contacts-1 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Henning Eggers (community) | code | Approve | |
Review via email: mp+25911@code.launchpad.net |
Commit message
Description of the change
This is my branch to refactor bug role view code for reuse in another view.
lp:~sinzui/launchpad/bug-contacts-1
Diff size: 797 (includes deletions of redundant tests)
Launchpad bug: https:/
Test command: ./bin/test -vv \
-t test_bugsupervisor -t test_securityco
Pre-
Target release: 10.05
Refactor bug role view code for reuse
-------
+configure-
fields. The form views for those fields need refactoring so that common
validation and change behaviours can be shared.
Rules
-----
* Extract the validate() and change actions of BugSupervisorEd
SecurityC
* Use the same validation rules--security contact has no rules.
This will fix bug 133676
QA
--
* Visit http://
* Change the bug supervisor and security contact roles to persons,
teams, and none to verify everything still works.
Lint
----
Linting changed files:
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
Test
----
I created two tests to verify the behaviours imparted from the mixin and
the view under test. The two tests are similar because of the four implicit
states of validation and permission rules, but their messages and data were
too different to make a single test suite.
* lib/lp/
* Test the validation and message behaviours.
* lib/lp/
* Test the validation and message behaviours.
* lib/lp/
* Deleted parts that tested the view, not the user experience.
Implementation
--------------
* lib/lp/
Extracted a mixin to do validation and data changes.
* lib/lp/
Used the mixin in the existing form. Removed the odd messages and
assertion that the mixin controls.
Curtis Hovey (sinzui) wrote : | # |
On Tue, 2010-05-25 at 16:34 +0000, Henning Eggers wrote:
...
> > === modified file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
> > @@ -7,11 +7,12 @@
> > __all__ = ["SecurityConta
> >
> > from lp.bugs.
> > +from lp.bugs.
>
> from lp.bugs.
>
> What do you think?
I was thinking of __init__ since it is common. I did not create a module
because it would inflate the diff. Deryck agrees with
lp.bugs.
> [...]
>
> > === added file 'lib/lp/
> > --- lib/lp/
> > +++ lib/lp/
>
> Thanks for adding this test. ;)
>
> > @@ -0,0 +1,166 @@
> > +# Copyright10 Canonical Ltd. This software is licensed under the
>
> # Copyright 2010 Canonical Ltd...
>
> > +# GNU Affero General Public License version (see the file LICENSE).
> > +
> > +"""Unit tests for bug supervisor views."""
> > +
> > +__metaclass__ = type
> > +
> > +import unittest
> > +
> > +from zope.app.
> > +
> > +from lp.bugs.
> > +from lp.testing import login, login_person, TestCaseWithFactory
> > +from lp.testing.views import create_
> > +from canonical.testing import DatabaseFunctio
> > +
> > +
> > +class TestBugSupervis
> > +
> > + layer = DatabaseFunctio
> > +
> > + def setUp(self):
> > + super(TestBugSu
> > + self.owner = self.factory.
> > + name='splat', displayname='<splat />')
>
> I understand that you use specific names because they appear in the messages
> that you test but using a "tag" displayname here seems a bit cryptic. All
> those < and > make the messages harder to read. I'd suggest using proper
> names here and use the "tags" when you need them. See my suggestion below.
I rather not since these *are* only used when they are needed. I am not
a bug domain expert, but I am certain that showing a notification for
the change, and including links in it is a strong indication something
is fundamentally wrong with these roles. It is clear that changing the
roles is dangerous and the UI did not set my expectation *before* I made
the change. The markup has historically been an XSS issue and the
testing for this is weak, Each name is tested exactly once for each
message. I think this qualifies for just enough testing.
> > + self.product = self.factory.
> > + name="boing", displayname='<boing />', owner=self.owner)
>
> Same here.
^ as above.
> > + self.team = self.factory.
> > + login_person(
> > +
> > + def _makeForm(self, person):
> > + if person is None:
> > + name = ''
> ...
Preview Diff
1 | === added file 'lib/lp/bugs/browser/bugrole.py' |
2 | --- lib/lp/bugs/browser/bugrole.py 1970-01-01 00:00:00 +0000 |
3 | +++ lib/lp/bugs/browser/bugrole.py 2010-05-25 21:21:40 +0000 |
4 | @@ -0,0 +1,127 @@ |
5 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
6 | +# GNU Affero General Public License version (see the file LICENSE). |
7 | + |
8 | +"""Common classes to support bug roles.""" |
9 | + |
10 | +__metaclass__ = type |
11 | + |
12 | +__all__ = [ |
13 | + 'BugRoleMixin', |
14 | + ] |
15 | + |
16 | +from canonical.launchpad.webapp.menu import structured |
17 | +from canonical.launchpad.webapp.publisher import canonical_url |
18 | + |
19 | + |
20 | +class BugRoleMixin: |
21 | + |
22 | + INVALID_PERSON = object() |
23 | + OTHER_USER = object() |
24 | + OTHER_TEAM = object() |
25 | + OK = object() |
26 | + |
27 | + def _getFieldState(self, field_name, data): |
28 | + """Return the enum that summarises the field state.""" |
29 | + # The field_name will not be in the data if the user did not enter |
30 | + # a person in the ValidPersonOrTeam vocabulary. |
31 | + if field_name not in data: |
32 | + return self.INVALID_PERSON |
33 | + role = data[field_name] |
34 | + user = self.user |
35 | + # The user may assign the role to None, himself, or a team he admins. |
36 | + if role is None or self.context.userCanAlterSubscription(role, user): |
37 | + return self.OK |
38 | + # The user is not an admin of the team, or he entered another user. |
39 | + if role.isTeam(): |
40 | + return self.OTHER_TEAM |
41 | + else: |
42 | + return self.OTHER_USER |
43 | + |
44 | + def validateBugSupervisor(self, data): |
45 | + """Validates the new bug supervisor. |
46 | + |
47 | + Verify that the value is None, the user, or a team he administers, |
48 | + otherwise, set a field error. |
49 | + """ |
50 | + field_state = self._getFieldState('bug_supervisor', data) |
51 | + if field_state is self.INVALID_PERSON: |
52 | + error = ( |
53 | + 'You must choose a valid person or team to be the ' |
54 | + 'bug supervisor for %s.' % self.context.displayname) |
55 | + elif field_state is self.OTHER_TEAM: |
56 | + supervisor = data['bug_supervisor'] |
57 | + team_url = canonical_url( |
58 | + supervisor, rootsite='mainsite', view_name='+members') |
59 | + error = structured( |
60 | + 'You cannot set %(team)s as the bug supervisor for ' |
61 | + '%(target)s because you are not an administrator of that ' |
62 | + 'team.<br />If you believe that %(team)s should be the ' |
63 | + 'bug supervisor for %(target)s, notify one of the ' |
64 | + '<a href="%(url)s">%(team)s administrators</a>. See ' |
65 | + '<a href="https://help.launchpad.net/BugSupervisors">' |
66 | + 'the help wiki</a> for information about setting a bug ' |
67 | + 'supervisor.', |
68 | + team=supervisor.displayname, |
69 | + target=self.context.displayname, |
70 | + url=team_url) |
71 | + elif field_state is self.OTHER_USER: |
72 | + error = structured( |
73 | + 'You cannot set another person as the bug supervisor for ' |
74 | + '%(target)s.<br />See ' |
75 | + '<a href="https://help.launchpad.net/BugSupervisors">' |
76 | + 'the help wiki</a> for information about setting a bug ' |
77 | + 'supervisor.', |
78 | + target=self.context.displayname) |
79 | + else: |
80 | + # field_state is self.OK. |
81 | + return |
82 | + self.setFieldError('bug_supervisor', error) |
83 | + |
84 | + def changeBugSupervisor(self, bug_supervisor): |
85 | + self.context.setBugSupervisor(bug_supervisor, self.user) |
86 | + if bug_supervisor is not None: |
87 | + self.request.response.addNotification(structured( |
88 | + 'Successfully changed the bug supervisor to ' |
89 | + '<a href="%(supervisor_url)s">%(displayname)s</a>.' |
90 | + '<br /><a href="%(supervisor_url)s">%(displayname)s</a> ' |
91 | + 'has also been subscribed to bug notifications for ' |
92 | + '%(targetname)s.<br />You can ' |
93 | + '<a href="%(targeturl)s/+subscribe">change the ' |
94 | + 'subscriptions</a> for %(targetname)s at any time.', |
95 | + supervisor_url=canonical_url(bug_supervisor), |
96 | + displayname=bug_supervisor.displayname, |
97 | + targetname=self.context.displayname, |
98 | + targeturl=canonical_url(self.context))) |
99 | + |
100 | + def validateSecurityContact(self, data): |
101 | + """Validates the new security contact. |
102 | + |
103 | + Verify that the value is None, the user, or a team he administers, |
104 | + otherwise, set a field error. |
105 | + """ |
106 | + field_state = self._getFieldState('security_contact', data) |
107 | + if field_state is self.INVALID_PERSON: |
108 | + error = ( |
109 | + 'You must choose a valid person or team to be the ' |
110 | + 'security contact for %s.' % self.context.displayname) |
111 | + elif field_state is self.OTHER_TEAM: |
112 | + supervisor = data['security_contact'] |
113 | + team_url = canonical_url( |
114 | + supervisor, rootsite='mainsite', view_name='+members') |
115 | + error = structured( |
116 | + 'You cannot set %(team)s as the security contact for ' |
117 | + '%(target)s because you are not an administrator of that ' |
118 | + 'team.<br />If you believe that %(team)s should be the ' |
119 | + 'security contact for %(target)s, notify one of the ' |
120 | + '<a href="%(url)s">%(team)s administrators</a>.', |
121 | + team=supervisor.displayname, |
122 | + target=self.context.displayname, |
123 | + url=team_url) |
124 | + elif field_state is self.OTHER_USER: |
125 | + error = structured( |
126 | + 'You cannot set another person as the security contact for ' |
127 | + '%(target)s.', target=self.context.displayname) |
128 | + else: |
129 | + # field_state is self.OK. |
130 | + return |
131 | + self.setFieldError('security_contact', error) |
132 | |
133 | === modified file 'lib/lp/bugs/browser/bugsupervisor.py' |
134 | --- lib/lp/bugs/browser/bugsupervisor.py 2010-04-29 15:32:46 +0000 |
135 | +++ lib/lp/bugs/browser/bugsupervisor.py 2010-05-25 21:21:40 +0000 |
136 | @@ -1,19 +1,23 @@ |
137 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
138 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
139 | # GNU Affero General Public License version 3 (see the file LICENSE). |
140 | |
141 | """Browser view for bug supervisor.""" |
142 | |
143 | __metaclass__ = type |
144 | |
145 | -__all__ = ['BugSupervisorEditView'] |
146 | +__all__ = [ |
147 | + 'BugSupervisorEditView', |
148 | + ] |
149 | |
150 | -from canonical.launchpad.webapp import ( |
151 | - action, canonical_url, LaunchpadEditFormView) |
152 | -from canonical.launchpad.webapp.menu import structured |
153 | -from lazr.restful.interface import copy_field, use_template |
154 | from zope.interface import Interface |
155 | |
156 | +from lazr.restful.interface import copy_field |
157 | + |
158 | +from canonical.launchpad.webapp.launchpadform import ( |
159 | + action, LaunchpadEditFormView) |
160 | +from canonical.launchpad.webapp.publisher import canonical_url |
161 | from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor |
162 | +from lp.bugs.browser.bugrole import BugRoleMixin |
163 | |
164 | |
165 | class BugSupervisorEditSchema(Interface): |
166 | @@ -26,7 +30,7 @@ |
167 | IHasBugSupervisor['bug_supervisor'], readonly=False) |
168 | |
169 | |
170 | -class BugSupervisorEditView(LaunchpadEditFormView): |
171 | +class BugSupervisorEditView(BugRoleMixin, LaunchpadEditFormView): |
172 | """Browser view class for editing the bug supervisor.""" |
173 | |
174 | schema = BugSupervisorEditSchema |
175 | @@ -47,99 +51,23 @@ |
176 | """See `LaunchpadFormView`""" |
177 | return {BugSupervisorEditSchema: self.context} |
178 | |
179 | + @property |
180 | + def next_url(self): |
181 | + """See `LaunchpadFormView`.""" |
182 | + return canonical_url(self.context) |
183 | + |
184 | + cancel_url = next_url |
185 | + |
186 | + def validate(self, data): |
187 | + """See `LaunchpadFormView`.""" |
188 | + self.validateBugSupervisor(data) |
189 | + |
190 | @action('Change', name='change') |
191 | def change_action(self, action, data): |
192 | """Redirect to the target page with a success message.""" |
193 | - target = self.context |
194 | bug_supervisor = data['bug_supervisor'] |
195 | - target.setBugSupervisor(bug_supervisor, self.user) |
196 | - |
197 | - if bug_supervisor is not None: |
198 | - self.request.response.addNotification(structured( |
199 | - 'Successfully changed the bug supervisor to ' |
200 | - '<a href="%(supervisor_url)s">%(displayname)s</a>.' |
201 | - '<br />' |
202 | - '<a href="%(supervisor_url)s">%(displayname)s</a> ' |
203 | - 'has also been ' |
204 | - 'subscribed to bug notifications for %(targetname)s. ' |
205 | - '<br />' |
206 | - 'You can ' |
207 | - '<a href="%(targeturl)s/+subscribe">' |
208 | - 'change the subscriptions</a> for ' |
209 | - '%(targetname)s at any time.', |
210 | - supervisor_url=canonical_url(bug_supervisor), |
211 | - displayname=bug_supervisor.displayname, |
212 | - targetname=self.context.displayname, |
213 | - targeturl=canonical_url(self.context))) |
214 | - else: |
215 | + self.changeBugSupervisor(bug_supervisor) |
216 | + if bug_supervisor is None: |
217 | self.request.response.addNotification( |
218 | "Successfully cleared the bug supervisor. " |
219 | "You can set the bug supervisor again at any time.") |
220 | - |
221 | - self.request.response.redirect(canonical_url(target)) |
222 | - |
223 | - def validate(self, data): |
224 | - """Validates the new bug supervisor. |
225 | - |
226 | - The following values are valid as bug supervisors: |
227 | - * None, indicating that the bug supervisor field for the target |
228 | - should be cleared in change_action(). |
229 | - * A valid Person (email address or launchpad id). |
230 | - * A valid Team of which the current user is an administrator. |
231 | - |
232 | - If the bug supervisor entered does not meet any of the above |
233 | - criteria then the submission will fail and the user will be notified |
234 | - of the error. |
235 | - """ |
236 | - |
237 | - # `data` will not have a bug_supervisor entry in cases where the |
238 | - # bug_supervisor the user entered is valid according to the |
239 | - # ValidPersonOrTeam vocabulary |
240 | - # (i.e. is not a Person, Team or None). |
241 | - if not data.has_key('bug_supervisor'): |
242 | - self.setFieldError( |
243 | - 'bug_supervisor', |
244 | - 'You must choose a valid person or team to be the' |
245 | - ' bug supervisor for %s.' % |
246 | - self.context.displayname) |
247 | - |
248 | - return |
249 | - |
250 | - supervisor = data['bug_supervisor'] |
251 | - |
252 | - # Making a person the bug supervisor implies subscribing him |
253 | - # to all bug mail. Ensure that the current user can indeed |
254 | - # do this. |
255 | - if (supervisor is not None and |
256 | - not self.context.userCanAlterSubscription(supervisor, self.user)): |
257 | - if supervisor.isTeam(): |
258 | - error = structured( |
259 | - "You cannot set %(team)s as the bug supervisor for " |
260 | - "%(target)s because you are not an administrator of that " |
261 | - "team.<br />If you believe that %(team)s should be the " |
262 | - "bug supervisor for %(target)s, please notify one of the " |
263 | - "<a href=\"%(url)s\">%(team)s administrators</a>. See " |
264 | - "<a href=\"https://help.launchpad.net/BugSupervisors\">" |
265 | - "the help wiki</a> for information about setting a bug " |
266 | - "supervisor.", |
267 | - team=supervisor.displayname, |
268 | - target=self.context.displayname, |
269 | - url=(canonical_url(supervisor, rootsite='mainsite') + |
270 | - '/+members')) |
271 | - self.setFieldError('bug_supervisor', error) |
272 | - else: |
273 | - error = structured( |
274 | - "You cannot set another person as the bug supervisor for " |
275 | - "%(target)s.<br />See " |
276 | - "<a href=\"https://help.launchpad.net/BugSupervisors\">" |
277 | - "the help wiki</a> for information about setting a bug " |
278 | - "supervisor.", |
279 | - person=supervisor.displayname, |
280 | - target=self.context.displayname) |
281 | - self.setFieldError('bug_supervisor', error) |
282 | - |
283 | - def cancel_url(self): |
284 | - """See `LaunchpadFormView`.""" |
285 | - return canonical_url(self.context) |
286 | - |
287 | - |
288 | |
289 | === modified file 'lib/lp/bugs/browser/securitycontact.py' |
290 | --- lib/lp/bugs/browser/securitycontact.py 2010-05-18 21:24:27 +0000 |
291 | +++ lib/lp/bugs/browser/securitycontact.py 2010-05-25 21:21:40 +0000 |
292 | @@ -1,17 +1,22 @@ |
293 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
294 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
295 | # GNU Affero General Public License version 3 (see the file LICENSE). |
296 | |
297 | """Browser view classes for security contacts.""" |
298 | |
299 | __metaclass__ = type |
300 | -__all__ = ["SecurityContactEditView"] |
301 | - |
302 | + |
303 | +__all__ = [ |
304 | + "SecurityContactEditView", |
305 | + ] |
306 | + |
307 | +from canonical.launchpad.webapp.launchpadform import ( |
308 | + action, LaunchpadFormView) |
309 | +from canonical.launchpad.webapp.publisher import canonical_url |
310 | from lp.bugs.interfaces.securitycontact import IHasSecurityContact |
311 | -from canonical.launchpad.webapp import ( |
312 | - canonical_url, LaunchpadFormView, action) |
313 | - |
314 | - |
315 | -class SecurityContactEditView(LaunchpadFormView): |
316 | +from lp.bugs.browser.bugrole import BugRoleMixin |
317 | + |
318 | + |
319 | +class SecurityContactEditView(BugRoleMixin, LaunchpadFormView): |
320 | """Browser view for editing the security contact. |
321 | |
322 | self.context is assumed to implement IHasSecurityContact. |
323 | @@ -35,6 +40,10 @@ |
324 | return { |
325 | 'security_contact': self.context.security_contact} |
326 | |
327 | + def validate(self, data): |
328 | + """See `LaunchpadFormView`.""" |
329 | + self.validateSecurityContact(data) |
330 | + |
331 | @action('Change', name='change') |
332 | def change_action(self, action, data): |
333 | security_contact = data['security_contact'] |
334 | @@ -43,25 +52,12 @@ |
335 | |
336 | self.context.security_contact = security_contact |
337 | if security_contact: |
338 | - security_contact_display_value = None |
339 | - if security_contact.preferredemail: |
340 | - # The security contact was set to a new person or team. |
341 | - security_contact_display_value = ( |
342 | - security_contact.preferredemail.email) |
343 | - else: |
344 | - # The security contact doesn't have a preferred email address, |
345 | - # so it must be a team. |
346 | - assert security_contact.isTeam(), ( |
347 | - "Expected security contact with no email address " |
348 | - "to be a team.") |
349 | - security_contact_display_value = security_contact.displayname |
350 | - |
351 | self.request.response.addNotification( |
352 | - "Successfully changed the security contact to %s" % |
353 | - security_contact_display_value) |
354 | + "Successfully changed the security contact to %s." % |
355 | + security_contact.displayname) |
356 | else: |
357 | self.request.response.addNotification( |
358 | - "Successfully removed the security contact") |
359 | + "Successfully removed the security contact.") |
360 | |
361 | @property |
362 | def next_url(self): |
363 | |
364 | === added file 'lib/lp/bugs/browser/tests/test_bugsupervisor.py' |
365 | --- lib/lp/bugs/browser/tests/test_bugsupervisor.py 1970-01-01 00:00:00 +0000 |
366 | +++ lib/lp/bugs/browser/tests/test_bugsupervisor.py 2010-05-25 21:21:40 +0000 |
367 | @@ -0,0 +1,167 @@ |
368 | +# Copyright10 Canonical Ltd. This software is licensed under the |
369 | +# GNU Affero General Public License version (see the file LICENSE). |
370 | + |
371 | +"""Unit tests for bug supervisor views.""" |
372 | + |
373 | +__metaclass__ = type |
374 | + |
375 | +import unittest |
376 | + |
377 | +from zope.app.form.interfaces import ConversionError |
378 | + |
379 | +from lp.bugs.browser.bugsupervisor import BugSupervisorEditSchema |
380 | +from lp.testing import login, login_person, TestCaseWithFactory |
381 | +from lp.testing.views import create_initialized_view |
382 | +from canonical.testing import DatabaseFunctionalLayer |
383 | + |
384 | + |
385 | +class TestBugSupervisorEditView(TestCaseWithFactory): |
386 | + |
387 | + layer = DatabaseFunctionalLayer |
388 | + |
389 | + def setUp(self): |
390 | + super(TestBugSupervisorEditView, self).setUp() |
391 | + self.owner = self.factory.makePerson( |
392 | + name='splat', displayname='<splat />') |
393 | + self.product = self.factory.makeProduct( |
394 | + name="boing", displayname='<boing />', owner=self.owner) |
395 | + self.team = self.factory.makeTeam(name='thud', owner=self.owner) |
396 | + login_person(self.owner) |
397 | + |
398 | + def _makeForm(self, person): |
399 | + if person is None: |
400 | + name = '' |
401 | + else: |
402 | + name = person.name |
403 | + return { |
404 | + 'field.bug_supervisor': name, |
405 | + 'field.actions.change': 'Change', |
406 | + } |
407 | + |
408 | + def test_view_attributes(self): |
409 | + self.product.displayname = 'Boing' |
410 | + view = create_initialized_view( |
411 | + self.product, name='+bugsupervisor') |
412 | + label = 'Edit bug supervisor for Boing' |
413 | + self.assertEqual(label, view.label) |
414 | + self.assertEqual(label, view.page_title) |
415 | + fields = ['bug_supervisor'] |
416 | + self.assertEqual(fields, view.field_names) |
417 | + adapter, context = view.adapters.popitem() |
418 | + self.assertEqual(BugSupervisorEditSchema, adapter) |
419 | + self.assertEqual(self.product, context) |
420 | + self.assertEqual('http://launchpad.dev/boing', view.next_url) |
421 | + self.assertEqual('http://launchpad.dev/boing', view.cancel_url) |
422 | + |
423 | + def test_owner_appoint_self_from_none(self): |
424 | + # This also verifies that displaynames are escaped. |
425 | + form = self._makeForm(self.owner) |
426 | + view = create_initialized_view( |
427 | + self.product, name='+bugsupervisor', form=form) |
428 | + self.assertEqual([], view.errors) |
429 | + self.assertEqual(self.product.bug_supervisor, self.owner) |
430 | + notifications = view.request.response.notifications |
431 | + self.assertEqual(1, len(notifications)) |
432 | + expected = ( |
433 | + 'Successfully changed the bug supervisor to ' |
434 | + '<a href="http://launchpad.dev/~splat"><splat /></a>.' |
435 | + '<br /><a href="http://launchpad.dev/~splat"><splat /></a> ' |
436 | + 'has also been subscribed to bug notifications for ' |
437 | + '<boing />.<br />You can ' |
438 | + '<a href="http://launchpad.dev/boing/+subscribe">change ' |
439 | + 'the subscriptions</a> for <boing /> at any time.') |
440 | + self.assertEqual(expected, notifications.pop().message) |
441 | + |
442 | + def test_owner_appoint_self_from_another(self): |
443 | + self.product.setBugSupervisor(self.team, self.owner) |
444 | + form = self._makeForm(self.owner) |
445 | + view = create_initialized_view( |
446 | + self.product, name='+bugsupervisor', form=form) |
447 | + self.assertEqual([], view.errors) |
448 | + self.assertEqual(self.owner, self.product.bug_supervisor) |
449 | + |
450 | + def test_owner_appoint_none(self): |
451 | + self.product.setBugSupervisor(self.owner, self.owner) |
452 | + form = self._makeForm(None) |
453 | + view = create_initialized_view( |
454 | + self.product, name='+bugsupervisor', form=form) |
455 | + self.assertEqual([], view.errors) |
456 | + self.assertEqual(self.product.bug_supervisor, None) |
457 | + notifications = view.request.response.notifications |
458 | + self.assertEqual(1, len(notifications)) |
459 | + expected = ( |
460 | + 'Successfully cleared the bug supervisor. ' |
461 | + 'You can set the bug supervisor again at any time.') |
462 | + self.assertEqual(expected, notifications.pop().message) |
463 | + |
464 | + def test_owner_appoint_his_team(self): |
465 | + form = self._makeForm(self.team) |
466 | + view = create_initialized_view( |
467 | + self.product, name='+bugsupervisor', form=form) |
468 | + self.assertEqual([], view.errors) |
469 | + self.assertEqual(self.team, self.product.bug_supervisor) |
470 | + |
471 | + def test_owner_cannot_appoint_another_team(self): |
472 | + team = self.factory.makeTeam(name='smack', displayname='<smack />') |
473 | + form = self._makeForm(team) |
474 | + view = create_initialized_view( |
475 | + self.product, name='+bugsupervisor', form=form) |
476 | + self.assertEqual(1, len(view.errors)) |
477 | + expected = ( |
478 | + 'You cannot set <smack /> as the bug supervisor for ' |
479 | + '<boing /> because you are not an administrator of that ' |
480 | + 'team.<br />If you believe that <smack /> should be the ' |
481 | + 'bug supervisor for <boing />, notify one of the ' |
482 | + '<a href="http://launchpad.dev/~smack/+members"><smack /> ' |
483 | + 'administrators</a>. See ' |
484 | + '<a href="https://help.launchpad.net/BugSupervisors">the ' |
485 | + 'help wiki</a> for information about setting a bug supervisor.') |
486 | + self.assertEqual(expected, view.errors.pop()) |
487 | + |
488 | + def test_owner_cannot_appoint_a_nonvalid_user(self): |
489 | + # The vocabulary only accepts valid users. |
490 | + form = self._makeForm(None) |
491 | + form['field.bug_supervisor'] = 'fnord' |
492 | + view = create_initialized_view( |
493 | + self.product, name='+bugsupervisor', form=form) |
494 | + self.assertEqual(2, len(view.errors)) |
495 | + expected = ( |
496 | + 'You must choose a valid person or team to be the bug supervisor ' |
497 | + 'for <boing />.') |
498 | + self.assertEqual(expected, view.errors.pop()) |
499 | + self.assertTrue(isinstance(view.errors.pop(), ConversionError)) |
500 | + |
501 | + def test_owner_cannot_appoint_another_user(self): |
502 | + another_user = self.factory.makePerson() |
503 | + form = self._makeForm(another_user) |
504 | + view = create_initialized_view( |
505 | + self.product, name='+bugsupervisor', form=form) |
506 | + self.assertEqual(1, len(view.errors)) |
507 | + expected = ( |
508 | + 'You cannot set another person as the bug supervisor for ' |
509 | + '<boing />.<br />See ' |
510 | + '<a href="https://help.launchpad.net/BugSupervisors">the help ' |
511 | + 'wiki</a> for information about setting a bug supervisor.') |
512 | + self.assertEqual(expected, view.errors.pop()) |
513 | + |
514 | + def test_admin_appoint_another_user(self): |
515 | + another_user = self.factory.makePerson() |
516 | + login('admin@canonical.com') |
517 | + form = self._makeForm(another_user) |
518 | + view = create_initialized_view( |
519 | + self.product, name='+bugsupervisor', form=form) |
520 | + self.assertEqual([], view.errors) |
521 | + self.assertEqual(another_user, self.product.bug_supervisor) |
522 | + |
523 | + def test_admin_appoint_another_team(self): |
524 | + another_team = self.factory.makeTeam() |
525 | + login('admin@canonical.com') |
526 | + form = self._makeForm(another_team) |
527 | + view = create_initialized_view( |
528 | + self.product, name='+bugsupervisor', form=form) |
529 | + self.assertEqual([], view.errors) |
530 | + self.assertEqual(another_team, self.product.bug_supervisor) |
531 | + |
532 | + |
533 | +def test_suite(): |
534 | + return unittest.TestLoader().loadTestsFromName(__name__) |
535 | |
536 | === added file 'lib/lp/bugs/browser/tests/test_securitycontact.py' |
537 | --- lib/lp/bugs/browser/tests/test_securitycontact.py 1970-01-01 00:00:00 +0000 |
538 | +++ lib/lp/bugs/browser/tests/test_securitycontact.py 2010-05-25 21:21:40 +0000 |
539 | @@ -0,0 +1,154 @@ |
540 | +# Copyright10 Canonical Ltd. This software is licensed under the |
541 | +# GNU Affero General Public License version (see the file LICENSE). |
542 | + |
543 | +"""Unit tests for security contact views.""" |
544 | + |
545 | +__metaclass__ = type |
546 | + |
547 | +import unittest |
548 | + |
549 | +from zope.app.form.interfaces import ConversionError |
550 | + |
551 | +from lp.testing import login, login_person, TestCaseWithFactory |
552 | +from lp.testing.views import create_initialized_view |
553 | +from canonical.testing import DatabaseFunctionalLayer |
554 | + |
555 | + |
556 | +class TestSecurityContactEditView(TestCaseWithFactory): |
557 | + |
558 | + layer = DatabaseFunctionalLayer |
559 | + |
560 | + def setUp(self): |
561 | + super(TestSecurityContactEditView, self).setUp() |
562 | + self.owner = self.factory.makePerson( |
563 | + name='splat', displayname='<splat />') |
564 | + self.product = self.factory.makeProduct( |
565 | + name="boing", displayname='<boing />', owner=self.owner) |
566 | + self.team = self.factory.makeTeam(name='thud', owner=self.owner) |
567 | + login_person(self.owner) |
568 | + |
569 | + def _makeForm(self, person): |
570 | + if person is None: |
571 | + name = '' |
572 | + else: |
573 | + name = person.name |
574 | + return { |
575 | + 'field.security_contact': name, |
576 | + 'field.actions.change': 'Change', |
577 | + } |
578 | + |
579 | + def test_view_attributes(self): |
580 | + self.product.displayname = 'Boing' |
581 | + view = create_initialized_view( |
582 | + self.product, name='+securitycontact') |
583 | + label = 'Edit Boing security contact' |
584 | + self.assertEqual(label, view.label) |
585 | + self.assertEqual(label, view.page_title) |
586 | + fields = ['security_contact'] |
587 | + self.assertEqual(fields, view.field_names) |
588 | + self.assertEqual('http://launchpad.dev/boing', view.next_url) |
589 | + self.assertEqual('http://launchpad.dev/boing', view.cancel_url) |
590 | + |
591 | + def test_owner_appoint_self_from_none(self): |
592 | + # This also verifies that displaynames are escaped. |
593 | + form = self._makeForm(self.owner) |
594 | + view = create_initialized_view( |
595 | + self.product, name='+securitycontact', form=form) |
596 | + self.assertEqual([], view.errors) |
597 | + self.assertEqual(self.product.security_contact, self.owner) |
598 | + notifications = view.request.response.notifications |
599 | + self.assertEqual(1, len(notifications)) |
600 | + expected = ( |
601 | + 'Successfully changed the security contact to <splat />.') |
602 | + self.assertEqual(expected, notifications.pop().message) |
603 | + |
604 | + def test_owner_appoint_self_from_another(self): |
605 | + self.product.security_contact = self.team |
606 | + form = self._makeForm(self.owner) |
607 | + view = create_initialized_view( |
608 | + self.product, name='+securitycontact', form=form) |
609 | + self.assertEqual([], view.errors) |
610 | + self.assertEqual(self.owner, self.product.security_contact) |
611 | + |
612 | + def test_owner_appoint_none(self): |
613 | + self.product.security_contact = self.owner |
614 | + form = self._makeForm(None) |
615 | + view = create_initialized_view( |
616 | + self.product, name='+securitycontact', form=form) |
617 | + self.assertEqual([], view.errors) |
618 | + self.assertEqual(self.product.security_contact, None) |
619 | + notifications = view.request.response.notifications |
620 | + self.assertEqual(1, len(notifications)) |
621 | + expected = ('Successfully removed the security contact.') |
622 | + self.assertEqual(expected, notifications.pop().message) |
623 | + |
624 | + def test_owner_appoint_his_team(self): |
625 | + form = self._makeForm(self.team) |
626 | + view = create_initialized_view( |
627 | + self.product, name='+securitycontact', form=form) |
628 | + self.assertEqual([], view.errors) |
629 | + self.assertEqual(self.product.security_contact, self.team) |
630 | + notifications = view.request.response.notifications |
631 | + self.assertEqual(1, len(notifications)) |
632 | + expected = ('Successfully changed the security contact to Thud.') |
633 | + self.assertEqual(expected, notifications.pop().message) |
634 | + |
635 | + def test_owner_cannot_appoint_another_team(self): |
636 | + team = self.factory.makeTeam(name='smack', displayname='<smack />') |
637 | + form = self._makeForm(team) |
638 | + view = create_initialized_view( |
639 | + self.product, name='+securitycontact', form=form) |
640 | + self.assertEqual(1, len(view.errors)) |
641 | + expected = ( |
642 | + 'You cannot set <smack /> as the security contact for ' |
643 | + '<boing /> because you are not an administrator of that ' |
644 | + 'team.<br />If you believe that <smack /> should be the ' |
645 | + 'security contact for <boing />, notify one of the ' |
646 | + '<a href="http://launchpad.dev/~smack/+members"><smack /> ' |
647 | + 'administrators</a>.') |
648 | + self.assertEqual(expected, view.errors.pop()) |
649 | + |
650 | + def test_owner_cannot_appoint_a_nonvalid_user(self): |
651 | + form = self._makeForm(None) |
652 | + form['field.security_contact'] = 'fnord' |
653 | + view = create_initialized_view( |
654 | + self.product, name='+securitycontact', form=form) |
655 | + self.assertEqual(2, len(view.errors)) |
656 | + expected = ( |
657 | + 'You must choose a valid person or team to be the ' |
658 | + 'security contact for <boing />.') |
659 | + self.assertEqual(expected, view.errors.pop()) |
660 | + self.assertTrue(isinstance(view.errors.pop(), ConversionError)) |
661 | + |
662 | + def test_owner_cannot_appoint_another_user(self): |
663 | + another_user = self.factory.makePerson() |
664 | + form = self._makeForm(another_user) |
665 | + view = create_initialized_view( |
666 | + self.product, name='+securitycontact', form=form) |
667 | + self.assertEqual(1, len(view.errors)) |
668 | + expected = ( |
669 | + 'You cannot set another person as the security contact for ' |
670 | + '<boing />.') |
671 | + self.assertEqual(expected, view.errors.pop()) |
672 | + |
673 | + def test_admin_appoint_another_user(self): |
674 | + another_user = self.factory.makePerson() |
675 | + login('admin@canonical.com') |
676 | + form = self._makeForm(another_user) |
677 | + view = create_initialized_view( |
678 | + self.product, name='+securitycontact', form=form) |
679 | + self.assertEqual([], view.errors) |
680 | + self.assertEqual(another_user, self.product.security_contact) |
681 | + |
682 | + def test_admin_appoint_another_team(self): |
683 | + another_team = self.factory.makeTeam() |
684 | + login('admin@canonical.com') |
685 | + form = self._makeForm(another_team) |
686 | + view = create_initialized_view( |
687 | + self.product, name='+securitycontact', form=form) |
688 | + self.assertEqual([], view.errors) |
689 | + self.assertEqual(another_team, self.product.security_contact) |
690 | + |
691 | + |
692 | +def test_suite(): |
693 | + return unittest.TestLoader().loadTestsFromName(__name__) |
694 | |
695 | === modified file 'lib/lp/bugs/stories/bugs/xx-malone-security-contacts.txt' |
696 | --- lib/lp/bugs/stories/bugs/xx-malone-security-contacts.txt 2009-06-12 16:36:02 +0000 |
697 | +++ lib/lp/bugs/stories/bugs/xx-malone-security-contacts.txt 2010-05-25 21:21:40 +0000 |
698 | @@ -6,17 +6,15 @@ |
699 | >>> browser.open("http://localhost:9000/ubuntu/+securitycontact") |
700 | >>> browser.getControl("Security Contact").value = "name16" |
701 | >>> browser.getControl("Change").click() |
702 | - >>> print browser.contents |
703 | - <!DOCTYPE... |
704 | - ...Successfully changed the security contact to foo.bar@canonical.com... |
705 | - ... |
706 | + >>> for message in get_feedback_messages(browser.contents): |
707 | + ... print extract_text(message) |
708 | + Successfully changed the security contact to Foo Bar. |
709 | |
710 | Or on an upstream. |
711 | |
712 | >>> browser.open("http://localhost:9000/firefox/+securitycontact") |
713 | >>> browser.getControl("Security Contact").value = "name21" |
714 | >>> browser.getControl("Change").click() |
715 | - >>> print browser.contents |
716 | - <!DOCTYPE... |
717 | - ...Successfully changed the security contact to Hoary Gnome Team... |
718 | - ... |
719 | + >>> for message in get_feedback_messages(browser.contents): |
720 | + ... print extract_text(message) |
721 | + Successfully changed the security contact to Hoary Gnome Team. |
722 | |
723 | === modified file 'lib/lp/bugs/stories/initial-bug-contacts/05-set-distribution-bugcontact.txt' |
724 | --- lib/lp/bugs/stories/initial-bug-contacts/05-set-distribution-bugcontact.txt 2009-11-12 15:33:27 +0000 |
725 | +++ lib/lp/bugs/stories/initial-bug-contacts/05-set-distribution-bugcontact.txt 2010-05-25 21:21:40 +0000 |
726 | @@ -42,74 +42,11 @@ |
727 | |
728 | And then we're redirected to the distribution page. |
729 | |
730 | - >>> colin_browser.url |
731 | - 'http://launchpad.dev/ubuntu' |
732 | - |
733 | - >>> for tag in find_tags_by_class(colin_browser.contents, |
734 | - ... "informational message"): |
735 | - ... print tag |
736 | - <div...Successfully changed the bug supervisor to ...Colin Watson... |
737 | - |
738 | -Now that the bug supervisor is set, there's a link to him on the |
739 | -distribution's bug page page. |
740 | - |
741 | - >>> browser.open('http://bugs.launchpad.dev/ubuntu') |
742 | - >>> browser.getLink(url='/~kamion').url |
743 | - 'http://launchpad.dev/~kamion' |
744 | - |
745 | -When the bug supervisor doesn't have an email address, the displayname is |
746 | -shown instead in the feedback message. Let's appoint the Ubuntu Team as |
747 | -the bug supervisor. Since Colin is an administrator of this team, he can |
748 | -do it. |
749 | - |
750 | - >>> colin_browser.open("http://launchpad.dev/ubuntu/+bugsupervisor") |
751 | - >>> colin_browser.getControl("Bug Supervisor").value = 'ubuntu-team' |
752 | - >>> colin_browser.getControl("Change").click() |
753 | - |
754 | - >>> print extract_text(colin_browser.contents) |
755 | - Ubuntu Linux in Launchpad |
756 | - ... |
757 | - Successfully changed the bug supervisor to Ubuntu Team. |
758 | - ... |
759 | - |
760 | -While anybody with edit permissions can set the bug supervisor, only |
761 | -certain persons can be successfully selected. All users can set |
762 | -themselves self and teams they administer as the bug supervisor, as |
763 | -shown above. But ordinary users cannot appoint other persons and teams |
764 | -they do not administer. |
765 | - |
766 | - >>> colin_browser.open("http://launchpad.dev/ubuntu/+bugsupervisor") |
767 | - >>> colin_browser.getControl("Bug Supervisor").value = 'admins' |
768 | - >>> colin_browser.getControl("Change").click() |
769 | - >>> print extract_text(colin_browser.contents) |
770 | - Edit bug supervisor for Ubuntu : Ubuntu |
771 | - ... |
772 | - You cannot set Launchpad Administrators as the bug supervisor for |
773 | - Ubuntu because you are not an administrator of that team. |
774 | - If you believe that Launchpad Administrators should be the bug |
775 | - supervisor for Ubuntu, please notify one of the Launchpad |
776 | - Administrators administrators. |
777 | - ... |
778 | - |
779 | -But members of the Launchpad administration team can appoint anybody |
780 | -as bug supervisors, teams... |
781 | - |
782 | - >>> admin_browser.open("http://launchpad.dev/ubuntu/+bugsupervisor") |
783 | - >>> admin_browser.getControl("Bug Supervisor").value = 'ubuntu-translators' |
784 | - >>> admin_browser.getControl("Change").click() |
785 | - >>> print extract_text(admin_browser.contents) |
786 | - Ubuntu Linux in Launchpad |
787 | - ... |
788 | - Successfully changed the bug supervisor to Ubuntu Translators. |
789 | - ... |
790 | - |
791 | -...as well as persons. |
792 | - |
793 | - >>> admin_browser.open("http://launchpad.dev/ubuntu/+bugsupervisor") |
794 | - >>> admin_browser.getControl("Bug Supervisor").value = 'carlos' |
795 | - >>> admin_browser.getControl("Change").click() |
796 | - >>> print extract_text(admin_browser.contents) |
797 | - Ubuntu Linux in Launchpad |
798 | - ... |
799 | - Successfully changed the bug supervisor to Carlos Perelló Marín. |
800 | - ... |
801 | + >>> colin_browser.url |
802 | + 'http://launchpad.dev/ubuntu' |
803 | + |
804 | + >>> for message in get_feedback_messages(colin_browser.contents): |
805 | + ... print extract_text(message) |
806 | + Successfully changed the bug supervisor to Colin Watson. |
807 | + Colin Watson has also been subscribed to bug notifications for Ubuntu. |
808 | + You can change the subscriptions for Ubuntu at any time. |
809 | |
810 | === modified file 'lib/lp/bugs/stories/initial-bug-contacts/10-set-upstream-bugcontact.txt' |
811 | --- lib/lp/bugs/stories/initial-bug-contacts/10-set-upstream-bugcontact.txt 2010-03-31 20:25:33 +0000 |
812 | +++ lib/lp/bugs/stories/initial-bug-contacts/10-set-upstream-bugcontact.txt 2010-05-25 21:21:40 +0000 |
813 | @@ -38,39 +38,6 @@ |
814 | Successfully changed the bug supervisor to Landscape Developers. |
815 | ... |
816 | |
817 | -If Sample Person tries to appoint another person, he'll get an error message. |
818 | - |
819 | - >>> sample_browser.open("http://localhost:9000/firefox/+bugsupervisor") |
820 | - >>> sample_browser.getControl(name="field.bug_supervisor").value = ( |
821 | - ... "robertc@robertcollins.net") |
822 | - >>> sample_browser.getControl("Change").click() |
823 | - >>> print sample_browser.url |
824 | - http://localhost:9000/firefox/+bugsupervisor |
825 | - >>> print extract_text(sample_browser.contents) |
826 | - Edit bug supervisor for Mozilla Firefox : Mozilla Firefox |
827 | - ... |
828 | - You cannot set another person as the bug supervisor for Mozilla Firefox. |
829 | - See the help wiki for information about setting a bug supervisor. |
830 | - The person or team responsible for bug management. |
831 | - ... |
832 | - |
833 | -If he tries to appoint a team which he does not administer, he'll get |
834 | -too an an error message. |
835 | - |
836 | - >>> sample_browser.getControl(name="field.bug_supervisor").value = ( |
837 | - ... "guadamen") |
838 | - >>> sample_browser.getControl("Change").click() |
839 | - >>> print sample_browser.url |
840 | - http://localhost:9000/firefox/+bugsupervisor |
841 | - >>> print extract_text(sample_browser.contents) |
842 | - Edit bug supervisor for Mozilla Firefox : Mozilla Firefox |
843 | - ... |
844 | - You cannot set GuadaMen as the bug supervisor for Mozilla Firefox |
845 | - because you are not an administrator of that team. |
846 | - If you believe that GuadaMen should be the bug supervisor for Mozilla |
847 | - Firefox, please notify one of the GuadaMen administrators. |
848 | - ... |
849 | - |
850 | Launchpad administrators can appoint anybody. |
851 | |
852 | >>> admin_browser = setupBrowser() |
853 | @@ -86,101 +53,3 @@ |
854 | ... |
855 | Successfully changed the bug supervisor to Robert Collins. |
856 | ... |
857 | - |
858 | - |
859 | -= Teams as Bug Supervisors = |
860 | - |
861 | -First, some setup. We create a new product owned by "Sample Person", using a |
862 | -separate browser instance to avoid breaking the other page tests. |
863 | - |
864 | - >>> sample_browser.open('http://launchpad.dev/projects/+new') |
865 | - >>> sample_browser.getControl('URL').value = 'testy' |
866 | - >>> sample_browser.getControl('Name').value = 'Test Product' |
867 | - >>> sample_browser.getControl('Title').value = ( |
868 | - ... 'A product for testing things') |
869 | - >>> sample_browser.getControl('Summary').value = ( |
870 | - ... 'Testing team bug supervisors') |
871 | - >>> sample_browser.getControl('Continue').click() |
872 | - |
873 | - # XXX Add Description: field |
874 | - >>> #sample_browser.getControl('Description').value = 'See bug 109652' |
875 | - >>> sample_browser.getControl(name='field.licenses').value = ['GNU_GPL_V2'] |
876 | - >>> sample_browser.getControl(name='field.license_info').value = 'foo' |
877 | - >>> sample_browser.getControl('Complete Registration').click() |
878 | - >>> sample_browser.url |
879 | - 'http://launchpad.dev/testy' |
880 | - |
881 | -By default, a newly created project doesn't use Launchpad for bug |
882 | -tracking, and none of the bug lists or edit controls will be |
883 | -available. |
884 | - |
885 | - >>> sample_browser.getLink('Bugs').click() |
886 | - >>> uses_malone_p = find_tag_by_id(sample_browser.contents, 'no-malone') |
887 | - >>> print extract_text(uses_malone_p) |
888 | - A product for testing things does not use Launchpad for bug tracking. |
889 | - |
890 | -We can change the settings to use Launchpad for bug tracking. |
891 | - |
892 | - >>> sample_browser.getLink( |
893 | - ... 'Configure bug tracker').click() |
894 | - >>> print sample_browser.title |
895 | - Configure bug tracker : Bugs : Test Product |
896 | - >>> sample_browser.getControl('In Launchpad').click() |
897 | - >>> sample_browser.getControl('Change').click() |
898 | - |
899 | -Now, information about bug tracking (bug supervisor, security contact) |
900 | -and their edit controls are available on the page. |
901 | - |
902 | - >>> bug_supervisor = find_tag_by_id( |
903 | - ... sample_browser.contents, 'bug-supervisor') |
904 | - >>> print extract_text(bug_supervisor) |
905 | - Bug supervisor: |
906 | - None set |
907 | - |
908 | -Sample Person is a member of "Warty Security Team". However, he should not be |
909 | -able to set Warty Security Team as a bug supervisor for Test Project as he's |
910 | -not an admin of that team. |
911 | - |
912 | - >>> sample_browser.getLink('Change bug supervisor').click() |
913 | - >>> sample_browser.url |
914 | - 'http://bugs.launchpad.dev/testy/+bugsupervisor' |
915 | - |
916 | - >>> sample_browser.getControl('Bug Supervisor').value = 'name20' |
917 | - >>> sample_browser.getControl('Change').click() |
918 | - >>> 'Successfully changed the bug supervisor' not in sample_browser.contents |
919 | - True |
920 | - |
921 | - >>> print sample_browser.contents |
922 | - <!DOCTYPE... |
923 | - ...You cannot set Warty Security Team as the bug supervisor... |
924 | - ...you are not an administrator of that team... |
925 | - |
926 | -In the warning, the user will be pointed to the Warty Security Team's |
927 | -membership page and advised to contact an administrator if she believes that |
928 | -the team should be a bug supervisor. |
929 | - |
930 | - >>> sample_browser.getLink('Warty Security Team administrators').click() |
931 | - >>> sample_browser.url |
932 | - 'http://launchpad.dev/~name20/+members' |
933 | - |
934 | -Launchpad admins can set a team as bug supervisor without any warnings, |
935 | -however, whether they're in the team or not. |
936 | - |
937 | - >>> login('test@canonical.com') |
938 | - |
939 | - >>> from zope.component import getUtility |
940 | - >>> from canonical.launchpad.interfaces import ( |
941 | - ... IPersonSet) |
942 | - >>> security_team = getUtility(IPersonSet).getByName('name20') |
943 | - >>> foo_bar = getUtility(IPersonSet).getByName('name16') |
944 | - >>> foo_bar.inTeam(security_team) |
945 | - False |
946 | - |
947 | - >>> logout() |
948 | - |
949 | - >>> admin_browser.open('http://bugs.launchpad.dev/testy/+bugsupervisor') |
950 | - >>> admin_browser.getControl('Bug Supervisor').value = 'name20' |
951 | - >>> admin_browser.getControl('Change').click() |
952 | - >>> 'Successfully changed the bug supervisor' in admin_browser.contents |
953 | - True |
954 | - |
955 | |
956 | === modified file 'lib/lp/bugs/stories/initial-bug-contacts/25-file-distribution-bug.txt' |
957 | --- lib/lp/bugs/stories/initial-bug-contacts/25-file-distribution-bug.txt 2009-11-12 12:01:22 +0000 |
958 | +++ lib/lp/bugs/stories/initial-bug-contacts/25-file-distribution-bug.txt 2010-05-25 21:21:40 +0000 |
959 | @@ -19,9 +19,9 @@ |
960 | >>> print browser.url.replace(bug_id, "BUG-ID") |
961 | http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/BUG-ID |
962 | |
963 | -We should have six subscribers now. The bug reporter (also a package |
964 | -subscriber), mark, the distro bug supervisor, ubuntu-team, and carlos |
965 | -and foobar, who are subscribed to the distribution. |
966 | +We should have three subscribers now. The bug reporter (also a package |
967 | +subscriber), mark, the distro bug supervisor kamion, and foobar, who is |
968 | +subscribed to the distribution. |
969 | |
970 | >>> from zope.component import getUtility |
971 | >>> from canonical.launchpad.interfaces import IBugSet |
972 | @@ -36,8 +36,7 @@ |
973 | |
974 | >>> bugset = getUtility(IBugSet) |
975 | >>> subscriber_names(bugset.get(bug_id)) |
976 | - [u'carlos', u'kamion', u'mark', u'name16', u'ubuntu-team', |
977 | - u'ubuntu-translators'] |
978 | + [u'kamion', u'mark', u'name16'] |
979 | |
980 | >>> logout() |
981 |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Am 24.05.2010 20:20, schrieb Curtis Hovey:
> This is my branch to refactor bug role view code for reuse in another view.
Thanks for doing this!
> /bugs.launchpad .net/bugs/ 133676 ntact -t initial- bug-contacts
> lp:~sinzui/launchpad/bug-contacts-1
> Diff size: 797 (includes deletions of redundant tests)
> Launchpad bug: https:/
> Test command: ./bin/test -vv \
> -t test_bugsupervisor -t test_securityco
> Pre-implementation: no one
I guess I should trust you that this is the right thing to do. Sure looks
right but shouldn't the bugs team at least know about it?
> Target release: 10.05 ------- ------- ------- ------- --
>
>
> Refactor bug role view code for reuse
> -------
[...]
> === modified file 'lib/lp/ bugs/browser/ bugsupervisor. py'
All good here except that I wonder if the Mixin should go in its own file?
(See next comment)
> === modified file 'lib/lp/ bugs/browser/ securitycontact .py' bugs/browser/ securitycontact .py 2010-05-18 21:24:27 +0000 bugs/browser/ securitycontact .py 2010-05-24 18:20:55 +0000 ctEditView" ] interfaces. securitycontact import IHasSecurityContact browser. bugsupervisor import BugRoleMixin
> --- lib/lp/
> +++ lib/lp/
> @@ -7,11 +7,12 @@
> __all__ = ["SecurityConta
>
> from lp.bugs.
> +from lp.bugs.
from lp.bugs. browser. bugrole import BugRoleMixin
What do you think?
[...]
> === added file 'lib/lp/ bugs/browser/ tests/test_ bugsupervisor. py' bugs/browser/ tests/test_ bugsupervisor. py 1970-01-01 00:00:00 +0000 bugs/browser/ tests/test_ bugsupervisor. py 2010-05-24 18:20:55 +0000
> --- lib/lp/
> +++ lib/lp/
Thanks for adding this test. ;)
> @@ -0,0 +1,166 @@
> +# Copyright10 Canonical Ltd. This software is licensed under the
# Copyright 2010 Canonical Ltd...
> +# GNU Affero General Public License version (see the file LICENSE). form.interfaces import ConversionError browser. bugsupervisor import BugSupervisorEd itSchema initialized_ view nalLayer orEditView( TestCaseWithFac tory): nalLayer pervisorEditVie w, self).setUp() makePerson(
> +
> +"""Unit tests for bug supervisor views."""
> +
> +__metaclass__ = type
> +
> +import unittest
> +
> +from zope.app.
> +
> +from lp.bugs.
> +from lp.testing import login, login_person, TestCaseWithFactory
> +from lp.testing.views import create_
> +from canonical.testing import DatabaseFunctio
> +
> +
> +class TestBugSupervis
> +
> + layer = DatabaseFunctio
> +
> + def setUp(self):
> + super(TestBugSu
> + self.owner = self.factory.
> + name='splat', displayname='<splat />')
I understand that you use specific names because they appear in the messages
that you test but using a "tag" displayname here seems a bit cryptic. All
those < and > make the messages harder to read. I'd suggest using proper
names here and use the "tags" when you need them. See my suggestion below.
> + self.product = self.factory. makeProduct(
> + name="boing", displayname='<boing />', owner=self.owner)
Same here.
> + self.team = self.factory. makeTeam( name='thud' , owner=self.owner) self.owner)
> + login_person(
> +
> + def _makeForm(self, person):
> + if person is None:
> + ...