Merge lp:~abentley/launchpad/bad-branch-name into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Approved by: Māris Fogels
Approved revision: no longer in the source branch.
Merged at revision: 11015
Proposed branch: lp:~abentley/launchpad/bad-branch-name
Merge into: lp:launchpad
Diff against target: 141 lines (+55/-19)
3 files modified
lib/lp/code/browser/sourcepackagerecipe.py (+5/-0)
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+48/-19)
lib/lp/code/model/sourcepackagerecipedata.py (+2/-0)
To merge this branch: bzr merge lp:~abentley/launchpad/bad-branch-name
Reviewer Review Type Date Requested Status
Māris Fogels (community) Approve
Review via email: mp+27549@code.launchpad.net

Commit message

Treat bad branch location in recipe as field error

Description of the change

= Summary =
Fix bug #592792: bad branch names in recipes produce oopses

== Proposed fix ==
Catch NoSuchBranch when creating the SourcePackageRecipe and set a suitable
field error.

== Pre-implementation notes ==
None

== Implementation details ==
Extracted createRecipe so it could be reused.

Implemented bad branch handling for SourcePackageRecipeDataInstruction.

== Tests ==
bin/test -t test_create_recipe_bad_base_branch -t test_create_recipe_bad_instruction_branch

== Demo and Q/A ==
Attempt to create a recipe for a base branch that doesn't exist. This should
produce a validation error.

Attempt to create a recipe that merges a branchh that doesn't exist. This
should produce a validation error.

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/code/model/sourcepackagerecipedata.py
  lib/lp/code/browser/sourcepackagerecipe.py
  lib/lp/code/browser/tests/test_sourcepackagerecipe.py

== Pyflakes notices ==

lib/lp/code/browser/tests/test_sourcepackagerecipe.py
    283: local variable 'recipe_fings' is assigned to but never used

To post a comment you must log in.
Revision history for this message
Māris Fogels (mars) wrote :

Hi Aaron,

This change looks good. Some questions:

  * Would the text "No such branch: foo" be clearer for our users than "Unknown branch: foo"? I personally find the meaning of "No such X" clearer than "Unknown X".

  * I see two convoluted calls to "extract_text(find_tags_by_class(browser.contents, 'message')[1])" in this diff. Should it be extracted into a nice helper function like get_page_message()?

Otherwise, this looks good to me. r=mars

Maris

review: Approve
Revision history for this message
Aaron Bentley (abentley) wrote :

