Merge lp:~allenap/launchpad/bugs-with-blueprints-bug-707103 into lp:launchpad

Proposed by Gavin Panella
Status: Merged
Approved by: Gavin Panella
Approved revision: no longer in the source branch.
Merged at revision: 12296
Proposed branch: lp:~allenap/launchpad/bugs-with-blueprints-bug-707103
Merge into: lp:launchpad
Diff against target: 390 lines (+147/-30)
6 files modified
lib/lp/bugs/interfaces/bugtarget.py (+26/-9)
lib/lp/bugs/interfaces/bugtask.py (+28/-10)
lib/lp/bugs/model/bugtarget.py (+2/-1)
lib/lp/bugs/model/bugtask.py (+19/-0)
lib/lp/bugs/tests/test_bugtask_search.py (+16/-0)
lib/lp/bugs/tests/test_searchtasks_webservice.py (+56/-10)
To merge this branch: bzr merge lp:~allenap/launchpad/bugs-with-blueprints-bug-707103
Reviewer Review Type Date Requested Status
Henning Eggers (community) code Approve
Review via email: mp+47866@code.launchpad.net

Commit message

[r=henninge][ui=none][bug=707103] Bugs with or without linked Blueprints can be selected via searchTasks(). This is available to the internal and web-service (devel) APIs.

Description of the change

Make it possible to search for bugs with or without linked Blueprints. I will update the advanced search in a subsequent branch; this branch just deals with the internal and webservice API.

To post a comment you must log in.
Revision history for this message
Henning Eggers (henninge) wrote :
Download full text (11.9 KiB)

[Sorry, somehow my mail client messed up. I hope you can figure out which lines my comments are referring to.]

Hi Gavin,
thanks for addressing this issue. Webservice API is not my strong side
so please forgive if I make the wrong assumptions.
The real problem in this branch that I see is
test_linked_blueprints_in_devel which I am sure needs fixing. Please
see my comments below.

 review needs-fixing

Cheers,
Henning

