Merge lp:~julian-edwards/maas/dup-boot-source-selections-part2-bug-1360280 into lp:~maas-committers/maas/trunk
- dup-boot-source-selections-part2-bug-1360280
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Julian Edwards |
Approved revision: | no longer in the source branch. |
Merged at revision: | 3367 |
Proposed branch: | lp:~julian-edwards/maas/dup-boot-source-selections-part2-bug-1360280 |
Merge into: | lp:~maas-committers/maas/trunk |
Prerequisite: | lp:~julian-edwards/maas/dup-boot-source-selections-bug-1360280 |
Diff against target: |
466 lines (+321/-38) 4 files modified
src/maasserver/api/tests/test_boot_source_selections.py (+24/-13) src/maasserver/forms.py (+56/-0) src/maasserver/testing/factory.py (+6/-0) src/maasserver/tests/test_forms_bootsourceselection.py (+235/-25) |
To merge this branch: | bzr merge lp:~julian-edwards/maas/dup-boot-source-selections-part2-bug-1360280 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Graham Binns (community) | Approve | ||
Review via email: mp+241472@code.launchpad.net |
Commit message
Validate BootSourceSelection changes against the available selection that lives in BootSourceCache. Previously, bogus entries could be added.
Description of the change
Whoever reviews this, please double check that this won't affect custom images. I don't think it will, but ...
Julian Edwards (julian-edwards) wrote : | # |
On Wednesday 12 Nov 2014 07:37:38 you wrote:
> I can *hear* jtv, here and elsewhere, saying "What is this obsession with
> creating 3 _things_?"
Arf :)
> Also, I don't actually understand from the test why you're creating three
> mostly identical BootSourceCaches, here and elsewhere. What does it
> actually achieve? Wouldn't one do just as well?
You figured this out....
> > + boot_caches.
> > + boot_source, arch=factory.
> > + os=os, release=release))
> > +
> > + params = {
> > + 'os': os,
> > + 'release': release,
> > + 'arches': [boot_caches[
>
> Ah, I get it now; you're combining the valid values for the field you're
> testing on from some but not all of the BootSourceCaches you've created.
>
> Consider moving the for loop into a helper. Also, add a comment (or a
> docstring on the helper so that you're not having to C&P comments all over
> the shop) explaining why you're creating multiple BootSourceCaches and what
> they'll be used for.
I've been staring at the code for so long that it didn't look that bad any
more. I guess I'll try for a helper - I ran out of brain juice on this one.
Thanks for reviewing!
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~julian-edwards/maas/dup-boot-source-selections-part2-bug-1360280 into lp:maas failed. Below is the output from the failed tests.
Ign http://
Get:1 http://
Ign http://
Get:2 http://
Ign http://
Hit http://
Get:3 http://
Hit http://
Get:4 http://
Get:5 http://
Hit http://
Get:6 http://
Get:7 http://
Get:8 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Get:9 http://
Get:10 http://
Get:11 http://
Get:12 http://
Hit http://
Hit http://
Ign http://
Ign http://
Fetched 1,200 kB in 2s (411 kB/s)
Reading package lists...
sudo DEBIAN_
--
Julian Edwards (julian-edwards) wrote : | # |
Thanks for pushing me to write a helper Graham, it was needed in the API tests that had become broken because of the extra validation!
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~julian-edwards/maas/dup-boot-source-selections-part2-bug-1360280 into lp:maas failed. Below is the output from the failed tests.
Ign http://
Get:1 http://
Ign http://
Get:2 http://
Ign http://
Hit http://
Get:3 http://
Hit http://
Get:4 http://
Get:5 http://
Hit http://
Get:6 http://
Get:7 http://
Get:8 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Get:9 http://
Hit http://
Hit http://
Get:10 http://
Get:11 http://
Get:12 http://
Hit http://
Hit http://
Ign http://
Ign http://
Fetched 1,200 kB in 2s (411 kB/s)
Reading package lists...
sudo DEBIAN_
--
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~julian-edwards/maas/dup-boot-source-selections-part2-bug-1360280 into lp:maas failed. Below is the output from the failed tests.
Ign http://
Get:1 http://
Get:2 http://
Ign http://
Ign http://
Hit http://
Get:3 http://
Hit http://
Get:4 http://
Get:5 http://
Hit http://
Get:6 http://
Get:7 http://
Get:8 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Get:9 http://
Get:10 http://
Get:11 http://
Get:12 http://
Hit http://
Hit http://
Ign http://
Ign http://
Fetched 1,200 kB in 2s (418 kB/s)
Reading package lists...
sudo DEBIAN_
--
Julian Edwards (julian-edwards) wrote : | # |
It would help if I remembered to push my changes up.
Preview Diff
1 | === modified file 'src/maasserver/api/tests/test_boot_source_selections.py' |
2 | --- src/maasserver/api/tests/test_boot_source_selections.py 2014-10-23 15:26:37 +0000 |
3 | +++ src/maasserver/api/tests/test_boot_source_selections.py 2014-11-13 03:13:37 +0000 |
4 | @@ -117,17 +117,20 @@ |
5 | boot_source_selection = factory.make_BootSourceSelection() |
6 | new_os = factory.make_name('os') |
7 | new_release = factory.make_name('release') |
8 | + boot_source_caches = factory.make_many_BootSourceCaches( |
9 | + 2, boot_source=boot_source_selection.boot_source, os=new_os, |
10 | + release=new_release) |
11 | new_values = { |
12 | 'os': new_os, |
13 | 'release': new_release, |
14 | - 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
15 | + 'arches': [boot_source_caches[0].arch, boot_source_caches[1].arch], |
16 | 'subarches': [ |
17 | - factory.make_name('subarch'), factory.make_name('subarch')], |
18 | - 'labels': [factory.make_name('label')], |
19 | + boot_source_caches[0].subarch, boot_source_caches[1].subarch], |
20 | + 'labels': [boot_source_caches[0].label], |
21 | } |
22 | response = self.client_put( |
23 | get_boot_source_selection_uri(boot_source_selection), new_values) |
24 | - self.assertEqual(httplib.OK, response.status_code) |
25 | + self.assertEqual(httplib.OK, response.status_code, response.content) |
26 | boot_source_selection = reload_object(boot_source_selection) |
27 | self.assertAttributes(boot_source_selection, new_values) |
28 | |
29 | @@ -216,12 +219,15 @@ |
30 | self.become_admin() |
31 | boot_source_selection = factory.make_BootSourceSelection() |
32 | new_release = factory.make_name('release') |
33 | + boot_source_caches = factory.make_many_BootSourceCaches( |
34 | + 2, boot_source=boot_source_selection.boot_source, |
35 | + release=new_release) |
36 | new_values = { |
37 | 'release': new_release, |
38 | - 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
39 | + 'arches': [boot_source_caches[0].arch, boot_source_caches[1].arch], |
40 | 'subarches': [ |
41 | - factory.make_name('subarch'), factory.make_name('subarch')], |
42 | - 'labels': [factory.make_name('label')], |
43 | + boot_source_caches[0].subarch, boot_source_caches[1].subarch], |
44 | + 'labels': [boot_source_caches[0].label], |
45 | } |
46 | response = self.client_put( |
47 | get_boot_source_selection_backward_uri( |
48 | @@ -279,12 +285,15 @@ |
49 | self.become_admin() |
50 | boot_source = factory.make_BootSource() |
51 | new_release = factory.make_name('release') |
52 | + boot_source_caches = factory.make_many_BootSourceCaches( |
53 | + 2, boot_source=boot_source, |
54 | + release=new_release) |
55 | params = { |
56 | 'release': new_release, |
57 | - 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
58 | + 'arches': [boot_source_caches[0].arch, boot_source_caches[1].arch], |
59 | 'subarches': [ |
60 | - factory.make_name('subarch'), factory.make_name('subarch')], |
61 | - 'labels': [factory.make_name('label')], |
62 | + boot_source_caches[0].subarch, boot_source_caches[1].subarch], |
63 | + 'labels': [boot_source_caches[0].label], |
64 | } |
65 | response = self.client.post( |
66 | reverse( |
67 | @@ -373,12 +382,14 @@ |
68 | self.become_admin() |
69 | boot_source = factory.make_BootSource() |
70 | new_release = factory.make_name('release') |
71 | + boot_source_caches = factory.make_many_BootSourceCaches( |
72 | + 2, boot_source=boot_source, release=new_release) |
73 | params = { |
74 | 'release': new_release, |
75 | - 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
76 | + 'arches': [boot_source_caches[0].arch, boot_source_caches[1].arch], |
77 | 'subarches': [ |
78 | - factory.make_name('subarch'), factory.make_name('subarch')], |
79 | - 'labels': [factory.make_name('label')], |
80 | + boot_source_caches[0].subarch, boot_source_caches[1].subarch], |
81 | + 'labels': [boot_source_caches[0].label], |
82 | } |
83 | response = self.client.post(self.get_uri(boot_source), params) |
84 | self.assertEqual(httplib.OK, response.status_code) |
85 | |
86 | === modified file 'src/maasserver/forms.py' |
87 | --- src/maasserver/forms.py 2014-11-07 15:33:47 +0000 |
88 | +++ src/maasserver/forms.py 2014-11-13 03:13:37 +0000 |
89 | @@ -109,6 +109,7 @@ |
90 | BootResourceFile, |
91 | BootResourceSet, |
92 | BootSource, |
93 | + BootSourceCache, |
94 | BootSourceSelection, |
95 | Config, |
96 | DownloadProgress, |
97 | @@ -2354,6 +2355,61 @@ |
98 | else: |
99 | self.boot_source = boot_source |
100 | |
101 | + def clean(self): |
102 | + cleaned_data = super(BootSourceSelectionForm, self).clean() |
103 | + |
104 | + # Don't filter on OS if not provided. This is to maintain |
105 | + # backwards compatibility for when OS didn't exist in the API. |
106 | + if cleaned_data['os']: |
107 | + cache = BootSourceCache.objects.filter( |
108 | + boot_source=self.boot_source, os=cleaned_data['os'], |
109 | + release=cleaned_data['release']) |
110 | + else: |
111 | + cache = BootSourceCache.objects.filter( |
112 | + boot_source=self.boot_source, release=cleaned_data['release']) |
113 | + |
114 | + if not cache.exists(): |
115 | + set_form_error( |
116 | + self, "os", |
117 | + "OS %s with release %s has no available images for download" % |
118 | + (cleaned_data['os'], cleaned_data['release'])) |
119 | + return cleaned_data |
120 | + |
121 | + values = cache.values_list("arch", "subarch", "label") |
122 | + arches, subarches, labels = zip(*values) |
123 | + |
124 | + # Validate architectures. |
125 | + required_arches_set = set(arch for arch in cleaned_data['arches']) |
126 | + wildcard_arches = '*' in required_arches_set |
127 | + if not wildcard_arches and not required_arches_set <= set(arches): |
128 | + set_form_error( |
129 | + self, "arches", |
130 | + "No available images to download for %s" % |
131 | + cleaned_data['arches']) |
132 | + |
133 | + # Validate subarchitectures. |
134 | + required_subarches_set = set(sa for sa in cleaned_data['subarches']) |
135 | + wildcard_subarches = '*' in required_subarches_set |
136 | + if ( |
137 | + not wildcard_subarches and |
138 | + not required_subarches_set <= set(subarches) |
139 | + ): |
140 | + set_form_error( |
141 | + self, "subarches", |
142 | + "No available images to download for %s" % |
143 | + cleaned_data['subarches']) |
144 | + |
145 | + # Validate labels. |
146 | + required_labels_set = set(label for label in cleaned_data['labels']) |
147 | + wildcard_labels = '*' in required_labels_set |
148 | + if not wildcard_labels and not required_labels_set <= set(labels): |
149 | + set_form_error( |
150 | + self, "labels", |
151 | + "No available images to download for %s" % |
152 | + cleaned_data['labels']) |
153 | + |
154 | + return cleaned_data |
155 | + |
156 | def save(self, *args, **kwargs): |
157 | boot_source_selection = super( |
158 | BootSourceSelectionForm, self).save(commit=False) |
159 | |
160 | === modified file 'src/maasserver/testing/factory.py' |
161 | --- src/maasserver/testing/factory.py 2014-11-07 16:54:51 +0000 |
162 | +++ src/maasserver/testing/factory.py 2014-11-13 03:13:37 +0000 |
163 | @@ -891,6 +891,12 @@ |
164 | boot_source=boot_source, os=os, arch=arch, |
165 | subarch=subarch, release=release, label=label) |
166 | |
167 | + def make_many_BootSourceCaches(self, number, **kwargs): |
168 | + caches = list() |
169 | + for _ in range(number): |
170 | + caches.append(self.make_BootSourceCache(**kwargs)) |
171 | + return caches |
172 | + |
173 | def make_BootSourceSelection(self, boot_source=None, os=None, |
174 | release=None, arches=None, subarches=None, |
175 | labels=None): |
176 | |
177 | === modified file 'src/maasserver/tests/test_forms_bootsourceselection.py' |
178 | --- src/maasserver/tests/test_forms_bootsourceselection.py 2014-11-11 05:15:23 +0000 |
179 | +++ src/maasserver/tests/test_forms_bootsourceselection.py 2014-11-13 03:13:37 +0000 |
180 | @@ -24,16 +24,47 @@ |
181 | class TestBootSourceSelectionForm(MAASServerTestCase): |
182 | """Tests for `BootSourceSelectionForm`.""" |
183 | |
184 | + def make_valid_source_selection_params(self, boot_source=None): |
185 | + # Helper that creates a valid BootSourceCache and parameters for |
186 | + # a BootSourceSelectionForm that will validate against the |
187 | + # cache. |
188 | + if boot_source is None: |
189 | + boot_source = factory.make_BootSource() |
190 | + arch = factory.make_name('arch') |
191 | + arch2 = factory.make_name('arch') |
192 | + subarch = factory.make_name('subarch') |
193 | + subarch2 = factory.make_name('subarch') |
194 | + label = factory.make_name('label') |
195 | + label2 = factory.make_name('label') |
196 | + params = { |
197 | + 'os': factory.make_name('os'), |
198 | + 'release': factory.make_name('release'), |
199 | + 'arches': [arch, arch2], |
200 | + 'subarches': [subarch, subarch2], |
201 | + 'labels': [label, label2], |
202 | + } |
203 | + factory.make_BootSourceCache( |
204 | + boot_source=boot_source, |
205 | + os=params['os'], |
206 | + release=params['release'], |
207 | + arch=arch, |
208 | + subarch=subarch, |
209 | + label=label, |
210 | + ) |
211 | + factory.make_BootSourceCache( |
212 | + boot_source=boot_source, |
213 | + os=params['os'], |
214 | + release=params['release'], |
215 | + arch=arch2, |
216 | + subarch=subarch2, |
217 | + label=label2, |
218 | + ) |
219 | + return params |
220 | + |
221 | def test_edits_boot_source_selection_object(self): |
222 | boot_source_selection = factory.make_BootSourceSelection() |
223 | - params = { |
224 | - 'os': factory.make_name('os'), |
225 | - 'release': factory.make_name('release'), |
226 | - 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
227 | - 'subarches': [ |
228 | - factory.make_name('subarch'), factory.make_name('subarch')], |
229 | - 'labels': [factory.make_name('label'), factory.make_name('label')], |
230 | - } |
231 | + boot_source = boot_source_selection.boot_source |
232 | + params = self.make_valid_source_selection_params(boot_source) |
233 | form = BootSourceSelectionForm( |
234 | instance=boot_source_selection, data=params) |
235 | self.assertTrue(form.is_valid(), form._errors) |
236 | @@ -43,14 +74,7 @@ |
237 | |
238 | def test_creates_boot_source_selection_object(self): |
239 | boot_source = factory.make_BootSource() |
240 | - params = { |
241 | - 'os': factory.make_name('os'), |
242 | - 'release': factory.make_name('release'), |
243 | - 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
244 | - 'subarches': [ |
245 | - factory.make_name('subarch'), factory.make_name('subarch')], |
246 | - 'labels': [factory.make_name('label'), factory.make_name('label')], |
247 | - } |
248 | + params = self.make_valid_source_selection_params(boot_source) |
249 | form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
250 | self.assertTrue(form.is_valid(), form._errors) |
251 | boot_source_selection = form.save() |
252 | @@ -58,15 +82,9 @@ |
253 | |
254 | def test_cannot_create_duplicate_entry(self): |
255 | boot_source = factory.make_BootSource() |
256 | - params = { |
257 | - 'os': factory.make_name('os'), |
258 | - 'release': factory.make_name('release'), |
259 | - 'arches': [factory.make_name('arch'), factory.make_name('arch')], |
260 | - 'subarches': [ |
261 | - factory.make_name('subarch'), factory.make_name('subarch')], |
262 | - 'labels': [factory.make_name('label'), factory.make_name('label')], |
263 | - } |
264 | - form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
265 | + params = self.make_valid_source_selection_params(boot_source) |
266 | + form = BootSourceSelectionForm( |
267 | + boot_source=boot_source, data=params) |
268 | self.assertTrue(form.is_valid(), form._errors) |
269 | form.save() |
270 | |
271 | @@ -79,3 +97,195 @@ |
272 | form = BootSourceSelectionForm( |
273 | boot_source=boot_source, data=dup_params) |
274 | self.assertRaises(ValidationError, form.save) |
275 | + |
276 | + def test_validates_if_boot_source_cache_has_same_os_and_release(self): |
277 | + boot_source = factory.make_BootSource() |
278 | + boot_cache = factory.make_BootSourceCache(boot_source) |
279 | + |
280 | + params = { |
281 | + 'os': boot_cache.os, |
282 | + 'release': boot_cache.release, |
283 | + } |
284 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
285 | + self.assertTrue(form.is_valid(), form._errors) |
286 | + |
287 | + def test_rejects_if_boot_source_cache_has_different_os(self): |
288 | + boot_source = factory.make_BootSource() |
289 | + boot_cache = factory.make_BootSourceCache(boot_source) |
290 | + |
291 | + params = { |
292 | + 'os': factory.make_name('os'), |
293 | + 'release': boot_cache.release, |
294 | + } |
295 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
296 | + self.assertFalse(form.is_valid()) |
297 | + self.assertEqual( |
298 | + { |
299 | + "os": [ |
300 | + "OS %s with release %s has no available images " |
301 | + "for download" % (params['os'], boot_cache.release) |
302 | + ] |
303 | + }, |
304 | + form._errors) |
305 | + |
306 | + def test_rejects_if_boot_source_cache_has_different_release(self): |
307 | + boot_source = factory.make_BootSource() |
308 | + boot_cache = factory.make_BootSourceCache(boot_source) |
309 | + |
310 | + params = { |
311 | + 'os': boot_cache.os, |
312 | + 'release': factory.make_name('release'), |
313 | + } |
314 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
315 | + self.assertFalse(form.is_valid()) |
316 | + self.assertEqual( |
317 | + { |
318 | + "os": [ |
319 | + "OS %s with release %s has no available images " |
320 | + "for download" % (boot_cache.os, params['release']) |
321 | + ] |
322 | + }, |
323 | + form._errors) |
324 | + |
325 | + def make_some_caches(self, boot_source, os, release): |
326 | + # Make a few BootSourceCache records that the following tests can use |
327 | + # to validate against when using BootSourceSelectionForm. |
328 | + return factory.make_many_BootSourceCaches( |
329 | + 3, boot_source=boot_source, os=os, release=release) |
330 | + |
331 | + def test_validates_if_boot_source_cache_has_arch(self): |
332 | + boot_source = factory.make_BootSource() |
333 | + os = factory.make_name('os') |
334 | + release = factory.make_name('release') |
335 | + boot_caches = self.make_some_caches(boot_source, os, release) |
336 | + |
337 | + # Request arches that are in two of the cache records. |
338 | + params = { |
339 | + 'os': os, |
340 | + 'release': release, |
341 | + 'arches': [boot_caches[0].arch, boot_caches[2].arch], |
342 | + } |
343 | + |
344 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
345 | + self.assertTrue(form.is_valid(), form._errors) |
346 | + |
347 | + def test_rejects_if_boot_source_cache_does_not_have_arch(self): |
348 | + boot_source = factory.make_BootSource() |
349 | + os = factory.make_name('os') |
350 | + release = factory.make_name('release') |
351 | + factory.make_BootSourceCache( |
352 | + boot_source, os=os, release=release) |
353 | + |
354 | + params = { |
355 | + 'os': os, |
356 | + 'release': release, |
357 | + 'arches': [factory.make_name('arch')], |
358 | + } |
359 | + |
360 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
361 | + self.assertFalse(form.is_valid()) |
362 | + self.assertEqual( |
363 | + { |
364 | + "arches": [ |
365 | + "No available images to download for %s" % |
366 | + params['arches'] |
367 | + ] |
368 | + }, |
369 | + form._errors) |
370 | + |
371 | + def test_validates_if_boot_source_cache_has_subarch(self): |
372 | + boot_source = factory.make_BootSource() |
373 | + os = factory.make_name('os') |
374 | + release = factory.make_name('release') |
375 | + boot_caches = self.make_some_caches(boot_source, os, release) |
376 | + |
377 | + # Request subarches that are in two of the cache records. |
378 | + params = { |
379 | + 'os': os, |
380 | + 'release': release, |
381 | + 'subarches': [boot_caches[0].subarch, boot_caches[2].subarch], |
382 | + } |
383 | + |
384 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
385 | + self.assertTrue(form.is_valid(), form._errors) |
386 | + |
387 | + def test_rejects_if_boot_source_cache_does_not_have_subarch(self): |
388 | + boot_source = factory.make_BootSource() |
389 | + os = factory.make_name('os') |
390 | + release = factory.make_name('release') |
391 | + factory.make_BootSourceCache( |
392 | + boot_source, os=os, release=release) |
393 | + |
394 | + params = { |
395 | + 'os': os, |
396 | + 'release': release, |
397 | + 'subarches': [factory.make_name('subarch')], |
398 | + } |
399 | + |
400 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
401 | + self.assertFalse(form.is_valid()) |
402 | + self.assertEqual( |
403 | + { |
404 | + "subarches": [ |
405 | + "No available images to download for %s" % |
406 | + params['subarches'] |
407 | + ] |
408 | + }, |
409 | + form._errors) |
410 | + |
411 | + def test_validates_if_boot_source_cache_has_label(self): |
412 | + boot_source = factory.make_BootSource() |
413 | + os = factory.make_name('os') |
414 | + release = factory.make_name('release') |
415 | + boot_caches = self.make_some_caches(boot_source, os, release) |
416 | + |
417 | + # Request labels that are in two of the cache records. |
418 | + params = { |
419 | + 'os': os, |
420 | + 'release': release, |
421 | + 'labels': [boot_caches[0].label, boot_caches[2].label], |
422 | + } |
423 | + |
424 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
425 | + self.assertTrue(form.is_valid(), form._errors) |
426 | + |
427 | + def test_rejects_if_boot_source_cache_does_not_have_label(self): |
428 | + boot_source = factory.make_BootSource() |
429 | + os = factory.make_name('os') |
430 | + release = factory.make_name('release') |
431 | + factory.make_BootSourceCache( |
432 | + boot_source, os=os, release=release) |
433 | + |
434 | + params = { |
435 | + 'os': os, |
436 | + 'release': release, |
437 | + 'labels': [factory.make_name('label')], |
438 | + } |
439 | + |
440 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
441 | + self.assertFalse(form.is_valid()) |
442 | + self.assertEqual( |
443 | + { |
444 | + "labels": [ |
445 | + "No available images to download for %s" % |
446 | + params['labels'] |
447 | + ] |
448 | + }, |
449 | + form._errors) |
450 | + |
451 | + def test_star_values_in_request_validate_against_any_cache(self): |
452 | + boot_source = factory.make_BootSource() |
453 | + os = factory.make_name('os') |
454 | + release = factory.make_name('release') |
455 | + factory.make_BootSourceCache( |
456 | + boot_source, os=os, release=release) |
457 | + params = { |
458 | + 'os': os, |
459 | + 'release': release, |
460 | + 'arches': ['*'], |
461 | + 'subarches': ['*'], |
462 | + 'labels': ['*'], |
463 | + } |
464 | + |
465 | + form = BootSourceSelectionForm(boot_source=boot_source, data=params) |
466 | + self.assertTrue(form.is_valid(), form._errors) |
Looks good. There's a don't-make-me-think nit inline, but otherwise this is fine.