Updated per our discussion.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/browser/sourcepackagerecipe.py'
--- lib/lp/code/browser/sourcepackagerecipe.py 2010-06-12 13:34:11 +0000
+++ lib/lp/code/browser/sourcepackagerecipe.py 2010-06-15 15:10:56 +0000
@@ -40,6 +40,7 @@
40from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget40from canonical.widgets.itemswidgets import LabeledMultiCheckBoxWidget
41from lp.buildmaster.interfaces.buildbase import BuildStatus41from lp.buildmaster.interfaces.buildbase import BuildStatus
42from lp.code.errors import ForbiddenInstruction42from lp.code.errors import ForbiddenInstruction
43from lp.code.interfaces.branch import NoSuchBranch
43from lp.code.interfaces.sourcepackagerecipe import (44from lp.code.interfaces.sourcepackagerecipe import (
44 ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT)45 ISourcePackageRecipe, ISourcePackageRecipeSource, MINIMAL_RECIPE_TEXT)
45from lp.code.interfaces.sourcepackagerecipebuild import (46from lp.code.interfaces.sourcepackagerecipebuild import (
@@ -364,6 +365,10 @@
364 'recipe_text',365 'recipe_text',
365 'The bzr-builder instruction "run" is not permitted here.')366 'The bzr-builder instruction "run" is not permitted here.')
366 return367 return
368 except NoSuchBranch, e:
369 self.setFieldError(
370 'recipe_text', '%s is not a branch on Launchpad.' % e.name)
371 return
367372
368 self.next_url = canonical_url(source_package_recipe)373 self.next_url = canonical_url(source_package_recipe)
369374
370375
=== modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py'
--- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-12 13:52:31 +0000
+++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2010-06-15 15:10:56 +0000
@@ -65,6 +65,12 @@
65 return extract_text(find_main_content(browser.contents))65 return extract_text(find_main_content(browser.contents))
6666
6767
68def get_message_text(browser, index):
69 """Return the text of a message, specified by index."""
70 tags = find_tags_by_class(browser.contents, 'message')[index]
71 return extract_text(tags)
72
73
68class TestSourcePackageRecipeAddView(TestCaseForRecipe):74class TestSourcePackageRecipeAddView(TestCaseForRecipe):
6975
70 layer = DatabaseFunctionalLayer76 layer = DatabaseFunctionalLayer
@@ -142,7 +148,7 @@
142 browser.getControl('Create Recipe').click()148 browser.getControl('Create Recipe').click()
143149
144 self.assertEqual(150 self.assertEqual(
145 extract_text(find_tags_by_class(browser.contents, 'message')[1]),151 get_message_text(browser, 1),
146 'The bzr-builder instruction "run" is not permitted here.')152 'The bzr-builder instruction "run" is not permitted here.')
147153
148 def test_create_new_recipe_empty_name(self):154 def test_create_new_recipe_empty_name(self):
@@ -162,30 +168,53 @@
162 browser.getControl('Create Recipe').click()168 browser.getControl('Create Recipe').click()
163169
164 self.assertEqual(170 self.assertEqual(
165 extract_text(find_tags_by_class(browser.contents, 'message')[1]),171 get_message_text(browser, 1), 'Required input is missing.')
166 'Required input is missing.')172
173 def createRecipe(self, recipe_text, branch=None):
174 if branch is None:
175 product = self.factory.makeProduct(
176 name='ratatouille', displayname='Ratatouille')
177 branch = self.factory.makeBranch(
178 owner=self.chef, product=product, name='veggies')
179
180 # A new recipe can be created from the branch page.
181 browser = self.getUserBrowser(canonical_url(branch), user=self.chef)
182 browser.getLink('Create packaging recipe').click()
183
184 browser.getControl(name='field.name').value = 'daily'
185 browser.getControl('Description').value = 'Make some food!'
186 browser.getControl('Recipe text').value = recipe_text
187 browser.getControl('Create Recipe').click()
188 return browser
167189
168 def test_create_recipe_bad_text(self):190 def test_create_recipe_bad_text(self):
169 # If a user tries to create source package recipe with bad text, they191 # If a user tries to create source package recipe with bad text, they
170 # should get an error.192 # should get an error.
171 product = self.factory.makeProduct(193 browser = self.createRecipe('Foo bar baz')
172 name='ratatouille', displayname='Ratatouille')
173 branch = self.factory.makeBranch(
174 owner=self.chef, product=product, name='veggies')
175
176 # A new recipe can be created from the branch page.
177 browser = self.getUserBrowser(canonical_url(branch), user=self.chef)
178 browser.getLink('Create packaging recipe').click()
179
180 browser.getControl(name='field.name').value = 'daily'
181 browser.getControl('Description').value = 'Make some food!'
182 browser.getControl('Recipe text').value = 'Foo bar baz'
183 browser.getControl('Create Recipe').click()
184
185 self.assertEqual(194 self.assertEqual(
186 extract_text(find_tags_by_class(browser.contents, 'message')[1]),195 get_message_text(browser, 1),
187 'The recipe text is not a valid bzr-builder recipe.')196 'The recipe text is not a valid bzr-builder recipe.')
188197
198 def test_create_recipe_bad_base_branch(self):
199 # If a user tries to create source package recipe with a bad base
200 # branch location, they should get an error.
201 browser = self.createRecipe(MINIMAL_RECIPE_TEXT % 'foo')
202 self.assertEqual(
203 get_message_text(browser, 1), 'foo is not a branch on Launchpad.')
204
205 def test_create_recipe_bad_instruction_branch(self):
206 # If a user tries to create source package recipe with a bad
207 # instruction branch location, they should get an error.
208 product = self.factory.makeProduct(
209 name='ratatouille', displayname='Ratatouille')
210 branch = self.factory.makeBranch(
211 owner=self.chef, product=product, name='veggies')
212 recipe = MINIMAL_RECIPE_TEXT % branch.bzr_identity
213 recipe += 'nest packaging foo debian'
214 browser = self.createRecipe(recipe, branch)
215 self.assertEqual(
216 get_message_text(browser, 1), 'foo is not a branch on Launchpad.')
217
189 def test_create_dupe_recipe(self):218 def test_create_dupe_recipe(self):
190 # You shouldn't be able to create a duplicate recipe owned by the same219 # You shouldn't be able to create a duplicate recipe owned by the same
191 # person with the same name.220 # person with the same name.
@@ -206,7 +235,7 @@
206 browser.getControl('Create Recipe').click()235 browser.getControl('Create Recipe').click()
207236
208 self.assertEqual(237 self.assertEqual(
209 extract_text(find_tags_by_class(browser.contents, 'message')[1]),238 get_message_text(browser, 1),
210 'There is already a recipe owned by Master Chef with this name.')239 'There is already a recipe owned by Master Chef with this name.')
211240
212241
213242
=== modified file 'lib/lp/code/model/sourcepackagerecipedata.py'
--- lib/lp/code/model/sourcepackagerecipedata.py 2010-06-11 04:28:38 +0000
+++ lib/lp/code/model/sourcepackagerecipedata.py 2010-06-15 15:10:56 +0000
@@ -211,6 +211,8 @@
211 line_number += 1211 line_number += 1
212 comment = None212 comment = None
213 db_branch = branch_map[instruction.recipe_branch.url]213 db_branch = branch_map[instruction.recipe_branch.url]
214 if db_branch is None:
215 raise NoSuchBranch(instruction.recipe_branch.url)
214 insn = _SourcePackageRecipeDataInstruction(216 insn = _SourcePackageRecipeDataInstruction(
215 instruction.recipe_branch.name, type, comment,217 instruction.recipe_branch.name, type, comment,
216 line_number, db_branch, instruction.recipe_branch.revspec,218 line_number, db_branch, instruction.recipe_branch.revspec,