> > === modified file 'lib/lp/bugs/interfaces/bugtarget.py' ---
> > lib/lp/bugs/interfaces/bugtarget.py 2010-12-02 16:14:30 +0000
> > +++ lib/lp/bugs/interfaces/bugtarget.py 2011-01-31 14:57:17
> > +0000
[...]
> > @@ -203,16 +215,21 @@ has_bugtasks = Attribute( "True if a BugTask
> > has ever been reported for this target.")
> >
> > + # searchTasks devel API declaration.
> > @call_with(search_params=None, user=REQUEST_USER)
> > @operation_parameters(**search_tasks_params_for_api_devel)
> > @operation_returns_collection_of(IBugTask)
> > @export_read_operation() + + # Pop the *default* version
> > (decorators are run last to first).
> > @operation_removed_in_version('devel')
> >
> > + # searchTasks default API declaration.
> > @call_with(search_params=None, user=REQUEST_USER) -
> > @operation_parameters(**search_tasks_params_for_api_1_0) +
> > @operation_parameters(**search_tasks_params_for_api_default)
> > @operation_returns_collection_of(IBugTask)
> > @export_read_operation() + def searchTasks(search_params,
> > user=None, order_by=None, search_text=None, status=None,
> > importance=None,
I think it is due to my lack of webservice foo but I don't understand
how this works. Why are the decorators repeating themselves? Is that
the proper way to handle that? I am just asking so you can double
check, I don't expect a full explanation atm. ;-) Thanks for the added
comments, though.

> > === modified file 'lib/lp/bugs/interfaces/bugtask.py' ---
> > lib/lp/bugs/interfaces/bugtask.py 2010-12-17 04:26:11 +0000 +++
> > lib/lp/bugs/interfaces/bugtask.py 2011-01-31 14:57:17 +0000 @@
> > -9,6 +9,7 @@
> >
> > __all__ = [ 'BUG_SUPERVISOR_BUGTASK_STATUSES', +
> > 'BugBlueprintSearch', 'BugBranchSearch',
> > 'BugTagsSearchCombinator', 'BugTaskImportance', @@ -28,8 +29,6 @@
> > 'IDistroBugTask', 'IDistroSeriesBugTask',
> > 'IFrontPageBugTaskSearch', - 'IllegalRelatedBugTasksParams', -
> > 'IllegalTarget',
Hm, did you re-sort this manually or did some tool do it? For the
imports fixing script I implemented case insensitive sorting. But
that does not touch __all__.

> > 'INominationsReviewTableBatchNavigator', 'INullBugTask',
> > 'IPersonBugTaskSearch', @@ -37,13 +36,16 @@
> > 'IRemoveQuestionFromBugTaskForm', 'IUpstreamBugTask',
> > 'IUpstreamProductBugTaskSearch', +
> > 'IllegalRelatedBugTasksParams', + 'IllegalTarget',
> > 'RESOLVED_BUGTASK_STATUSES', 'UNRESOLVED_BUGTASK_STATUSES',
> > 'UserCannotEditBugTaskAssignee',
> > 'UserCannotEditBugTaskImportance',
> > 'UserCannotEditBugTaskMilestone', 'UserCannotEditBugTaskStatus', -
> > 'valid_remote_bug_url'] + 'valid_remote_bug_url', + ]
> >
> > from lazr.enum import ( DBEnumeratedType, @@ -340,7 +342,7 @@
> > """Bug branch search option.
> >
> > The possible values to search for bugs having branche...

review: Needs Fixing
Revision history for this message
Gavin Panella (allenap) wrote :
Download full text (14.6 KiB)

> [Sorry, somehow my mail client messed up. I hope you can figure out which
> lines my comments are referring to.]

Wow, it really nuked it :)

>
> Hi Gavin,
> thanks for addressing this issue. Webservice API is not my strong side
> so please forgive if I make the wrong assumptions.
> The real problem in this branch that I see is
> test_linked_blueprints_in_devel which I am sure needs fixing. Please
> see my comments below.
>
> review needs-fixing
>
> Cheers,
> Henning
>
>> === modified file 'lib/lp/bugs/interfaces/bugtarget.py' ---
>> lib/lp/bugs/interfaces/bugtarget.py 2010-12-02 16:14:30 +0000
>> +++ lib/lp/bugs/interfaces/bugtarget.py 2011-01-31 14:57:17
>> +0000
> [...]
>> @@ -203,16 +215,21 @@ has_bugtasks = Attribute( "True if a BugTask
>> has ever been reported for this target.")
>>
>> + # searchTasks devel API declaration.
>> @call_with(search_params=None, user=REQUEST_USER)
>> @operation_parameters(**search_tasks_params_for_api_devel)
>> @operation_returns_collection_of(IBugTask)
>> @export_read_operation() + + # Pop the *default* version
>> (decorators are run last to first).
>> @operation_removed_in_version('devel')
>>
>> + # searchTasks default API declaration.
>> @call_with(search_params=None, user=REQUEST_USER) -
>> @operation_parameters(**search_tasks_params_for_api_1_0) +
>> @operation_parameters(**search_tasks_params_for_api_default)
>> @operation_returns_collection_of(IBugTask)
>> @export_read_operation() + def searchTasks(search_params,
>> user=None, order_by=None, search_text=None, status=None,
>> importance=None,
>
> I think it is due to my lack of webservice foo but I don't understand
> how this works. Why are the decorators repeating themselves? Is that
> the proper way to handle that? I am just asking so you can double
> check, I don't expect a full explanation atm. ;-) Thanks for the added
> comments, though.

It's a bit crazy. While fixing this branch I filed a couple of bugs
against lazr.restful because of it, but here goes...

Everything has to be read in reverse, then we have to realise that
annotations are assembled onto a stack.

* This block declares the default API for searchTasks.

    export_read_operation()
    operation_returns_collection_of(IBugTask)
    operation_parameters(**search_tasks_params_for_api_default)
    call_with(search_params=None, user=REQUEST_USER)

* This is a weird operation. It declares that the operation above is
  not available in the devel API version by pushing a new version onto
  the annotations stack and setting its type to "removed_operation".

    operation_removed_in_version('devel')

* This block now declares the devel API for searchTasks. The
  annotation on the stack is already for the devel version by virtue
  of operation_removed_in_version. The export_read_operation below
  changes the annotation type to "read_operation".

    export_read_operation()
    operation_returns_collection_of(IBugTask)
    operation_parameters(**search_tasks_params_for_api_devel)
    call_with(search_params=None, user=REQUEST_USER)

>
>> === modified file 'lib/lp/bugs/interfaces/bugtask.py' ---
>> lib/lp/bugs/interfaces/bugtask.py 2010-12-17 04:26:11 +0000 +++
>> lib/lp/bugs/interfaces/...

Revision history for this message
Henning Eggers (henninge) wrote :

Thanks for your reply an the explanations.

>> Hm, did you re-sort this manually or did some tool do it? For the
>> imports fixing script I implemented case insensitive sorting. But
>> that does not touch __all__.
>
>A tool did it for me: Emacs :)
>
>(I set sort-fold-case before using sort-lines.)
>
>Is it wrong?

Since we don't have a written-down convention on it, it is not. ;-) I was merely wondering because for the import formatting I had used case-insensitive sorting, after a little discussion, because it is easier for humans to parse. But we never mentioned to use the same for __all__ although that would make sense to me. I was just wondering if you did this consciously (and have an opinion) or not. I guess the latter. No big deal, though.

You missed a major comment, though, which I am sure is due to the wacky formatting of my reply. I apologize for that again. Here it is:

> === modified file 'lib/lp/bugs/tests/test_searchtasks_webservice.py'
> @@ -35,3 +38,51 @@
> response = self.webservice.named_get('/mebuntu/inkanyamba',
> 'searchTasks', api_version='devel').jsonBody()
> self.assertEqual(response['total_size'], 1)
> +
> + def test_linked_blueprints_in_devel(self):
> + response = self.webservice.named_get('/mebuntu/inkanyamba',
> + 'searchTasks', api_version='devel').jsonBody()
> + self.assertEqual(response['total_size'], 1)
> +

I am not really sure I understand what this test does. I don't see
where the code refers to the new search parameter. Actually, it looks
identical to the previous test. Something missing here?

I won't block you on this because of my currently long feedback loop and I trust you will be able to either explain or fix it.

Thanks again!

review: Approve (code)
Revision history for this message
Gavin Panella (allenap) wrote :

> Thanks for your reply an the explanations.
>
> >> Hm, did you re-sort this manually or did some tool do it? For the
> >> imports fixing script I implemented case insensitive sorting. But
> >> that does not touch __all__.
> >
> >A tool did it for me: Emacs :)
> >
> >(I set sort-fold-case before using sort-lines.)
> >
> >Is it wrong?
>
> Since we don't have a written-down convention on it, it is not. ;-) I was
> merely wondering because for the import formatting I had used case-insensitive
> sorting, after a little discussion, because it is easier for humans to parse.
> But we never mentioned to use the same for __all__ although that would make
> sense to me. I was just wondering if you did this consciously (and have an
> opinion) or not. I guess the latter. No big deal, though.

Ah, I thought we had agreed to sort case-insensitively. My preference
is actually for case-sensitive sorting since it is the default in
Emacs, and because I, strangely, find it easier to read.

> You missed a major comment, though, which I am sure is due to the wacky
> formatting of my reply. I apologize for that again. Here it is:

Oops, thanks.

> > === modified file 'lib/lp/bugs/tests/test_searchtasks_webservice.py'
> > @@ -35,3 +38,51 @@
> > response = self.webservice.named_get('/mebuntu/inkanyamba',
> > 'searchTasks', api_version='devel').jsonBody()
> > self.assertEqual(response['total_size'], 1)
> > +
> > + def test_linked_blueprints_in_devel(self):
> > + response = self.webservice.named_get('/mebuntu/inkanyamba',
> > + 'searchTasks', api_version='devel').jsonBody()
> > + self.assertEqual(response['total_size'], 1)
> > +
>
> I am not really sure I understand what this test does. I don't see
> where the code refers to the new search parameter. Actually, it looks
> identical to the previous test. Something missing here?

It was a copy and paste error! Thanks for spotting it.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/interfaces/bugtarget.py'
2--- lib/lp/bugs/interfaces/bugtarget.py 2010-12-02 16:14:30 +0000
3+++ lib/lp/bugs/interfaces/bugtarget.py 2011-02-01 16:35:45 +0000
4@@ -35,7 +35,6 @@
5 )
6 from lazr.restful.fields import Reference
7 from lazr.restful.interface import copy_field
8-
9 from zope.interface import (
10 Attribute,
11 Interface,
12@@ -52,6 +51,7 @@
13
14 from canonical.launchpad import _
15 from lp.bugs.interfaces.bugtask import (
16+ BugBlueprintSearch,
17 BugBranchSearch,
18 BugTagsSearchCombinator,
19 IBugTask,
20@@ -59,7 +59,8 @@
21 )
22 from lp.services.fields import Tag
23
24-search_tasks_params_for_api_1_0 = {
25+
26+search_tasks_params_common = {
27 "order_by": List(
28 title=_('List of fields by which the results are ordered.'),
29 value_type=Text(),
30@@ -80,7 +81,6 @@
31 "tags": copy_field(IBugTaskSearch['tag']),
32 "tags_combinator": copy_field(IBugTaskSearch['tags_combinator']),
33 "omit_duplicates": copy_field(IBugTaskSearch['omit_dupes']),
34- "omit_targeted": copy_field(IBugTaskSearch['omit_targeted']),
35 "status_upstream": copy_field(IBugTaskSearch['status_upstream']),
36 "milestone_assignment": copy_field(
37 IBugTaskSearch['milestone_assignment']),
38@@ -172,9 +172,21 @@
39 "date."),
40 required=False),
41 }
42-search_tasks_params_for_api_devel = search_tasks_params_for_api_1_0.copy()
43-search_tasks_params_for_api_devel["omit_targeted"] = copy_field(
44- IBugTaskSearch['omit_targeted'], default=False)
45+
46+search_tasks_params_for_api_default = dict(
47+ search_tasks_params_common,
48+ omit_targeted=copy_field(
49+ IBugTaskSearch['omit_targeted']))
50+
51+search_tasks_params_for_api_devel = dict(
52+ search_tasks_params_common,
53+ omit_targeted=copy_field(
54+ IBugTaskSearch['omit_targeted'], default=False),
55+ linked_blueprints=Choice(
56+ title=_(
57+ u"Search for bugs that are linked to blueprints or for "
58+ u"bugs that are not linked to blueprints."),
59+ vocabulary=BugBlueprintSearch, required=False))
60
61
62 class IHasBugs(Interface):
63@@ -203,16 +215,21 @@
64 has_bugtasks = Attribute(
65 "True if a BugTask has ever been reported for this target.")
66
67+ # searchTasks devel API declaration.
68 @call_with(search_params=None, user=REQUEST_USER)
69 @operation_parameters(**search_tasks_params_for_api_devel)
70 @operation_returns_collection_of(IBugTask)
71 @export_read_operation()
72+
73+ # Pop the *default* version (decorators are run last to first).
74 @operation_removed_in_version('devel')
75
76+ # searchTasks default API declaration.
77 @call_with(search_params=None, user=REQUEST_USER)
78- @operation_parameters(**search_tasks_params_for_api_1_0)
79+ @operation_parameters(**search_tasks_params_for_api_default)
80 @operation_returns_collection_of(IBugTask)
81 @export_read_operation()
82+
83 def searchTasks(search_params, user=None,
84 order_by=None, search_text=None,
85 status=None, importance=None,
86@@ -232,8 +249,8 @@
87 hardware_owner_is_affected_by_bug=False,
88 hardware_owner_is_subscribed_to_bug=False,
89 hardware_is_linked_to_bug=False, linked_branches=None,
90- structural_subscriber=None, modified_since=None,
91- created_since=None, prejoins=[]):
92+ linked_blueprints=None, structural_subscriber=None,
93+ modified_since=None, created_since=None, prejoins=[]):
94 """Search the IBugTasks reported on this entity.
95
96 :search_params: a BugTaskSearchParams object
97
98=== modified file 'lib/lp/bugs/interfaces/bugtask.py'
99--- lib/lp/bugs/interfaces/bugtask.py 2010-12-17 04:26:11 +0000
100+++ lib/lp/bugs/interfaces/bugtask.py 2011-02-01 16:35:45 +0000
101@@ -9,6 +9,7 @@
102
103 __all__ = [
104 'BUG_SUPERVISOR_BUGTASK_STATUSES',
105+ 'BugBlueprintSearch',
106 'BugBranchSearch',
107 'BugTagsSearchCombinator',
108 'BugTaskImportance',
109@@ -28,8 +29,6 @@
110 'IDistroBugTask',
111 'IDistroSeriesBugTask',
112 'IFrontPageBugTaskSearch',
113- 'IllegalRelatedBugTasksParams',
114- 'IllegalTarget',
115 'INominationsReviewTableBatchNavigator',
116 'INullBugTask',
117 'IPersonBugTaskSearch',
118@@ -37,13 +36,16 @@
119 'IRemoveQuestionFromBugTaskForm',
120 'IUpstreamBugTask',
121 'IUpstreamProductBugTaskSearch',
122+ 'IllegalRelatedBugTasksParams',
123+ 'IllegalTarget',
124 'RESOLVED_BUGTASK_STATUSES',
125 'UNRESOLVED_BUGTASK_STATUSES',
126 'UserCannotEditBugTaskAssignee',
127 'UserCannotEditBugTaskImportance',
128 'UserCannotEditBugTaskMilestone',
129 'UserCannotEditBugTaskStatus',
130- 'valid_remote_bug_url']
131+ 'valid_remote_bug_url',
132+ ]
133
134 from lazr.enum import (
135 DBEnumeratedType,
136@@ -340,7 +342,7 @@
137 """Bug branch search option.
138
139 The possible values to search for bugs having branches attached
140- or not having branches attched.
141+ or not having branches attached.
142 """
143
144 ALL = Item("Show all bugs")
145@@ -350,6 +352,20 @@
146 BUGS_WITHOUT_BRANCHES = Item("Show only Bugs without linked Branches")
147
148
149+class BugBlueprintSearch(EnumeratedType):
150+ """Bug blueprint search option.
151+
152+ The possible values to search for bugs having blueprints attached
153+ or not having blueprints attached.
154+ """
155+
156+ ALL = Item("Show all bugs")
157+
158+ BUGS_WITH_BLUEPRINTS = Item("Show only Bugs with linked Blueprints")
159+
160+ BUGS_WITHOUT_BLUEPRINTS = Item("Show only Bugs without linked Blueprints")
161+
162+
163 # XXX: Brad Bollenbach 2005-12-02 bugs=5320:
164 # In theory, INCOMPLETE belongs in UNRESOLVED_BUGTASK_STATUSES, but the
165 # semantics of our current reports would break if it were added to the
166@@ -1144,9 +1160,9 @@
167 hardware_owner_is_affected_by_bug=False,
168 hardware_owner_is_subscribed_to_bug=False,
169 hardware_is_linked_to_bug=False,
170- linked_branches=None, structural_subscriber=None,
171- modified_since=None, created_since=None,
172- exclude_conjoined_tasks=False):
173+ linked_branches=None, linked_blueprints=None,
174+ structural_subscriber=None, modified_since=None,
175+ created_since=None, exclude_conjoined_tasks=False):
176
177 self.bug = bug
178 self.searchtext = searchtext
179@@ -1189,6 +1205,7 @@
180 hardware_owner_is_subscribed_to_bug)
181 self.hardware_is_linked_to_bug = hardware_is_linked_to_bug
182 self.linked_branches = linked_branches
183+ self.linked_blueprints = linked_blueprints
184 self.structural_subscriber = structural_subscriber
185 self.modified_since = modified_since
186 self.created_since = created_since
187@@ -1265,8 +1282,8 @@
188 hardware_owner_is_affected_by_bug=False,
189 hardware_owner_is_subscribed_to_bug=False,
190 hardware_is_linked_to_bug=False, linked_branches=None,
191- structural_subscriber=None, modified_since=None,
192- created_since=None):
193+ linked_blueprints=None, structural_subscriber=None,
194+ modified_since=None, created_since=None):
195 """Create and return a new instance using the parameter list."""
196 search_params = cls(user=user, orderby=order_by)
197
198@@ -1332,7 +1349,8 @@
199 hardware_owner_is_subscribed_to_bug)
200 search_params.hardware_is_linked_to_bug = (
201 hardware_is_linked_to_bug)
202- search_params.linked_branches=linked_branches
203+ search_params.linked_branches = linked_branches
204+ search_params.linked_blueprints = linked_blueprints
205 search_params.structural_subscriber = structural_subscriber
206 search_params.modified_since = modified_since
207 search_params.created_since = created_since
208
209=== modified file 'lib/lp/bugs/model/bugtarget.py'
210--- lib/lp/bugs/model/bugtarget.py 2010-11-30 21:39:25 +0000
211+++ lib/lp/bugs/model/bugtarget.py 2011-02-01 16:35:45 +0000
212@@ -94,7 +94,8 @@
213 hardware_owner_is_affected_by_bug=False,
214 hardware_owner_is_subscribed_to_bug=False,
215 hardware_is_linked_to_bug=False, linked_branches=None,
216- modified_since=None, created_since=None, prejoins=[]):
217+ linked_blueprints=None, modified_since=None,
218+ created_since=None, prejoins=[]):
219 """See `IHasBugs`."""
220 if status is None:
221 # If no statuses are supplied, default to the
222
223=== modified file 'lib/lp/bugs/model/bugtask.py'
224--- lib/lp/bugs/model/bugtask.py 2011-01-20 04:50:35 +0000
225+++ lib/lp/bugs/model/bugtask.py 2011-02-01 16:35:45 +0000
226@@ -103,6 +103,7 @@
227 from lp.bugs.interfaces.bugtask import (
228 BUG_SUPERVISOR_BUGTASK_STATUSES,
229 BugBranchSearch,
230+ BugBlueprintSearch,
231 BugTaskImportance,
232 BugTaskSearchParams,
233 BugTaskStatus,
234@@ -2066,6 +2067,10 @@
235 # we don't need to add any clause.
236 pass
237
238+ linked_blueprints_clause = self._buildBlueprintRelatedClause(params)
239+ if linked_blueprints_clause is not None:
240+ extra_clauses.append(linked_blueprints_clause)
241+
242 if params.modified_since:
243 extra_clauses.append(
244 "Bug.date_last_updated > %s" % (
245@@ -2318,6 +2323,20 @@
246 ', '.join(tables), ' AND '.join(clauses))
247 return clause
248
249+ def _buildBlueprintRelatedClause(self, params):
250+ """Find bugs related to Blueprints, or not."""
251+ linked_blueprints = params.linked_blueprints
252+ if linked_blueprints == BugBlueprintSearch.BUGS_WITH_BLUEPRINTS:
253+ return "EXISTS (%s)" % (
254+ "SELECT 1 FROM SpecificationBug"
255+ " WHERE SpecificationBug.bug = Bug.id")
256+ elif linked_blueprints == BugBlueprintSearch.BUGS_WITHOUT_BLUEPRINTS:
257+ return "NOT EXISTS (%s)" % (
258+ "SELECT 1 FROM SpecificationBug"
259+ " WHERE SpecificationBug.bug = Bug.id")
260+ else:
261+ return None
262+
263 def buildOrigin(self, join_tables, prejoin_tables, clauseTables):
264 """Build the parameter list for Store.using().
265
266
267=== modified file 'lib/lp/bugs/tests/test_bugtask_search.py'
268--- lib/lp/bugs/tests/test_bugtask_search.py 2011-01-13 17:00:41 +0000
269+++ lib/lp/bugs/tests/test_bugtask_search.py 2011-02-01 16:35:45 +0000
270@@ -31,6 +31,7 @@
271 from lp.bugs.interfaces.bugattachment import BugAttachmentType
272 from lp.bugs.interfaces.bugtask import (
273 BugBranchSearch,
274+ BugBlueprintSearch,
275 BugTaskImportance,
276 BugTaskSearchParams,
277 BugTaskStatus,
278@@ -445,6 +446,21 @@
279 user=None, linked_branches=BugBranchSearch.BUGS_WITHOUT_BRANCHES)
280 self.assertSearchFinds(params, self.bugtasks[1:])
281
282+ def test_blueprints_linked(self):
283+ # Search results can be limited to bugs with or without linked
284+ # blueprints.
285+ with person_logged_in(self.owner):
286+ blueprint = self.factory.makeSpecification()
287+ blueprint.linkBug(self.bugtasks[0].bug)
288+ params = self.getBugTaskSearchParams(
289+ user=None, linked_blueprints=(
290+ BugBlueprintSearch.BUGS_WITH_BLUEPRINTS))
291+ self.assertSearchFinds(params, self.bugtasks[:1])
292+ params = self.getBugTaskSearchParams(
293+ user=None, linked_blueprints=(
294+ BugBlueprintSearch.BUGS_WITHOUT_BLUEPRINTS))
295+ self.assertSearchFinds(params, self.bugtasks[1:])
296+
297 def test_limit_search_to_one_bug(self):
298 # Search results can be limited to a given bug.
299 params = self.getBugTaskSearchParams(
300
301=== modified file 'lib/lp/bugs/tests/test_searchtasks_webservice.py'
302--- lib/lp/bugs/tests/test_searchtasks_webservice.py 2010-10-04 19:50:45 +0000
303+++ lib/lp/bugs/tests/test_searchtasks_webservice.py 2011-02-01 16:35:45 +0000
304@@ -5,26 +5,29 @@
305
306 __metaclass__ = type
307
308-from canonical.launchpad.ftests import login
309-from lp.testing import TestCaseWithFactory
310 from canonical.launchpad.testing.pages import LaunchpadWebServiceCaller
311 from canonical.testing.layers import DatabaseFunctionalLayer
312+from lp.testing import (
313+ person_logged_in,
314+ TestCaseWithFactory,
315+ )
316
317
318 class TestOmitTargetedParameter(TestCaseWithFactory):
319 """Test all values for the omit_targeted search parameter."""
320+
321 layer = DatabaseFunctionalLayer
322
323 def setUp(self):
324- TestCaseWithFactory.setUp(self)
325- login('foo.bar@canonical.com')
326- self.distro = self.factory.makeDistribution(name='mebuntu')
327- self.release = self.factory.makeDistroRelease(name='inkanyamba',
328- distribution=self.distro)
329+ super(TestOmitTargetedParameter, self).setUp()
330+ self.owner = self.factory.makePerson()
331+ with person_logged_in(self.owner):
332+ self.distro = self.factory.makeDistribution(name='mebuntu')
333+ self.release = self.factory.makeDistroRelease(
334+ name='inkanyamba', distribution=self.distro)
335 self.bug = self.factory.makeBugTask(target=self.release)
336-
337- self.webservice = LaunchpadWebServiceCaller('launchpad-library',
338- 'salgado-change-anything')
339+ self.webservice = LaunchpadWebServiceCaller(
340+ 'launchpad-library', 'salgado-change-anything')
341
342 def test_omit_targeted_old_default_true(self):
343 response = self.webservice.named_get('/mebuntu/inkanyamba',
344@@ -35,3 +38,46 @@
345 response = self.webservice.named_get('/mebuntu/inkanyamba',
346 'searchTasks', api_version='devel').jsonBody()
347 self.assertEqual(response['total_size'], 1)
348+
349+
350+class TestLinkedBlueprintsParameter(TestCaseWithFactory):
351+ """Tests for the linked_blueprints parameter."""
352+
353+ layer = DatabaseFunctionalLayer
354+
355+ def setUp(self):
356+ super(TestLinkedBlueprintsParameter, self).setUp()
357+ self.owner = self.factory.makePerson()
358+ with person_logged_in(self.owner):
359+ self.product = self.factory.makeProduct()
360+ self.bug = self.factory.makeBugTask(target=self.product)
361+ self.webservice = LaunchpadWebServiceCaller(
362+ 'launchpad-library', 'salgado-change-anything')
363+
364+ def search(self, api_version, **kwargs):
365+ return self.webservice.named_get(
366+ '/%s' % self.product.name, 'searchTasks',
367+ api_version=api_version, **kwargs).jsonBody()
368+
369+ def test_linked_blueprints_in_devel(self):
370+ # Searching for linked Blueprints works in the devel API.
371+ self.search("devel", linked_blueprints="Show all bugs")
372+
373+ def test_linked_blueprints_in_devel_2(self):
374+ # The linked_blueprints is considered. An error is returned if its
375+ # value is not a member of BugBlueprintSearch.
376+ self.assertRaises(
377+ ValueError, self.search, "devel",
378+ linked_blueprints="Teabags!")
379+
380+ def test_linked_blueprints_not_in_1_0(self):
381+ # Searching for linked Blueprints does not work in the 1.0 API. No
382+ # validation is performed for the linked_blueprints parameter, and
383+ # thus no error is returned when we pass rubbish.
384+ self.search("1.0", linked_blueprints="Teabags!")
385+
386+ def test_linked_blueprints_not_in_beta(self):
387+ # Searching for linked Blueprints does not work in the beta API. No
388+ # validation is performed for the linked_blueprints parameter, and
389+ # thus no error is returned when we pass rubbish.
390+ self.search("beta", linked_blueprints="Teabags!")