Merge lp:~cjwatson/launchpad/recipe-ambiguous-vcs into lp:launchpad
- recipe-ambiguous-vcs
- Merge into devel
Proposed by
Colin Watson
Status: | Merged |
---|---|
Merged at revision: | 18737 |
Proposed branch: | lp:~cjwatson/launchpad/recipe-ambiguous-vcs |
Merge into: | lp:launchpad |
Diff against target: |
290 lines (+92/-35) 6 files modified
lib/lp/code/browser/tests/test_sourcepackagerecipe.py (+2/-5) lib/lp/code/interfaces/sourcepackagerecipe.py (+17/-2) lib/lp/code/model/sourcepackagerecipe.py (+6/-5) lib/lp/code/model/sourcepackagerecipebuild.py (+3/-2) lib/lp/code/model/sourcepackagerecipedata.py (+25/-19) lib/lp/code/model/tests/test_sourcepackagerecipe.py (+39/-2) |
To merge this branch: | bzr merge lp:~cjwatson/launchpad/recipe-ambiguous-vcs |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+350699@code.launchpad.net |
Commit message
Handle the case where a Bazaar branch and a Git repository have the same identity URL when creating a recipe.
Description of the change
There are still various bodges because we're using bzr-builder to parse git-build-recipe recipes, but passing through the type should smooth over the rough edges.
To post a comment you must log in.
Revision history for this message
William Grant (wgrant) : | # |
review:
Approve
(code)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/code/browser/tests/test_sourcepackagerecipe.py' | |||
2 | --- lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2018-01-02 16:10:26 +0000 | |||
3 | +++ lib/lp/code/browser/tests/test_sourcepackagerecipe.py 2018-07-23 19:40:00 +0000 | |||
4 | @@ -1,4 +1,4 @@ | |||
6 | 1 | # Copyright 2010-2017 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2010-2018 Canonical Ltd. This software is licensed under the |
7 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
8 | 3 | 3 | ||
9 | 4 | """Tests for the source package recipe view classes and templates.""" | 4 | """Tests for the source package recipe view classes and templates.""" |
10 | @@ -551,12 +551,9 @@ | |||
11 | 551 | # branch location, they should get an error. | 551 | # branch location, they should get an error. |
12 | 552 | browser = self.createRecipe( | 552 | browser = self.createRecipe( |
13 | 553 | self.minimal_recipe_text.splitlines()[0] + '\nfoo\n') | 553 | self.minimal_recipe_text.splitlines()[0] + '\nfoo\n') |
14 | 554 | # This page doesn't know whether the user was aiming for a Bazaar | ||
15 | 555 | # branch or a Git repository; the error message always says | ||
16 | 556 | # "branch". | ||
17 | 557 | self.assertEqual( | 554 | self.assertEqual( |
18 | 558 | get_feedback_messages(browser.contents)[1], | 555 | get_feedback_messages(browser.contents)[1], |
20 | 559 | 'foo is not a branch on Launchpad.') | 556 | 'foo %s' % self.no_such_object_message) |
21 | 560 | 557 | ||
22 | 561 | def test_create_recipe_bad_instruction_branch(self): | 558 | def test_create_recipe_bad_instruction_branch(self): |
23 | 562 | # If a user tries to create source package recipe with a bad | 559 | # If a user tries to create source package recipe with a bad |
24 | 563 | 560 | ||
25 | === modified file 'lib/lp/code/interfaces/sourcepackagerecipe.py' | |||
26 | --- lib/lp/code/interfaces/sourcepackagerecipe.py 2017-06-16 10:02:47 +0000 | |||
27 | +++ lib/lp/code/interfaces/sourcepackagerecipe.py 2018-07-23 19:40:00 +0000 | |||
28 | @@ -1,4 +1,4 @@ | |||
30 | 1 | # Copyright 2009-2017 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
31 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
32 | 3 | 3 | ||
33 | 4 | """Interface of the `SourcePackageRecipe` content type.""" | 4 | """Interface of the `SourcePackageRecipe` content type.""" |
34 | @@ -15,11 +15,16 @@ | |||
35 | 15 | 'ISourcePackageRecipeSource', | 15 | 'ISourcePackageRecipeSource', |
36 | 16 | 'MINIMAL_RECIPE_TEXT_BZR', | 16 | 'MINIMAL_RECIPE_TEXT_BZR', |
37 | 17 | 'MINIMAL_RECIPE_TEXT_GIT', | 17 | 'MINIMAL_RECIPE_TEXT_GIT', |
38 | 18 | 'RecipeBranchType', | ||
39 | 18 | ] | 19 | ] |
40 | 19 | 20 | ||
41 | 20 | 21 | ||
42 | 21 | from textwrap import dedent | 22 | from textwrap import dedent |
43 | 22 | 23 | ||
44 | 24 | from lazr.enum import ( | ||
45 | 25 | EnumeratedType, | ||
46 | 26 | Item, | ||
47 | 27 | ) | ||
48 | 23 | from lazr.lifecycle.snapshot import doNotSnapshot | 28 | from lazr.lifecycle.snapshot import doNotSnapshot |
49 | 24 | from lazr.restful.declarations import ( | 29 | from lazr.restful.declarations import ( |
50 | 25 | call_with, | 30 | call_with, |
51 | @@ -105,13 +110,23 @@ | |||
52 | 105 | """An iterator of the branches referenced by this recipe.""" | 110 | """An iterator of the branches referenced by this recipe.""" |
53 | 106 | 111 | ||
54 | 107 | 112 | ||
55 | 113 | class RecipeBranchType(EnumeratedType): | ||
56 | 114 | """The revision control system used for a recipe.""" | ||
57 | 115 | |||
58 | 116 | BZR = Item("Bazaar") | ||
59 | 117 | |||
60 | 118 | GIT = Item("Git") | ||
61 | 119 | |||
62 | 120 | |||
63 | 108 | class IRecipeBranchSource(Interface): | 121 | class IRecipeBranchSource(Interface): |
64 | 109 | 122 | ||
65 | 110 | def getParsedRecipe(recipe_text): | 123 | def getParsedRecipe(recipe_text): |
66 | 111 | """Parse recipe text into recipe data. | 124 | """Parse recipe text into recipe data. |
67 | 112 | 125 | ||
68 | 113 | :param recipe_text: Recipe text as a string. | 126 | :param recipe_text: Recipe text as a string. |
70 | 114 | :return: a `RecipeBranch` representing the recipe. | 127 | :return: a tuple of a `RecipeBranch` representing the recipe and a |
71 | 128 | `RecipeBranchType` indicating the revision control system to be | ||
72 | 129 | used for the recipe. | ||
73 | 115 | """ | 130 | """ |
74 | 116 | 131 | ||
75 | 117 | 132 | ||
76 | 118 | 133 | ||
77 | === modified file 'lib/lp/code/model/sourcepackagerecipe.py' | |||
78 | --- lib/lp/code/model/sourcepackagerecipe.py 2018-05-09 09:23:36 +0000 | |||
79 | +++ lib/lp/code/model/sourcepackagerecipe.py 2018-07-23 19:40:00 +0000 | |||
80 | @@ -184,8 +184,9 @@ | |||
81 | 184 | owner_ids, need_validity=True)) | 184 | owner_ids, need_validity=True)) |
82 | 185 | 185 | ||
83 | 186 | def setRecipeText(self, recipe_text): | 186 | def setRecipeText(self, recipe_text): |
86 | 187 | parsed = getUtility(IRecipeBranchSource).getParsedRecipe(recipe_text) | 187 | parsed, recipe_branch_type = ( |
87 | 188 | self._recipe_data.setRecipe(parsed) | 188 | getUtility(IRecipeBranchSource).getParsedRecipe(recipe_text)) |
88 | 189 | self._recipe_data.setRecipe(parsed, recipe_branch_type) | ||
89 | 189 | 190 | ||
90 | 190 | def getRecipeText(self, validate=False): | 191 | def getRecipeText(self, validate=False): |
91 | 191 | """See `ISourcePackageRecipe`.""" | 192 | """See `ISourcePackageRecipe`.""" |
92 | @@ -215,9 +216,9 @@ | |||
93 | 215 | """See `ISourcePackageRecipeSource.new`.""" | 216 | """See `ISourcePackageRecipeSource.new`.""" |
94 | 216 | store = IMasterStore(SourcePackageRecipe) | 217 | store = IMasterStore(SourcePackageRecipe) |
95 | 217 | sprecipe = SourcePackageRecipe() | 218 | sprecipe = SourcePackageRecipe() |
99 | 218 | builder_recipe = getUtility(IRecipeBranchSource).getParsedRecipe( | 219 | builder_recipe, recipe_branch_type = ( |
100 | 219 | recipe) | 220 | getUtility(IRecipeBranchSource).getParsedRecipe(recipe)) |
101 | 220 | SourcePackageRecipeData(builder_recipe, sprecipe) | 221 | SourcePackageRecipeData(builder_recipe, recipe_branch_type, sprecipe) |
102 | 221 | sprecipe.registrant = registrant | 222 | sprecipe.registrant = registrant |
103 | 222 | sprecipe.owner = owner | 223 | sprecipe.owner = owner |
104 | 223 | sprecipe.name = name | 224 | sprecipe.name = name |
105 | 224 | 225 | ||
106 | === modified file 'lib/lp/code/model/sourcepackagerecipebuild.py' | |||
107 | --- lib/lp/code/model/sourcepackagerecipebuild.py 2016-08-12 12:56:41 +0000 | |||
108 | +++ lib/lp/code/model/sourcepackagerecipebuild.py 2018-07-23 19:40:00 +0000 | |||
109 | @@ -1,4 +1,4 @@ | |||
111 | 1 | # Copyright 2010-2016 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2010-2018 Canonical Ltd. This software is licensed under the |
112 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
113 | 3 | 3 | ||
114 | 4 | """Implementation code for source package builds.""" | 4 | """Implementation code for source package builds.""" |
115 | @@ -164,8 +164,9 @@ | |||
116 | 164 | getUtility(ISourcePackageRecipeDataSource).createManifestFromText( | 164 | getUtility(ISourcePackageRecipeDataSource).createManifestFromText( |
117 | 165 | text, self) | 165 | text, self) |
118 | 166 | else: | 166 | else: |
120 | 167 | self.manifest.setRecipe( | 167 | parsed, recipe_branch_type = ( |
121 | 168 | getUtility(IRecipeBranchSource).getParsedRecipe(text)) | 168 | getUtility(IRecipeBranchSource).getParsedRecipe(text)) |
122 | 169 | self.manifest.setRecipe(parsed, recipe_branch_type) | ||
123 | 169 | 170 | ||
124 | 170 | def getManifestText(self): | 171 | def getManifestText(self): |
125 | 171 | if self.manifest is None: | 172 | if self.manifest is None: |
126 | 172 | 173 | ||
127 | === modified file 'lib/lp/code/model/sourcepackagerecipedata.py' | |||
128 | --- lib/lp/code/model/sourcepackagerecipedata.py 2016-02-06 02:20:04 +0000 | |||
129 | +++ lib/lp/code/model/sourcepackagerecipedata.py 2018-07-23 19:40:00 +0000 | |||
130 | @@ -1,4 +1,4 @@ | |||
132 | 1 | # Copyright 2009-2016 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2018 Canonical Ltd. This software is licensed under the |
133 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
134 | 3 | 3 | ||
135 | 4 | """Implementation of the recipe storage. | 4 | """Implementation of the recipe storage. |
136 | @@ -61,6 +61,7 @@ | |||
137 | 61 | IRecipeBranchSource, | 61 | IRecipeBranchSource, |
138 | 62 | ISourcePackageRecipeData, | 62 | ISourcePackageRecipeData, |
139 | 63 | ISourcePackageRecipeDataSource, | 63 | ISourcePackageRecipeDataSource, |
140 | 64 | RecipeBranchType, | ||
141 | 64 | ) | 65 | ) |
142 | 65 | from lp.code.model.branch import Branch | 66 | from lp.code.model.branch import Branch |
143 | 66 | from lp.code.model.gitrepository import GitRepository | 67 | from lp.code.model.gitrepository import GitRepository |
144 | @@ -239,10 +240,14 @@ | |||
145 | 239 | # formats are mostly compatible, the header line must say | 240 | # formats are mostly compatible, the header line must say |
146 | 240 | # "bzr-builder" even though git-build-recipe also supports its own | 241 | # "bzr-builder" even though git-build-recipe also supports its own |
147 | 241 | # name there. | 242 | # name there. |
149 | 242 | recipe_text = re.sub( | 243 | recipe_text, git_substitutions = re.subn( |
150 | 243 | r"^(#\s*)git-build-recipe", r"\1bzr-builder", recipe_text) | 244 | r"^(#\s*)git-build-recipe", r"\1bzr-builder", recipe_text) |
151 | 245 | recipe_branch_type = ( | ||
152 | 246 | RecipeBranchType.GIT if git_substitutions | ||
153 | 247 | else RecipeBranchType.BZR) | ||
154 | 244 | parser = RecipeParser(recipe_text) | 248 | parser = RecipeParser(recipe_text) |
156 | 245 | return parser.parse(permitted_instructions=SAFE_INSTRUCTIONS) | 249 | recipe_branch = parser.parse(permitted_instructions=SAFE_INSTRUCTIONS) |
157 | 250 | return recipe_branch, recipe_branch_type | ||
158 | 246 | 251 | ||
159 | 247 | @staticmethod | 252 | @staticmethod |
160 | 248 | def findRecipes(branch_or_repository, revspecs=None): | 253 | def findRecipes(branch_or_repository, revspecs=None): |
161 | @@ -306,9 +311,10 @@ | |||
162 | 306 | @classmethod | 311 | @classmethod |
163 | 307 | def createManifestFromText(cls, text, sourcepackage_recipe_build): | 312 | def createManifestFromText(cls, text, sourcepackage_recipe_build): |
164 | 308 | """See `ISourcePackageRecipeDataSource`.""" | 313 | """See `ISourcePackageRecipeDataSource`.""" |
166 | 309 | parsed = cls.getParsedRecipe(text) | 314 | parsed, recipe_branch_type = cls.getParsedRecipe(text) |
167 | 310 | return cls( | 315 | return cls( |
169 | 311 | parsed, sourcepackage_recipe_build=sourcepackage_recipe_build) | 316 | parsed, recipe_branch_type, |
170 | 317 | sourcepackage_recipe_build=sourcepackage_recipe_build) | ||
171 | 312 | 318 | ||
172 | 313 | def getRecipe(self): | 319 | def getRecipe(self): |
173 | 314 | """The BaseRecipeBranch version of the recipe.""" | 320 | """The BaseRecipeBranch version of the recipe.""" |
174 | @@ -388,26 +394,26 @@ | |||
175 | 388 | instruction.recipe_branch, insn, branch_map, line_number) | 394 | instruction.recipe_branch, insn, branch_map, line_number) |
176 | 389 | return line_number | 395 | return line_number |
177 | 390 | 396 | ||
179 | 391 | def setRecipe(self, builder_recipe): | 397 | def setRecipe(self, builder_recipe, recipe_branch_type): |
180 | 392 | """Convert the BaseRecipeBranch `builder_recipe` to the db form.""" | 398 | """Convert the BaseRecipeBranch `builder_recipe` to the db form.""" |
181 | 393 | clear_property_cache(self) | 399 | clear_property_cache(self) |
182 | 394 | if builder_recipe.format > MAX_RECIPE_FORMAT: | 400 | if builder_recipe.format > MAX_RECIPE_FORMAT: |
183 | 395 | raise TooNewRecipeFormat(builder_recipe.format, MAX_RECIPE_FORMAT) | 401 | raise TooNewRecipeFormat(builder_recipe.format, MAX_RECIPE_FORMAT) |
186 | 396 | base = getUtility(IBranchLookup).getByUrl(builder_recipe.url) | 402 | if recipe_branch_type == RecipeBranchType.BZR: |
187 | 397 | if base is None: | 403 | base = getUtility(IBranchLookup).getByUrl(builder_recipe.url) |
188 | 404 | if base is None: | ||
189 | 405 | raise NoSuchBranch(builder_recipe.url) | ||
190 | 406 | elif base.private: | ||
191 | 407 | raise PrivateBranchRecipe(base) | ||
192 | 408 | elif recipe_branch_type == RecipeBranchType.GIT: | ||
193 | 398 | base = getUtility(IGitLookup).getByUrl(builder_recipe.url) | 409 | base = getUtility(IGitLookup).getByUrl(builder_recipe.url) |
194 | 399 | if base is None: | 410 | if base is None: |
202 | 400 | # If possible, try to raise an exception consistent with | 411 | raise NoSuchGitRepository(builder_recipe.url) |
196 | 401 | # whether the current recipe is Bazaar-based or Git-based, | ||
197 | 402 | # so that error messages make more sense. | ||
198 | 403 | if self.base_git_repository is not None: | ||
199 | 404 | raise NoSuchGitRepository(builder_recipe.url) | ||
200 | 405 | else: | ||
201 | 406 | raise NoSuchBranch(builder_recipe.url) | ||
203 | 407 | elif base.private: | 412 | elif base.private: |
204 | 408 | raise PrivateGitRepositoryRecipe(base) | 413 | raise PrivateGitRepositoryRecipe(base) |
207 | 409 | elif base.private: | 414 | else: |
208 | 410 | raise PrivateBranchRecipe(base) | 415 | raise AssertionError( |
209 | 416 | 'Unknown recipe_branch_type: %r' % recipe_branch_type) | ||
210 | 411 | branch_map = self._scanInstructions(base, builder_recipe) | 417 | branch_map = self._scanInstructions(base, builder_recipe) |
211 | 412 | # If this object hasn't been added to a store yet, there can't be any | 418 | # If this object hasn't been added to a store yet, there can't be any |
212 | 413 | # instructions linking to us yet. | 419 | # instructions linking to us yet. |
213 | @@ -426,12 +432,12 @@ | |||
214 | 426 | self.deb_version_template = unicode(builder_recipe.deb_version) | 432 | self.deb_version_template = unicode(builder_recipe.deb_version) |
215 | 427 | self.recipe_format = unicode(builder_recipe.format) | 433 | self.recipe_format = unicode(builder_recipe.format) |
216 | 428 | 434 | ||
218 | 429 | def __init__(self, recipe, sourcepackage_recipe=None, | 435 | def __init__(self, recipe, recipe_branch_type, sourcepackage_recipe=None, |
219 | 430 | sourcepackage_recipe_build=None): | 436 | sourcepackage_recipe_build=None): |
220 | 431 | """Initialize from the bzr-builder recipe and link it to a db recipe. | 437 | """Initialize from the bzr-builder recipe and link it to a db recipe. |
221 | 432 | """ | 438 | """ |
222 | 433 | super(SourcePackageRecipeData, self).__init__() | 439 | super(SourcePackageRecipeData, self).__init__() |
224 | 434 | self.setRecipe(recipe) | 440 | self.setRecipe(recipe, recipe_branch_type) |
225 | 435 | self.sourcepackage_recipe = sourcepackage_recipe | 441 | self.sourcepackage_recipe = sourcepackage_recipe |
226 | 436 | self.sourcepackage_recipe_build = sourcepackage_recipe_build | 442 | self.sourcepackage_recipe_build = sourcepackage_recipe_build |
227 | 437 | 443 | ||
228 | 438 | 444 | ||
229 | === modified file 'lib/lp/code/model/tests/test_sourcepackagerecipe.py' | |||
230 | --- lib/lp/code/model/tests/test_sourcepackagerecipe.py 2018-03-02 01:11:48 +0000 | |||
231 | +++ lib/lp/code/model/tests/test_sourcepackagerecipe.py 2018-07-23 19:40:00 +0000 | |||
232 | @@ -37,6 +37,8 @@ | |||
233 | 37 | PrivateGitRepositoryRecipe, | 37 | PrivateGitRepositoryRecipe, |
234 | 38 | TooNewRecipeFormat, | 38 | TooNewRecipeFormat, |
235 | 39 | ) | 39 | ) |
236 | 40 | from lp.code.interfaces.gitrepository import IGitRepositorySet | ||
237 | 41 | from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch | ||
238 | 40 | from lp.code.interfaces.sourcepackagerecipe import ( | 42 | from lp.code.interfaces.sourcepackagerecipe import ( |
239 | 41 | ISourcePackageRecipe, | 43 | ISourcePackageRecipe, |
240 | 42 | ISourcePackageRecipeSource, | 44 | ISourcePackageRecipeSource, |
241 | @@ -1098,12 +1100,47 @@ | |||
242 | 1098 | 1100 | ||
243 | 1099 | class TestRecipeBranchRoundTrippingBzr( | 1101 | class TestRecipeBranchRoundTrippingBzr( |
244 | 1100 | TestRecipeBranchRoundTrippingMixin, BzrMixin, TestCaseWithFactory): | 1102 | TestRecipeBranchRoundTrippingMixin, BzrMixin, TestCaseWithFactory): |
246 | 1101 | pass | 1103 | |
247 | 1104 | def test_builds_recipe_with_ambiguous_git_repository(self): | ||
248 | 1105 | # Arrange for Bazaar and Git prefixes to match. | ||
249 | 1106 | self.pushConfig('codehosting', bzr_lp_prefix='lp:', lp_url_hosts='') | ||
250 | 1107 | project = self.base_branch.product | ||
251 | 1108 | repository = self.factory.makeGitRepository(target=project) | ||
252 | 1109 | with person_logged_in(project.owner): | ||
253 | 1110 | ICanHasLinkedBranch(project).setBranch(self.base_branch) | ||
254 | 1111 | getUtility(IGitRepositorySet).setDefaultRepository( | ||
255 | 1112 | project, repository) | ||
256 | 1113 | clear_property_cache(self.base_branch) | ||
257 | 1114 | recipe_text = '''\ | ||
258 | 1115 | # %(recipe_id)s format 0.3 deb-version 0.1-{revno} | ||
259 | 1116 | %(base)s | ||
260 | 1117 | ''' % {'recipe_id': self.recipe_id, 'base': self.base_branch.identity} | ||
261 | 1118 | recipe = self.get_recipe(recipe_text) | ||
262 | 1119 | self.assertEqual(self.base_branch, recipe.base_branch) | ||
263 | 1102 | 1120 | ||
264 | 1103 | 1121 | ||
265 | 1104 | class TestRecipeBranchRoundTrippingGit( | 1122 | class TestRecipeBranchRoundTrippingGit( |
266 | 1105 | TestRecipeBranchRoundTrippingMixin, GitMixin, TestCaseWithFactory): | 1123 | TestRecipeBranchRoundTrippingMixin, GitMixin, TestCaseWithFactory): |
268 | 1106 | pass | 1124 | |
269 | 1125 | def test_builds_recipe_with_ambiguous_bzr_branch(self): | ||
270 | 1126 | # Arrange for Bazaar and Git prefixes to match. | ||
271 | 1127 | self.pushConfig('codehosting', bzr_lp_prefix='lp:', lp_url_hosts='') | ||
272 | 1128 | project = self.base_branch.target | ||
273 | 1129 | branch = self.factory.makeBranch(product=project) | ||
274 | 1130 | with person_logged_in(project.owner): | ||
275 | 1131 | ICanHasLinkedBranch(project).setBranch(branch) | ||
276 | 1132 | getUtility(IGitRepositorySet).setDefaultRepository( | ||
277 | 1133 | project, self.base_branch.repository) | ||
278 | 1134 | recipe_text = '''\ | ||
279 | 1135 | # %(recipe_id)s format 0.3 deb-version 0.1-{revno} | ||
280 | 1136 | %(base)s | ||
281 | 1137 | ''' % { | ||
282 | 1138 | 'recipe_id': self.recipe_id, | ||
283 | 1139 | 'base': self.base_branch.repository.identity, | ||
284 | 1140 | } | ||
285 | 1141 | recipe = self.get_recipe(recipe_text) | ||
286 | 1142 | self.assertEqual( | ||
287 | 1143 | self.base_branch.repository, recipe.base_git_repository) | ||
288 | 1107 | 1144 | ||
289 | 1108 | 1145 | ||
290 | 1109 | class RecipeDateLastModified(TestCaseWithFactory): | 1146 | class RecipeDateLastModified(TestCaseWithFactory): |