Merge ~ilasc/launchpad:call-download-api-without-file-id into launchpad:master

Proposed by Ioana Lasc
Status: Merged
Approved by: Ioana Lasc
Approved revision: 06bfe18c19e6212fc0186d1dab2efaf7256e73a0
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~ilasc/launchpad:call-download-api-without-file-id
Merge into: launchpad:master
Diff against target: 819 lines (+59/-420)
10 files modified
lib/lp/code/errors.py (+1/-8)
lib/lp/code/interfaces/branchhosting.py (+2/-18)
lib/lp/code/model/branch.py (+1/-62)
lib/lp/code/model/branchhosting.py (+4/-29)
lib/lp/code/model/tests/test_branch.py (+6/-149)
lib/lp/code/model/tests/test_branchhosting.py (+21/-77)
lib/lp/code/tests/helpers.py (+0/-14)
lib/lp/services/webapp/publisher.py (+1/-6)
lib/lp/snappy/browser/tests/test_snap.py (+2/-7)
lib/lp/snappy/tests/test_snap.py (+21/-50)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+429433@code.launchpad.net

Commit message

Call new Loggerhead download API without file-id

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

Good to see so much red! Just a few things to fix up before landing this, I think.

review: Approve
Revision history for this message
Ioana Lasc (ilasc) wrote :

Glad we're in agreement on the red quantity, wasn't sure if I had gone a bit too far with it :-)
I've tidied up the url quoting and the filename / path occurrences.
Thanks Colin!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/code/errors.py b/lib/lp/code/errors.py
2index 2fa4a47..82042a5 100644
3--- a/lib/lp/code/errors.py
4+++ b/lib/lp/code/errors.py
5@@ -365,23 +365,16 @@ class BranchHostingFault(Exception):
6 class BranchFileNotFound(BranchHostingFault):
7 """Raised when a file does not exist in a branch."""
8
9- def __init__(self, branch_id, filename=None, file_id=None, rev=None):
10+ def __init__(self, branch_id, filename, rev=None):
11 super().__init__()
12- if (filename is None) == (file_id is None):
13- raise AssertionError(
14- "Exactly one of filename and file_id must be given."
15- )
16 self.branch_id = branch_id
17 self.filename = filename
18- self.file_id = file_id
19 self.rev = rev
20
21 def __str__(self):
22 message = "Branch ID %s has no file " % self.branch_id
23 if self.filename is not None:
24 message += self.filename
25- else:
26- message += "with ID %s" % self.file_id
27 if self.rev is not None:
28 message += " at revision %s" % self.rev
29 return message
30diff --git a/lib/lp/code/interfaces/branchhosting.py b/lib/lp/code/interfaces/branchhosting.py
31index e93096a..41b8926 100644
32--- a/lib/lp/code/interfaces/branchhosting.py
33+++ b/lib/lp/code/interfaces/branchhosting.py
34@@ -28,27 +28,11 @@ class IBranchHostingClient(Interface):
35 :return: The diff between `old` and `new` as a byte string.
36 """
37
38- def getInventory(branch_id, dirname, rev=None, logger=None):
39- """Get information on files in a directory.
40-
41- :param branch_id: The ID of the branch.
42- :param dirname: The name of the directory, relative to the root of
43- the branch.
44- :param rev: An optional revno or revision ID. Defaults to 'head:'.
45- :param logger: An optional logger.
46- :raises ValueError: if `rev` is ill-formed.
47- :raises BranchFileNotFound: if the directory does not exist.
48- :raises BranchHostingFault: if the API returned some other error.
49- :return: The directory inventory as a dict: see
50- `loggerhead.controllers.inventory_ui` for details.
51- """
52-
53- def getBlob(branch_id, file_id, rev=None, logger=None):
54+ def getBlob(branch_id, path, rev=None, logger=None):
55 """Get a blob by file name from a branch.
56
57 :param branch_id: The ID of the branch.
58- :param file_id: The file ID of the file. (`getInventory` may be
59- useful to retrieve this.)
60+ :param path: The relative path of the file.
61 :param rev: An optional revno or revision ID. Defaults to 'head:'.
62 :param logger: An optional logger.
63 :raises ValueError: if `rev` is ill-formed.
64diff --git a/lib/lp/code/model/branch.py b/lib/lp/code/model/branch.py
65index 43ed1b0..63d1b13 100644
66--- a/lib/lp/code/model/branch.py
67+++ b/lib/lp/code/model/branch.py
68@@ -8,13 +8,10 @@ __all__ = [
69 ]
70
71 import operator
72-import os.path
73 from datetime import datetime
74 from functools import partial
75-from urllib.parse import urlsplit
76
77 import pytz
78-import six
79 from breezy import urlutils
80 from breezy.revision import NULL_REVISION
81 from breezy.url_policy_open import open_only_scheme
82@@ -66,7 +63,6 @@ from lp.code.enums import (
83 )
84 from lp.code.errors import (
85 AlreadyLatestFormat,
86- BranchFileNotFound,
87 BranchMergeProposalExists,
88 BranchTargetError,
89 BranchTypeError,
90@@ -142,12 +138,10 @@ from lp.services.database.interfaces import IMasterStore, IStore
91 from lp.services.database.sqlbase import SQLBase, sqlvalues
92 from lp.services.database.sqlobject import ForeignKey, IntCol, StringCol
93 from lp.services.database.stormexpr import Array, ArrayAgg, ArrayIntersects
94-from lp.services.features import getFeatureFlag
95 from lp.services.helpers import shortlist
96 from lp.services.job.interfaces.job import JobStatus
97 from lp.services.job.model.job import Job
98 from lp.services.mail.notificationrecipientset import NotificationRecipientSet
99-from lp.services.memcache.interfaces import IMemcacheClient
100 from lp.services.propertycache import cachedproperty, get_property_cache
101 from lp.services.webapp import urlappend
102 from lp.services.webapp.authorization import check_permission
103@@ -899,64 +893,9 @@ class Branch(SQLBase, WebhookTargetMixin, BzrIdentityMixin):
104 ):
105 """See `IBranch`."""
106 hosting_client = getUtility(IBranchHostingClient)
107- if enable_memcache is None:
108- enable_memcache = not getFeatureFlag(
109- "code.bzr.blob.disable_memcache"
110- )
111 if revision_id is None:
112 revision_id = self.last_scanned_id
113- if revision_id is None:
114- # revision_id may still be None if the branch scanner hasn't
115- # scanned this branch yet. In this case, we won't be able to
116- # guarantee that subsequent calls to this method within the same
117- # transaction with revision_id=None will see the same revision,
118- # and we won't be able to cache file lists. Neither is fatal,
119- # and this should be relatively rare.
120- enable_memcache = False
121- dirname = os.path.dirname(filename)
122- unset = object()
123- file_list = unset
124- if enable_memcache:
125- memcache_client = getUtility(IMemcacheClient)
126- instance_name = urlsplit(
127- config.codehosting.internal_bzr_api_endpoint
128- ).hostname
129- memcache_key = six.ensure_binary(
130- "%s:bzr-file-list:%s:%s:%s"
131- % (instance_name, self.id, revision_id, dirname)
132- )
133- description = "file list for %s:%s:%s" % (
134- self.unique_name,
135- revision_id,
136- dirname,
137- )
138- file_list = memcache_client.get_json(
139- memcache_key, logger, description, default=unset
140- )
141-
142- if file_list is unset:
143- try:
144- inventory = hosting_client.getInventory(
145- self.id, dirname, rev=revision_id
146- )
147- file_list = {
148- entry["filename"]: entry["file_id"]
149- for entry in inventory["filelist"]
150- }
151- except BranchFileNotFound:
152- file_list = None
153- if enable_memcache:
154- # Cache the file list in case there's a request for another
155- # file in the same directory.
156- memcache_client.set_json(
157- memcache_key, file_list, logger=logger
158- )
159- file_id = (file_list or {}).get(os.path.basename(filename))
160- if file_id is None:
161- raise BranchFileNotFound(
162- self.unique_name, filename=filename, rev=revision_id
163- )
164- return hosting_client.getBlob(self.id, file_id, rev=revision_id)
165+ return hosting_client.getBlob(self.id, filename, rev=revision_id)
166
167 def getDiff(self, new, old=None):
168 """See `IBranch`."""
169diff --git a/lib/lp/code/model/branchhosting.py b/lib/lp/code/model/branchhosting.py
170index 8b5abb4..365a6c8 100644
171--- a/lib/lp/code/model/branchhosting.py
172+++ b/lib/lp/code/model/branchhosting.py
173@@ -128,43 +128,18 @@ class BranchHostingClient:
174 "Failed to get diff from Bazaar branch: %s" % e
175 )
176
177- def getInventory(self, branch_id, dirname, rev=None, logger=None):
178+ def getBlob(self, branch_id, path, rev=None, logger=None):
179 """See `IBranchHostingClient`."""
180 self._checkRevision(rev)
181 try:
182 if logger is not None:
183 logger.info(
184- "Requesting inventory at %s from branch %s"
185- % (dirname, branch_id)
186- )
187- quoted_tail = "files/%s/%s" % (
188- quote(rev or "head:", safe=""),
189- quote(dirname.lstrip("/")),
190- )
191- return self._get(branch_id, quoted_tail, as_json=True)
192- except requests.RequestException as e:
193- if (
194- e.response is not None
195- and e.response.status_code == requests.codes.NOT_FOUND
196- ):
197- raise BranchFileNotFound(branch_id, filename=dirname, rev=rev)
198- else:
199- raise BranchHostingFault(
200- "Failed to get inventory from Bazaar branch: %s" % e
201- )
202-
203- def getBlob(self, branch_id, file_id, rev=None, logger=None):
204- """See `IBranchHostingClient`."""
205- self._checkRevision(rev)
206- try:
207- if logger is not None:
208- logger.info(
209- "Fetching file ID %s from branch %s" % (file_id, branch_id)
210+ "Fetching file ID %s from branch %s" % (path, branch_id)
211 )
212 return self._get(
213 branch_id,
214 "download/%s/%s"
215- % (quote(rev or "head:", safe=""), quote(file_id, safe="")),
216+ % (quote(rev or "head:", safe=""), quote(path, safe="/")),
217 as_json=False,
218 )
219 except requests.RequestException as e:
220@@ -172,7 +147,7 @@ class BranchHostingClient:
221 e.response is not None
222 and e.response.status_code == requests.codes.NOT_FOUND
223 ):
224- raise BranchFileNotFound(branch_id, file_id=file_id, rev=rev)
225+ raise BranchFileNotFound(branch_id, filename=path, rev=rev)
226 else:
227 raise BranchHostingFault(
228 "Failed to get file from Bazaar branch: %s" % e
229diff --git a/lib/lp/code/model/tests/test_branch.py b/lib/lp/code/model/tests/test_branch.py
230index 5f037c4..d78c340 100644
231--- a/lib/lp/code/model/tests/test_branch.py
232+++ b/lib/lp/code/model/tests/test_branch.py
233@@ -3,7 +3,6 @@
234
235 """Tests for Branches."""
236
237-import json
238 from datetime import datetime, timedelta
239
240 import transaction
241@@ -117,7 +116,6 @@ from lp.services.features.testing import FeatureFixture
242 from lp.services.job.interfaces.job import JobStatus
243 from lp.services.job.runner import JobRunner
244 from lp.services.job.tests import block_on_job, monitor_celery
245-from lp.services.memcache.interfaces import IMemcacheClient
246 from lp.services.osutils import override_environ
247 from lp.services.propertycache import clear_property_cache
248 from lp.services.webapp.authorization import check_permission
249@@ -3686,157 +3684,39 @@ class TestBranchGetBlob(TestCaseWithFactory):
250
251 def test_default_rev_unscanned(self):
252 branch = self.factory.makeBranch()
253- hosting_fixture = self.useFixture(
254- BranchHostingFixture(
255- file_list={"README.txt": "some-file-id"}, blob=b"Some text"
256- )
257- )
258+ self.useFixture(BranchHostingFixture(blob=b"Some text"))
259 blob = branch.getBlob("src/README.txt")
260 self.assertEqual(b"Some text", blob)
261- self.assertEqual(
262- [((branch.id, "src"), {"rev": None})],
263- hosting_fixture.getInventory.calls,
264- )
265- self.assertEqual(
266- [((branch.id, "some-file-id"), {"rev": None})],
267- hosting_fixture.getBlob.calls,
268- )
269- self.assertEqual({}, getUtility(IMemcacheClient)._cache)
270
271 def test_default_rev_scanned(self):
272 branch = self.factory.makeBranch()
273 removeSecurityProxy(branch).last_scanned_id = "scanned-id"
274- hosting_fixture = self.useFixture(
275- BranchHostingFixture(
276- file_list={"README.txt": "some-file-id"}, blob=b"Some text"
277- )
278- )
279+ self.useFixture(BranchHostingFixture(blob=b"Some text"))
280 blob = branch.getBlob("src/README.txt")
281 self.assertEqual(b"Some text", blob)
282- self.assertEqual(
283- [((branch.id, "src"), {"rev": "scanned-id"})],
284- hosting_fixture.getInventory.calls,
285- )
286- self.assertEqual(
287- [((branch.id, "some-file-id"), {"rev": "scanned-id"})],
288- hosting_fixture.getBlob.calls,
289- )
290- key = (
291- "bazaar.launchpad.test:bzr-file-list:%s:scanned-id:src" % branch.id
292- )
293- self.assertEqual(
294- json.dumps({"README.txt": "some-file-id"}),
295- getUtility(IMemcacheClient).get(key.encode("UTF-8")),
296- )
297
298 def test_with_rev(self):
299 branch = self.factory.makeBranch()
300- hosting_fixture = self.useFixture(
301- BranchHostingFixture(
302- file_list={"README.txt": "some-file-id"}, blob=b"Some text"
303- )
304- )
305- blob = branch.getBlob("src/README.txt", revision_id="some-rev")
306- self.assertEqual(b"Some text", blob)
307- self.assertEqual(
308- [((branch.id, "src"), {"rev": "some-rev"})],
309- hosting_fixture.getInventory.calls,
310- )
311- self.assertEqual(
312- [((branch.id, "some-file-id"), {"rev": "some-rev"})],
313- hosting_fixture.getBlob.calls,
314- )
315- key = "bazaar.launchpad.test:bzr-file-list:%s:some-rev:src" % branch.id
316- self.assertEqual(
317- json.dumps({"README.txt": "some-file-id"}),
318- getUtility(IMemcacheClient).get(key.encode("UTF-8")),
319- )
320-
321- def test_cached_inventory(self):
322- branch = self.factory.makeBranch()
323- hosting_fixture = self.useFixture(
324- BranchHostingFixture(blob=b"Some text")
325- )
326- key = "bazaar.launchpad.test:bzr-file-list:%s:some-rev:src" % branch.id
327- getUtility(IMemcacheClient).set(
328- key.encode("UTF-8"), json.dumps({"README.txt": "some-file-id"})
329- )
330- blob = branch.getBlob("src/README.txt", revision_id="some-rev")
331- self.assertEqual(b"Some text", blob)
332- self.assertEqual([], hosting_fixture.getInventory.calls)
333- self.assertEqual(
334- [((branch.id, "some-file-id"), {"rev": "some-rev"})],
335- hosting_fixture.getBlob.calls,
336- )
337-
338- def test_disable_memcache(self):
339- self.useFixture(
340- FeatureFixture({"code.bzr.blob.disable_memcache": "on"})
341- )
342- branch = self.factory.makeBranch()
343- hosting_fixture = self.useFixture(
344- BranchHostingFixture(
345- file_list={"README.txt": "some-file-id"}, blob=b"Some text"
346- )
347- )
348- key = "bazaar.launchpad.test:bzr-file-list:%s:some-rev:src" % branch.id
349- getUtility(IMemcacheClient).set(key.encode("UTF-8"), "{}")
350+ self.useFixture(BranchHostingFixture(blob=b"Some text"))
351 blob = branch.getBlob("src/README.txt", revision_id="some-rev")
352 self.assertEqual(b"Some text", blob)
353- self.assertEqual(
354- [((branch.id, "src"), {"rev": "some-rev"})],
355- hosting_fixture.getInventory.calls,
356- )
357- self.assertEqual(
358- "{}", getUtility(IMemcacheClient).get(key.encode("UTF-8"))
359- )
360
361 def test_file_at_root_of_branch(self):
362 branch = self.factory.makeBranch()
363 hosting_fixture = self.useFixture(
364- BranchHostingFixture(
365- file_list={"README.txt": "some-file-id"}, blob=b"Some text"
366- )
367+ BranchHostingFixture(blob=b"Some text")
368 )
369 blob = branch.getBlob("README.txt", revision_id="some-rev")
370 self.assertEqual(b"Some text", blob)
371 self.assertEqual(
372- [((branch.id, ""), {"rev": "some-rev"})],
373- hosting_fixture.getInventory.calls,
374- )
375- self.assertEqual(
376- [((branch.id, "some-file-id"), {"rev": "some-rev"})],
377+ [((branch.id, "README.txt"), {"rev": "some-rev"})],
378 hosting_fixture.getBlob.calls,
379 )
380- key = "bazaar.launchpad.test:bzr-file-list:%s:some-rev:" % branch.id
381- self.assertEqual(
382- json.dumps({"README.txt": "some-file-id"}),
383- getUtility(IMemcacheClient).get(key.encode("UTF-8")),
384- )
385-
386- def test_file_not_in_directory(self):
387- branch = self.factory.makeBranch()
388- hosting_fixture = self.useFixture(BranchHostingFixture(file_list={}))
389- self.assertRaises(
390- BranchFileNotFound,
391- branch.getBlob,
392- "src/README.txt",
393- revision_id="some-rev",
394- )
395- self.assertEqual(
396- [((branch.id, "src"), {"rev": "some-rev"})],
397- hosting_fixture.getInventory.calls,
398- )
399- self.assertEqual([], hosting_fixture.getBlob.calls)
400- key = "bazaar.launchpad.test:bzr-file-list:%s:some-rev:src" % branch.id
401- self.assertEqual(
402- "{}", getUtility(IMemcacheClient).get(key.encode("UTF-8"))
403- )
404
405 def test_missing_directory(self):
406 branch = self.factory.makeBranch()
407 hosting_fixture = self.useFixture(BranchHostingFixture())
408- hosting_fixture.getInventory = FakeMethod(
409+ hosting_fixture.getBlob = FakeMethod(
410 failure=BranchFileNotFound(
411 branch.unique_name, filename="src", rev="some-rev"
412 )
413@@ -3847,29 +3727,6 @@ class TestBranchGetBlob(TestCaseWithFactory):
414 "src/README.txt",
415 revision_id="some-rev",
416 )
417- self.assertEqual(
418- [((branch.id, "src"), {"rev": "some-rev"})],
419- hosting_fixture.getInventory.calls,
420- )
421- self.assertEqual([], hosting_fixture.getBlob.calls)
422- key = "bazaar.launchpad.test:bzr-file-list:%s:some-rev:src" % branch.id
423- self.assertEqual(
424- "null", getUtility(IMemcacheClient).get(key.encode("UTF-8"))
425- )
426-
427- def test_cached_missing_directory(self):
428- branch = self.factory.makeBranch()
429- hosting_fixture = self.useFixture(BranchHostingFixture())
430- key = "bazaar.launchpad.test:bzr-file-list:%s:some-rev:src" % branch.id
431- getUtility(IMemcacheClient).set(key.encode("UTF-8"), "null")
432- self.assertRaises(
433- BranchFileNotFound,
434- branch.getBlob,
435- "src/README.txt",
436- revision_id="some-rev",
437- )
438- self.assertEqual([], hosting_fixture.getInventory.calls)
439- self.assertEqual([], hosting_fixture.getBlob.calls)
440
441
442 class TestBranchUnscan(TestCaseWithFactory):
443diff --git a/lib/lp/code/model/tests/test_branchhosting.py b/lib/lp/code/model/tests/test_branchhosting.py
444index ccf6dc8..ddd719d 100644
445--- a/lib/lp/code/model/tests/test_branchhosting.py
446+++ b/lib/lp/code/model/tests/test_branchhosting.py
447@@ -108,108 +108,44 @@ class TestBranchHostingClient(TestCase):
448 "1",
449 )
450
451- def test_getInventory(self):
452- with self.mockRequests("GET", json={"filelist": []}):
453- response = self.client.getInventory(123, "dir/path/file/name")
454- self.assertEqual({"filelist": []}, response)
455- self.assertRequest(
456- "+branch-id/123/+json/files/head%3A/dir/path/file/name"
457- )
458-
459- def test_getInventory_revision(self):
460- with self.mockRequests("GET", json={"filelist": []}):
461- response = self.client.getInventory(
462- 123, "dir/path/file/name", rev="a"
463- )
464- self.assertEqual({"filelist": []}, response)
465- self.assertRequest("+branch-id/123/+json/files/a/dir/path/file/name")
466-
467- def test_getInventory_not_found(self):
468- with self.mockRequests("GET", status=404):
469- self.assertRaisesWithContent(
470- BranchFileNotFound,
471- "Branch ID 123 has no file dir/path/file/name",
472- self.client.getInventory,
473- 123,
474- "dir/path/file/name",
475- )
476-
477- def test_getInventory_revision_not_found(self):
478- with self.mockRequests("GET", status=404):
479- self.assertRaisesWithContent(
480- BranchFileNotFound,
481- "Branch ID 123 has no file dir/path/file/name at revision a",
482- self.client.getInventory,
483- 123,
484- "dir/path/file/name",
485- rev="a",
486- )
487-
488- def test_getInventory_bad_revision(self):
489- self.assertRaises(
490- ValueError,
491- self.client.getInventory,
492- 123,
493- "dir/path/file/name",
494- rev="x/y",
495- )
496-
497- def test_getInventory_failure(self):
498- with self.mockRequests("GET", status=400):
499- self.assertRaisesWithContent(
500- BranchHostingFault,
501- "Failed to get inventory from Bazaar branch: "
502- "400 Client Error: Bad Request",
503- self.client.getInventory,
504- 123,
505- "dir/path/file/name",
506- )
507-
508- def test_getInventory_url_quoting(self):
509- with self.mockRequests("GET", json={"filelist": []}):
510- self.client.getInventory(123, "+file/ name?", rev="+rev id?")
511- self.assertRequest(
512- "+branch-id/123/+json/files/%2Brev%20id%3F/%2Bfile/%20name%3F"
513- )
514-
515 def test_getBlob(self):
516 blob = b"".join(bytes((i,)) for i in range(256))
517 with self.mockRequests("GET", body=blob):
518- response = self.client.getBlob(123, "file-id")
519+ response = self.client.getBlob(123, "file-name")
520 self.assertEqual(blob, response)
521- self.assertRequest("+branch-id/123/download/head%3A/file-id")
522+ self.assertRequest("+branch-id/123/download/head%3A/file-name")
523
524 def test_getBlob_revision(self):
525 blob = b"".join(bytes((i,)) for i in range(256))
526 with self.mockRequests("GET", body=blob):
527- response = self.client.getBlob(123, "file-id", rev="a")
528+ response = self.client.getBlob(123, "file-name", rev="a")
529 self.assertEqual(blob, response)
530- self.assertRequest("+branch-id/123/download/a/file-id")
531+ self.assertRequest("+branch-id/123/download/a/file-name")
532
533 def test_getBlob_not_found(self):
534 with self.mockRequests("GET", status=404):
535 self.assertRaisesWithContent(
536 BranchFileNotFound,
537- "Branch ID 123 has no file with ID file-id",
538+ "Branch ID 123 has no file src/file",
539 self.client.getBlob,
540 123,
541- "file-id",
542+ "src/file",
543 )
544
545 def test_getBlob_revision_not_found(self):
546 with self.mockRequests("GET", status=404):
547 self.assertRaisesWithContent(
548 BranchFileNotFound,
549- "Branch ID 123 has no file with ID file-id at revision a",
550+ "Branch ID 123 has no file src/file at revision a",
551 self.client.getBlob,
552 123,
553- "file-id",
554+ "src/file",
555 rev="a",
556 )
557
558 def test_getBlob_bad_revision(self):
559 self.assertRaises(
560- ValueError, self.client.getBlob, 123, "file-id", rev="x/y"
561+ ValueError, self.client.getBlob, 123, "file-name", rev="x/y"
562 )
563
564 def test_getBlob_failure(self):
565@@ -220,7 +156,7 @@ class TestBranchHostingClient(TestCase):
566 "400 Client Error: Bad Request",
567 self.client.getBlob,
568 123,
569- "file-id",
570+ "file-name",
571 )
572
573 def test_getBlob_url_quoting(self):
574@@ -228,7 +164,15 @@ class TestBranchHostingClient(TestCase):
575 with self.mockRequests("GET", body=blob):
576 self.client.getBlob(123, "+file/ id?", rev="+rev id?")
577 self.assertRequest(
578- "+branch-id/123/download/%2Brev%20id%3F/%2Bfile%2F%20id%3F"
579+ "+branch-id/123/download/%2Brev%20id%3F/%2Bfile/%20id%3F"
580+ )
581+
582+ def test_getBlob_url_quoting_forward_slash(self):
583+ blob = b"".join(bytes((i,)) for i in range(256))
584+ with self.mockRequests("GET", body=blob):
585+ self.client.getBlob(123, "+snap/snapcraft.yaml?", rev="+rev id?")
586+ self.assertRequest(
587+ "+branch-id/123/download/%2Brev%20id%3F/%2Bsnap/snapcraft.yaml%3F"
588 )
589
590 def test_works_in_job(self):
591@@ -246,11 +190,11 @@ class TestBranchHostingClient(TestCase):
592 with self.testcase.mockRequests(
593 "GET", body=blob, set_default_timeout=False
594 ):
595- self.blob = self.testcase.client.getBlob(123, "file-id")
596+ self.blob = self.testcase.client.getBlob(123, "file-name")
597 # We must make this assertion inside the job, since the job
598 # runner creates a separate timeline.
599 self.testcase.assertRequest(
600- "+branch-id/123/download/head%3A/file-id"
601+ "+branch-id/123/download/head%3A/file-name"
602 )
603
604 job = GetBlobJob(self)
605diff --git a/lib/lp/code/tests/helpers.py b/lib/lp/code/tests/helpers.py
606index 7f36b61..ff8165b 100644
607--- a/lib/lp/code/tests/helpers.py
608+++ b/lib/lp/code/tests/helpers.py
609@@ -311,25 +311,11 @@ class BranchHostingFixture(fixtures.Fixture):
610 def __init__(
611 self,
612 diff=None,
613- inventory=None,
614- file_list=None,
615 blob=None,
616 disable_memcache=True,
617 ):
618 self.create = FakeMethod()
619 self.getDiff = FakeMethod(result=diff or {})
620- if inventory is None:
621- if file_list is not None:
622- # Simple common case.
623- inventory = {
624- "filelist": [
625- {"filename": filename, "file_id": file_id}
626- for filename, file_id in file_list.items()
627- ],
628- }
629- else:
630- inventory = {"filelist": []}
631- self.getInventory = FakeMethod(result=inventory)
632 self.getBlob = FakeMethod(result=blob)
633 self.disable_memcache = disable_memcache
634
635diff --git a/lib/lp/services/webapp/publisher.py b/lib/lp/services/webapp/publisher.py
636index c44c87b..88e3d76 100644
637--- a/lib/lp/services/webapp/publisher.py
638+++ b/lib/lp/services/webapp/publisher.py
639@@ -29,12 +29,7 @@ import http.client
640 import json
641 import re
642 from cgi import FieldStorage
643-from typing import (
644- Any,
645- Dict,
646- Optional,
647- Type,
648-)
649+from typing import Any, Dict, Optional, Type
650 from urllib.parse import urlparse
651 from wsgiref.headers import Headers
652
653diff --git a/lib/lp/snappy/browser/tests/test_snap.py b/lib/lp/snappy/browser/tests/test_snap.py
654index 74b502c..10af3a1 100644
655--- a/lib/lp/snappy/browser/tests/test_snap.py
656+++ b/lib/lp/snappy/browser/tests/test_snap.py
657@@ -771,7 +771,6 @@ class TestSnapAddView(BaseTestSnapView):
658 def test_initial_name_extraction_bzr_success(self):
659 self.useFixture(
660 BranchHostingFixture(
661- file_list={"snapcraft.yaml": "file-id"},
662 blob=b"name: test-snap",
663 )
664 )
665@@ -782,7 +781,7 @@ class TestSnapAddView(BaseTestSnapView):
666 self.assertEqual("test-snap", initial_values["store_name"])
667
668 def test_initial_name_extraction_bzr_error(self):
669- self.useFixture(BranchHostingFixture()).getInventory = FakeMethod(
670+ self.useFixture(BranchHostingFixture()).getBlob = FakeMethod(
671 failure=BranchHostingFault
672 )
673 branch = self.factory.makeBranch()
674@@ -792,11 +791,7 @@ class TestSnapAddView(BaseTestSnapView):
675 self.assertIsNone(initial_values["store_name"])
676
677 def test_initial_name_extraction_bzr_no_name(self):
678- self.useFixture(
679- BranchHostingFixture(
680- file_list={"snapcraft.yaml": "file-id"}, blob=b"some: nonsense"
681- )
682- )
683+ self.useFixture(BranchHostingFixture(blob=b"some: nonsense"))
684 branch = self.factory.makeBranch()
685 view = create_initialized_view(branch, "+new-snap")
686 initial_values = view.initial_values
687diff --git a/lib/lp/snappy/tests/test_snap.py b/lib/lp/snappy/tests/test_snap.py
688index 5329c46..48b1078 100644
689--- a/lib/lp/snappy/tests/test_snap.py
690+++ b/lib/lp/snappy/tests/test_snap.py
691@@ -2754,22 +2754,15 @@ class TestSnapSet(TestCaseWithFactory):
692 )
693
694 def test_getSnapcraftYaml_bzr_snap_snapcraft_yaml(self):
695- def getInventory(unique_name, dirname, *args, **kwargs):
696- if dirname == "snap":
697- return {
698- "filelist": [
699- {
700- "filename": "snapcraft.yaml",
701- "file_id": "some-file-id",
702- }
703- ]
704- }
705+ def getBlob(path, filename, *args, **kwargs):
706+ if filename == "snap/snapcraft.yaml":
707+ return b"name: test-snap"
708 else:
709- raise BranchFileNotFound("dummy", dirname)
710+ raise BranchFileNotFound("dummy", filename)
711
712 self.useFixture(
713 BranchHostingFixture(blob=b"name: test-snap")
714- ).getInventory = getInventory
715+ ).getBlob = getBlob
716 branch = self.factory.makeBranch()
717 self.assertEqual(
718 {"name": "test-snap"},
719@@ -2777,22 +2770,15 @@ class TestSnapSet(TestCaseWithFactory):
720 )
721
722 def test_getSnapcraftYaml_bzr_build_aux_snap_snapcraft_yaml(self):
723- def getInventory(unique_name, dirname, *args, **kwargs):
724- if dirname == "build-aux/snap":
725- return {
726- "filelist": [
727- {
728- "filename": "snapcraft.yaml",
729- "file_id": "some-file-id",
730- }
731- ]
732- }
733+ def getBlob(path, filename, *args, **kwargs):
734+ if filename == "build-aux/snap/snapcraft.yaml":
735+ return b"name: test-snap"
736 else:
737- raise BranchFileNotFound("dummy", dirname)
738+ raise BranchFileNotFound("dummy", filename)
739
740 self.useFixture(
741 BranchHostingFixture(blob=b"name: test-snap")
742- ).getInventory = getInventory
743+ ).getBlob = getBlob
744 branch = self.factory.makeBranch()
745 self.assertEqual(
746 {"name": "test-snap"},
747@@ -2800,22 +2786,15 @@ class TestSnapSet(TestCaseWithFactory):
748 )
749
750 def test_getSnapcraftYaml_bzr_plain_snapcraft_yaml(self):
751- def getInventory(unique_name, dirname, *args, **kwargs):
752- if dirname == "":
753- return {
754- "filelist": [
755- {
756- "filename": "snapcraft.yaml",
757- "file_id": "some-file-id",
758- }
759- ]
760- }
761+ def getBlob(path, filename, *args, **kwargs):
762+ if filename == "snapcraft.yaml":
763+ return b"name: test-snap"
764 else:
765- raise BranchFileNotFound("dummy", dirname)
766+ raise BranchFileNotFound("dummy", filename)
767
768 self.useFixture(
769 BranchHostingFixture(blob=b"name: test-snap")
770- ).getInventory = getInventory
771+ ).getBlob = getBlob
772 branch = self.factory.makeBranch()
773 self.assertEqual(
774 {"name": "test-snap"},
775@@ -2823,22 +2802,15 @@ class TestSnapSet(TestCaseWithFactory):
776 )
777
778 def test_getSnapcraftYaml_bzr_dot_snapcraft_yaml(self):
779- def getInventory(unique_name, dirname, *args, **kwargs):
780- if dirname == "":
781- return {
782- "filelist": [
783- {
784- "filename": ".snapcraft.yaml",
785- "file_id": "some-file-id",
786- }
787- ]
788- }
789+ def getBlob(path, filename, *args, **kwargs):
790+ if filename == ".snapcraft.yaml":
791+ return b"name: test-snap"
792 else:
793- raise BranchFileNotFound("dummy", dirname)
794+ raise BranchFileNotFound("dummy", filename)
795
796 self.useFixture(
797 BranchHostingFixture(blob=b"name: test-snap")
798- ).getInventory = getInventory
799+ ).getBlob = getBlob
800 branch = self.factory.makeBranch()
801 self.assertEqual(
802 {"name": "test-snap"},
803@@ -2846,7 +2818,7 @@ class TestSnapSet(TestCaseWithFactory):
804 )
805
806 def test_getSnapcraftYaml_bzr_error(self):
807- self.useFixture(BranchHostingFixture()).getInventory = FakeMethod(
808+ self.useFixture(BranchHostingFixture()).getBlob = FakeMethod(
809 failure=BranchHostingFault
810 )
811 branch = self.factory.makeBranch()
812@@ -2926,7 +2898,6 @@ class TestSnapSet(TestCaseWithFactory):
813 def test_getSnapcraftYaml_snap_bzr(self):
814 self.useFixture(
815 BranchHostingFixture(
816- file_list={"snapcraft.yaml": "some-file-id"},
817 blob=b"name: test-snap",
818 )
819 )

Subscribers

People subscribed via source and target branches

to status/vote changes: