Merge lp:~ricardokirkner/ols-store-tests/more-acceptance-tests into lp:~ubuntuone-pqm-team/ols-store-tests/store-acceptance-tests

Proposed by Ricardo Kirkner
Status: Merged
Merged at revision: 19
Proposed branch: lp:~ricardokirkner/ols-store-tests/more-acceptance-tests
Merge into: lp:~ubuntuone-pqm-team/ols-store-tests/store-acceptance-tests
Diff against target: 765 lines (+705/-3)
8 files modified
Makefile (+1/-1)
tests/api/cpi/helpers.py (+40/-2)
tests/api/cpi/test_channel.py (+56/-0)
tests/api/cpi/test_department.py (+82/-0)
tests/api/cpi/test_framework.py (+60/-0)
tests/api/cpi/test_highlight.py (+65/-0)
tests/api/cpi/test_metadata.py (+176/-0)
tests/api/cpi/test_package.py (+225/-0)
To merge this branch: bzr merge lp:~ricardokirkner/ols-store-tests/more-acceptance-tests
Reviewer Review Type Date Requested Status
James Tait (community) Approve
Matias Bordese (community) Approve
Review via email: mp+299342@code.launchpad.net

Commit message

added basic smoke tests for cpi APIs

added tests for following endpoints:

- package
- click metadata
- snap metadata
- highlights
- frameworks
- departments
- channels

To post a comment you must log in.
25. By Ricardo Kirkner

refactor tests to use constant

Revision history for this message
Matias Bordese (matiasb) wrote :

Looking good, added a few comments/questions.

review: Approve
26. By Ricardo Kirkner

fixes per review

Revision history for this message
Ricardo Kirkner (ricardokirkner) :
Revision history for this message
James Tait (jamestait) wrote :

Yep, looks good to me. I've made a couple of suggestions, but they are just that - this is good to land as-is, IMO.

As an aside, we're hard-coding some assumptions about URL structure (e.g. appending click-package-name or snap-package-id to /api/v1/package), which may not hold true longer-term. I think that's probably OK for these tests, since we *want* to know if URLs change, and I don't think *any* of the clients are actually parsing the links from the root resource anyway.

review: Approve
27. By Ricardo Kirkner

fixes per review

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2016-07-05 14:42:00 +0000
3+++ Makefile 2016-07-07 18:53:29 +0000
4@@ -3,7 +3,7 @@
5 PYTHON = python3
6 VM = store-acceptance-tests
7 VSSH = ols-vms shell ${VM}
8-TEST_TARGET := discover tests/api
9+TEST_TARGET ?= discover tests/api
10
11 vm-setup: ols-vms.conf
12 if [ `ols-vms status ${VM}` = 'RUNNING' ] ; then ols-vms stop ${VM} ; fi
13
14=== modified file 'tests/api/cpi/helpers.py'
15--- tests/api/cpi/helpers.py 2016-06-28 19:20:09 +0000
16+++ tests/api/cpi/helpers.py 2016-07-07 18:53:29 +0000
17@@ -1,14 +1,52 @@
18+import getpass
19 import os
20 import unittest
21
22
23 CPI_ROOT_URL = os.getenv(
24 'CPI_ROOT_URL', 'https://search.apps.staging.ubuntu.com')
25+SSO_ROOT_URL = os.getenv(
26+ 'SSO_ROOT_URL', 'https://login.staging.ubuntu.com')
27+
28+TEST_USER_EMAIL = os.getenv('TEST_USER_EMAIL', None)
29+if TEST_USER_EMAIL is None:
30+ TEST_USER_EMAIL = input('Test user email: ')
31+TEST_USER_PASSWORD = os.getenv('TEST_USER_PASSWORD', None)
32+if TEST_USER_PASSWORD is None:
33+ TEST_USER_PASSWORD = getpass.getpass('Test user password: ')
34+
35+CLICK_PACKAGE_NAME = 'demo37.ricardokirkner'
36+SNAP_PACKAGE_NAME = 'hello-world'
37+SNAP_PACKAGE_CHANNEL = 'stable'
38+SNAP_PACKAGE_REVISION = 6
39+SNAP_PACKAGE_SNAP_ID = 'JtwEnisYi8Mmk51vNLZPSOwSOFLwGdhs'
40+PRIVATE_SNAP_PACKAGE_NAME = 'ricardokirkner-test1'
41+PRIVATE_SNAP_PACKAGE_SNAP_ID = 'qrwoXeYOXAZla2ga3XtBSogV1vAUKqZh'
42+PRIVATE_SNAP_PACKAGE_CHANNEL = 'stable'
43+PRIVATE_SNAP_PACKAGE_REVISION = 1
44
45
46 class APITestCase(unittest.TestCase):
47
48- def assert_success(self, response):
49+ def assert_success(self, response, content_type='application/hal+json'):
50 self.assertEqual(response.status_code, 200)
51 self.assertEqual(response.headers['Content-Type'],
52- 'application/hal+json')
53+ content_type)
54+
55+ def assert_not_found(self, response, message=None):
56+ self.assertEqual(response.status_code, 404)
57+ self.assertEqual(response.headers['Content-Type'],
58+ 'application/hal+json')
59+ self.assertEqual(response.json(), {
60+ 'result': 'error',
61+ 'errors': [message or 'Not found'],
62+ })
63+
64+ def assert_bad_request(self, response, message):
65+ self.assertEqual(response.status_code, 400)
66+ self.assertEqual(response.headers['Content-Type'],
67+ 'application/hal+json')
68+ self.assertEqual(response.json(), {
69+ 'result': 'error',
70+ 'errors': [message],
71+ })
72
73=== added file 'tests/api/cpi/test_channel.py'
74--- tests/api/cpi/test_channel.py 1970-01-01 00:00:00 +0000
75+++ tests/api/cpi/test_channel.py 2016-07-07 18:53:29 +0000
76@@ -0,0 +1,56 @@
77+import requests
78+
79+from .helpers import (
80+ CPI_ROOT_URL,
81+ APITestCase,
82+)
83+
84+
85+def get_channels():
86+ url = '{}/api/v1/channels'.format(CPI_ROOT_URL)
87+ response = requests.get(url)
88+ return response
89+
90+
91+def get_channel(name):
92+ url = '{}/api/v1/channels/{}'.format(CPI_ROOT_URL, name)
93+ response = requests.get(url)
94+ return response
95+
96+
97+class ChannelsTestCase(APITestCase):
98+
99+ def assert_channels(self, response):
100+ body = response.json()
101+ # assert generic embedded data structure
102+ self.assertIn('_embedded', body)
103+ self.assertIn('clickindex:channel', body['_embedded'])
104+ # assert there is at least one result
105+ self.assertGreater(len(body['_embedded']['clickindex:channel']), 0)
106+ # assert result data structure
107+ channel_data = body['_embedded']['clickindex:channel']
108+ channel_names = ('stable', 'candidate', 'beta', 'edge')
109+ for item, name in zip(channel_data, channel_names):
110+ self._assert_channel_data(item, name=name)
111+
112+ def assert_channel(self, response, name):
113+ body = response.json()
114+ # assert result data structure
115+ self._assert_channel_data(body, name=name)
116+
117+ def _assert_channel_data(self, data, name=None):
118+ self.assertIn('promotion_order', data)
119+ self.assertIn('display_name', data)
120+ self.assertIn('name', data)
121+ if name is not None:
122+ self.assertEqual(data['name'], name)
123+
124+ def test_get_channels(self):
125+ response = get_channels()
126+ self.assert_success(response)
127+ self.assert_channels(response)
128+
129+ def test_get_channel(self):
130+ response = get_channel('edge')
131+ self.assert_success(response)
132+ self.assert_channel(response, 'edge')
133
134=== added file 'tests/api/cpi/test_department.py'
135--- tests/api/cpi/test_department.py 1970-01-01 00:00:00 +0000
136+++ tests/api/cpi/test_department.py 2016-07-07 18:53:29 +0000
137@@ -0,0 +1,82 @@
138+import requests
139+
140+from .helpers import (
141+ CPI_ROOT_URL,
142+ APITestCase,
143+)
144+
145+
146+def get_departments(include_subdepartments=None):
147+ url = '{}/api/v1/departments'.format(CPI_ROOT_URL)
148+ if include_subdepartments is True:
149+ url += '?include_subdepartments=true'
150+ elif include_subdepartments is False:
151+ url += '?include_subdepartments=false'
152+ response = requests.get(url)
153+ return response
154+
155+
156+def get_department(slug):
157+ url = '{}/api/v1/departments/{}'.format(CPI_ROOT_URL, slug)
158+ response = requests.get(url)
159+ return response
160+
161+
162+class DepartmentsTestCase(APITestCase):
163+
164+ def assert_departments(self, response):
165+ body = response.json()
166+ # assert generic embedded data structure
167+ self.assertIn('_embedded', body)
168+ self.assertIn('clickindex:department', body['_embedded'])
169+ # assert there is at least one result
170+ self.assertGreater(len(body['_embedded']['clickindex:department']), 0)
171+ # assert result data structure
172+ item = body['_embedded']['clickindex:department'][0]
173+ self._assert_department_data(item)
174+
175+ def assert_department(self, response, slug):
176+ body = response.json()
177+ # assert result data structure
178+ self._assert_department_data(body, slug=slug)
179+
180+ def _assert_department_data(self, data, slug=None):
181+ self.assertIn('has_children', data)
182+ self.assertIn('name', data)
183+ self.assertIn('slug', data)
184+ if slug is not None:
185+ self.assertEqual(data['slug'], slug)
186+
187+ def find_subdepartment(self, response, department, slug):
188+ body = response.json()
189+ for item in body.get('_embedded', {}).get('clickindex:department', []):
190+ department_match = item.get('slug') == department
191+ if department_match and item.get('has_children', False):
192+ for subitem in item.get('_embedded', {}).get(
193+ 'clickindex:department', []):
194+ if subitem.get('slug') == slug:
195+ return subitem
196+
197+ def test_get_departments(self):
198+ response = get_departments()
199+ self.assert_success(response)
200+ self.assert_departments(response)
201+
202+ def test_get_department(self):
203+ response = get_department(slug='games')
204+ self.assert_success(response)
205+ self.assert_department(response, slug='games')
206+
207+ def test_include_subdepartments_by_default(self):
208+ response = get_departments()
209+ self.assert_success(response)
210+ department = self.find_subdepartment(
211+ response, 'games', slug='fps')
212+ self.assertIsNotNone(department)
213+ self._assert_department_data(department, slug='fps')
214+
215+ def test_exclude_subdepartments(self):
216+ response = get_departments(include_subdepartments=False)
217+ self.assert_success(response)
218+ department = self.find_subdepartment(response, 'games', slug='fps')
219+ self.assertIsNone(department)
220
221=== added file 'tests/api/cpi/test_framework.py'
222--- tests/api/cpi/test_framework.py 1970-01-01 00:00:00 +0000
223+++ tests/api/cpi/test_framework.py 2016-07-07 18:53:29 +0000
224@@ -0,0 +1,60 @@
225+import requests
226+
227+from .helpers import (
228+ CPI_ROOT_URL,
229+ APITestCase,
230+)
231+
232+
233+def get_frameworks():
234+ url = '{}/api/v1/frameworks'.format(CPI_ROOT_URL)
235+ response = requests.get(url)
236+ return response
237+
238+
239+def get_framework(name):
240+ url = '{}/api/v1/frameworks/{}'.format(CPI_ROOT_URL, name)
241+ response = requests.get(url)
242+ return response
243+
244+
245+class FrameworksTestCase(APITestCase):
246+
247+ def assert_frameworks(self, response):
248+ body = response.json()
249+ # assert generic embedded data structure
250+ self.assertIn('_embedded', body)
251+ self.assertIn('clickindex:framework', body['_embedded'])
252+ # assert there is at least one result
253+ self.assertGreater(len(body['_embedded']['clickindex:framework']), 0)
254+ # assert result data structure
255+ item = body['_embedded']['clickindex:framework'][0]
256+ self._assert_framework_data(item)
257+
258+ def assert_framework(self, response, name):
259+ body = response.json()
260+ # assert result data structure
261+ self._assert_framework_data(body, name=name)
262+
263+ def _assert_framework_data(self, data, name=None):
264+ self.assertIn('enable_unauthenticated_downloads', data)
265+ self.assertIn('policy_vendor', data)
266+ self.assertIn('policy_version', data)
267+ self.assertIn('state', data)
268+ self.assertIn('name', data)
269+ if name is not None:
270+ self.assertEqual(data['name'], name)
271+
272+ def test_get_frameworks(self):
273+ response = get_frameworks()
274+ self.assert_success(response)
275+ self.assert_frameworks(response)
276+
277+ def test_get_framework(self):
278+ response = get_framework(name='ubuntu-sdk-15.04')
279+ self.assert_success(response)
280+ self.assert_framework(response, name='ubuntu-sdk-15.04')
281+
282+ def test_get_framework_not_found(self):
283+ response = get_framework(name='invalid')
284+ self.assert_not_found(response, message='No such framework')
285
286=== added file 'tests/api/cpi/test_highlight.py'
287--- tests/api/cpi/test_highlight.py 1970-01-01 00:00:00 +0000
288+++ tests/api/cpi/test_highlight.py 2016-07-07 18:53:29 +0000
289@@ -0,0 +1,65 @@
290+import requests
291+
292+from .helpers import (
293+ CPI_ROOT_URL,
294+ APITestCase,
295+)
296+
297+
298+def get_highlights():
299+ url = '{}/api/v1/highlights'.format(CPI_ROOT_URL)
300+ response = requests.get(url)
301+ return response
302+
303+
304+def get_highlight(slug):
305+ url = '{}/api/v1/highlights/{}'.format(CPI_ROOT_URL, slug)
306+ response = requests.get(url)
307+ return response
308+
309+
310+class HighlightsTestCase(APITestCase):
311+
312+ def assert_highlights(self, response):
313+ body = response.json()
314+ # assert generic embedded data structure
315+ self.assertIn('_embedded', body)
316+ self.assertIn('clickindex:highlight', body['_embedded'])
317+ # assert there is at least one result
318+ self.assertGreater(len(body['_embedded']['clickindex:highlight']), 0)
319+ # assert result data structure
320+ item = body['_embedded']['clickindex:highlight'][0]
321+ self._assert_highlight_data(item)
322+
323+ def assert_highlight(self, response, slug):
324+ body = response.json()
325+ # assert result data structure
326+ self._assert_highlight_data(body, slug=slug)
327+
328+ def _assert_highlight_data(self, data, slug=None):
329+ self.assertIn('name', data)
330+ self.assertIn('description', data)
331+ self.assertIn('country_code', data)
332+ self.assertIn('slug', data)
333+ if slug is not None:
334+ self.assertEqual(data['slug'], slug)
335+
336+ def test_get_highlights(self):
337+ response = get_highlights()
338+ self.assert_success(response)
339+ self.assert_highlights(response)
340+
341+ def test_get_highlight(self):
342+ response = get_highlight(slug='top-apps')
343+ self.assert_success(response)
344+ self.assert_highlight(response, slug='top-apps')
345+
346+ def test_get_highlight_not_found(self):
347+ response = get_highlight(slug='missing')
348+ self.assert_not_found(
349+ response, message="No such highlight u'missing'")
350+
351+ def test_get_highlight_invalid_slug(self):
352+ response = get_highlight(slug='top-apps:en')
353+ self.assert_not_found(
354+ response, message="No such highlight u'top-apps:en'")
355
356=== added file 'tests/api/cpi/test_metadata.py'
357--- tests/api/cpi/test_metadata.py 1970-01-01 00:00:00 +0000
358+++ tests/api/cpi/test_metadata.py 2016-07-07 18:53:29 +0000
359@@ -0,0 +1,176 @@
360+import json
361+
362+import requests
363+
364+from .helpers import (
365+ CLICK_PACKAGE_NAME,
366+ CPI_ROOT_URL,
367+ SNAP_PACKAGE_SNAP_ID,
368+ APITestCase,
369+)
370+
371+
372+def get_click_metadata(names=None, method='get'):
373+ url = '{}/api/v1/click-metadata'.format(CPI_ROOT_URL)
374+ if method == 'get':
375+ if names is not None:
376+ url += '?' + '&'.join('name=' + name for name in names)
377+ response = requests.get(url)
378+ elif method == 'post':
379+ data = {}
380+ if names is not None:
381+ data['name'] = names
382+ response = requests.post(url, data=json.dumps(data),
383+ headers={'Content-Type': 'application/json'})
384+ else:
385+ raise ValueError('invalid method: %s' % method)
386+ return response
387+
388+
389+class ClickMetadataTestCase(APITestCase):
390+
391+ def assert_click_metadata(self, response, name):
392+ body = response.json()
393+ self.assertEqual(len(body), 1)
394+ # assert result data structure
395+ self._assert_package_data(body[0], name=name)
396+
397+ def _assert_package_data(self, data, name):
398+ self.assertIn('anon_download_url', data)
399+ self.assertIn('binary_filesize', data)
400+ self.assertIn('changelog', data)
401+ self.assertIn('channel', data)
402+ self.assertIn('content', data)
403+ self.assertIn('department', data)
404+ self.assertIn('download_sha512', data)
405+ self.assertIn('download_url', data)
406+ self.assertIn('icon_url', data)
407+ self.assertIn('name', data)
408+ self.assertIn('origin', data)
409+ self.assertIn('revision', data)
410+ self.assertIn('status', data)
411+ self.assertIn('summary', data)
412+ self.assertIn('title', data)
413+ self.assertIn('version', data)
414+ self.assertEqual(data['name'], name)
415+
416+ def test_get_click_metadata(self):
417+ response = get_click_metadata(names=[CLICK_PACKAGE_NAME])
418+ self.assert_success(response, content_type='application/json')
419+ self.assert_click_metadata(response, name=CLICK_PACKAGE_NAME)
420+
421+ def test_get_click_metadata_no_packages(self):
422+ response = get_click_metadata()
423+ self.assert_bad_request(
424+ response,
425+ message='Package names must be specified in parameter "name".')
426+
427+ def test_get_click_metadata_via_post(self):
428+ response = get_click_metadata(
429+ names=[CLICK_PACKAGE_NAME], method='post')
430+ self.assert_success(response, content_type='application/json')
431+ self.assert_click_metadata(response, name=CLICK_PACKAGE_NAME)
432+
433+ def test_get_click_metadata_no_packages_via_post(self):
434+ response = get_click_metadata(method='post')
435+ self.assert_bad_request(
436+ response,
437+ message='Package names must be specified in parameter "name".')
438+
439+ def test_get_click_metadata_bad_request_via_post(self):
440+ url = '{}/api/v1/click-metadata'.format(CPI_ROOT_URL)
441+ response = requests.post(url, data='invalid data',
442+ headers={'Content-Type': 'application/json'})
443+ self.assert_bad_request(
444+ response, message='No JSON object could be decoded')
445+
446+
447+def get_metadata(snaps=None, fields=None):
448+ url = '{}/api/v1/metadata'.format(CPI_ROOT_URL)
449+ data = {}
450+ if snaps is not None:
451+ data['snaps'] = snaps
452+ if fields is not None:
453+ data['fields'] = fields
454+ response = requests.post(url, data=json.dumps(data),
455+ headers={'Content-Type': 'application/json'})
456+ return response
457+
458+
459+class MetadataTestCase(APITestCase):
460+
461+ def assert_metadata(self, response, fields=None, snap_id=None):
462+ body = response.json()
463+ # assert generic embedded data structure
464+ self.assertIn('_embedded', body)
465+ self.assertIn('clickindex:package', body['_embedded'])
466+ # assert there is at least one result
467+ self.assertGreater(len(body['_embedded']['clickindex:package']), 0)
468+ # assert result data structure
469+ item = body['_embedded']['clickindex:package'][0]
470+ self._assert_package_data(item, fields=fields, snap_id=snap_id)
471+
472+ def _assert_package_data(self, data, fields=None, snap_id=None):
473+ if fields is None:
474+ fields = [
475+ 'anon_download_url',
476+ 'binary_filesize',
477+ 'changelog',
478+ 'channel',
479+ 'content',
480+ 'department',
481+ 'download_sha512',
482+ 'download_url',
483+ 'icon_url',
484+ 'name',
485+ 'origin',
486+ 'package_name',
487+ 'plugs',
488+ 'revision',
489+ 'slots',
490+ 'snap_id',
491+ 'status',
492+ 'summary',
493+ 'title',
494+ 'version',
495+ ]
496+ expected = fields + ['_links']
497+ self.assertEqual(set(data.keys()), set(expected))
498+ if snap_id is not None:
499+ self.assertEqual(data['snap_id'], snap_id)
500+
501+ def assert_no_results(self, response):
502+ body = response.json()
503+ self._assert_package_data(body, fields=[])
504+
505+ def test_get_metadata(self):
506+ response = get_metadata(
507+ snaps=[{'snap_id': SNAP_PACKAGE_SNAP_ID}])
508+ self.assert_success(response)
509+ self.assert_metadata(response, snap_id=SNAP_PACKAGE_SNAP_ID)
510+
511+ def test_get_metadata_no_snaps(self):
512+ response = get_metadata()
513+ self.assert_bad_request(
514+ response,
515+ message='Packages must be specified in parameter "snaps".')
516+
517+ def test_get_metadata_missing_snap_id(self):
518+ response = get_metadata(snaps=[{}])
519+ self.assert_bad_request(
520+ response, message='Missing snap_id for snap.')
521+
522+ def test_get_metadata_filter_fields(self):
523+ fields = ['channel', 'revision', 'snap_id']
524+ response = get_metadata(
525+ snaps=[{'snap_id': SNAP_PACKAGE_SNAP_ID}],
526+ fields=fields)
527+ self.assert_success(response)
528+ self.assert_metadata(
529+ response, fields=fields, snap_id=SNAP_PACKAGE_SNAP_ID)
530+
531+ def test_get_metadata_invalid_package(self):
532+ response = get_metadata(
533+ snaps=[{'snap_id': 'invalid'}])
534+ self.assert_success(response)
535+ self.assert_no_results(response)
536
537=== added file 'tests/api/cpi/test_package.py'
538--- tests/api/cpi/test_package.py 1970-01-01 00:00:00 +0000
539+++ tests/api/cpi/test_package.py 2016-07-07 18:53:29 +0000
540@@ -0,0 +1,225 @@
541+import json
542+
543+import requests
544+import requests_oauthlib
545+
546+from .helpers import (
547+ CPI_ROOT_URL,
548+ PRIVATE_SNAP_PACKAGE_CHANNEL,
549+ PRIVATE_SNAP_PACKAGE_NAME,
550+ PRIVATE_SNAP_PACKAGE_REVISION,
551+ PRIVATE_SNAP_PACKAGE_SNAP_ID,
552+ SNAP_PACKAGE_CHANNEL,
553+ SNAP_PACKAGE_NAME,
554+ SNAP_PACKAGE_REVISION,
555+ SSO_ROOT_URL,
556+ TEST_USER_EMAIL,
557+ TEST_USER_PASSWORD,
558+ APITestCase,
559+)
560+
561+
562+def get_package(package_id, channel=None, revision=None, auth=False):
563+ url = '{}/api/v1/package/{}'.format(CPI_ROOT_URL, package_id)
564+ if channel is not None:
565+ url += '/{}'.format(channel)
566+ elif revision is not None:
567+ url += '/{}'.format(revision)
568+ if auth:
569+ # use client as contextmanager to ensure ssl socket is closed
570+ # afterwards
571+ with get_oauth_client() as client:
572+ response = client.get(url)
573+ else:
574+ response = requests.get(url)
575+ return response
576+
577+
578+def get_oauth_token_data(email, password):
579+ url = '{}/api/v2/tokens/oauth'.format(SSO_ROOT_URL)
580+ data = {
581+ 'email': email,
582+ 'password': password,
583+ 'token_name': 'store-acceptance-tests',
584+ }
585+ headers = {
586+ 'Content-Type': 'application/json',
587+ 'Accept': 'application/json',
588+ }
589+ response = requests.post(url, data=json.dumps(data), headers=headers)
590+ assert response.ok, 'Failed to get oauth token: %r' % response.content
591+
592+ data = response.json()
593+ return {
594+ 'client_key': data['consumer_key'],
595+ 'client_secret': data['consumer_secret'],
596+ 'resource_owner_key': data['token_key'],
597+ 'resource_owner_secret': data['token_secret'],
598+ }
599+
600+
601+def get_oauth_client():
602+ credentials = get_oauth_token_data(TEST_USER_EMAIL, TEST_USER_PASSWORD)
603+ client = requests_oauthlib.OAuth1Session(**credentials)
604+ client.headers.update({
605+ 'Content-Type': 'application/json',
606+ 'Cache-Control': 'no-cache',
607+ })
608+ return client
609+
610+
611+class PackageBaseTestCase(APITestCase):
612+
613+ def assert_package(self, response, snap_id=None, name=None,
614+ channel=None, revision=None, exclude_fields=None):
615+ body = response.json()
616+ fields = [
617+ 'alias',
618+ 'allow_unauthenticated',
619+ 'anon_download_url',
620+ 'architecture',
621+ 'binary_filesize',
622+ 'blacklist_country_codes',
623+ 'changelog',
624+ 'channel',
625+ 'channels',
626+ 'click_framework',
627+ 'click_version',
628+ 'company_name',
629+ 'confinement',
630+ 'content',
631+ 'date_published',
632+ 'department',
633+ 'description',
634+ 'developer_id',
635+ 'developer_name',
636+ 'download_sha512',
637+ 'download_url',
638+ 'epoch',
639+ 'framework',
640+ 'icon_url',
641+ 'icon_urls',
642+ 'id',
643+ 'is_published',
644+ 'keywords',
645+ 'last_updated',
646+ 'license',
647+ 'name',
648+ 'origin',
649+ 'package_name',
650+ 'plugs',
651+ 'price',
652+ 'prices',
653+ 'promoted',
654+ 'publisher',
655+ 'ratings_average',
656+ 'release',
657+ 'revision',
658+ 'screenshot_url',
659+ 'screenshot_urls',
660+ 'slots',
661+ 'snap_id',
662+ 'status',
663+ 'summary',
664+ 'support_url',
665+ 'terms_of_service',
666+ 'title',
667+ 'version',
668+ 'video_urls',
669+ 'website',
670+ 'whitelist_country_codes',
671+ ]
672+ # assert result data structure
673+ expected = set(fields + ['_links'])
674+ if exclude_fields:
675+ expected = expected - set(exclude_fields)
676+ self.assertEqual(set(body.keys()), expected)
677+ if snap_id is not None:
678+ self.assertEqual(body['snap_id'], snap_id)
679+ if name is not None:
680+ self.assertEqual(body['package_name'], name)
681+ if channel is not None:
682+ self.assertEqual(body['channel'], channel)
683+ self.assertIn(channel, body['channels'])
684+ if revision is not None:
685+ self.assertEqual(body['revision'], revision)
686+
687+
688+class PackageTestCase(PackageBaseTestCase):
689+
690+ def test_get_package(self):
691+ response = get_package(SNAP_PACKAGE_NAME)
692+ self.assert_success(response)
693+ self.assert_package(response, name=SNAP_PACKAGE_NAME)
694+
695+ def test_get_private_package_when_not_authorized(self):
696+ response = get_package(PRIVATE_SNAP_PACKAGE_SNAP_ID)
697+ self.assert_not_found(response, message='No such package')
698+
699+ def test_get_private_package_when_authorized(self):
700+ response = get_package(PRIVATE_SNAP_PACKAGE_SNAP_ID, auth=True)
701+ self.assert_success(response)
702+ self.assert_package(
703+ response, snap_id=PRIVATE_SNAP_PACKAGE_SNAP_ID,
704+ name=PRIVATE_SNAP_PACKAGE_NAME)
705+
706+ def test_get_package_not_found(self):
707+ response = get_package('missing')
708+ self.assert_not_found(response, message='No such package')
709+
710+
711+class ChannelPackageTestCase(PackageBaseTestCase):
712+
713+ def test_get_channel_package(self):
714+ response = get_package(SNAP_PACKAGE_NAME, channel=SNAP_PACKAGE_CHANNEL)
715+ self.assert_success(response)
716+ self.assert_package(response, name=SNAP_PACKAGE_NAME,
717+ channel=SNAP_PACKAGE_CHANNEL)
718+
719+ def test_get_private_channel_package_when_not_authorized(self):
720+ response = get_package(PRIVATE_SNAP_PACKAGE_SNAP_ID,
721+ channel=PRIVATE_SNAP_PACKAGE_CHANNEL)
722+ self.assert_not_found(response, message='No such package')
723+
724+ def test_get_private_channel_package_when_authorized(self):
725+ response = get_package(PRIVATE_SNAP_PACKAGE_SNAP_ID,
726+ channel=PRIVATE_SNAP_PACKAGE_CHANNEL, auth=True)
727+ self.assert_success(response)
728+ self.assert_package(
729+ response, snap_id=PRIVATE_SNAP_PACKAGE_SNAP_ID,
730+ name=PRIVATE_SNAP_PACKAGE_NAME, channel=PRIVATE_SNAP_PACKAGE_CHANNEL)
731+
732+ def test_get_channel_package_not_found(self):
733+ response = get_package('missing', channel='stable')
734+ self.assert_not_found(response, message='No such package')
735+
736+ def test_get_channel_package_wrong_channel(self):
737+ response = get_package(SNAP_PACKAGE_NAME, channel='missing')
738+ self.assert_not_found(response, message='No such package')
739+
740+
741+class PackageRevisionTestCase(PackageBaseTestCase):
742+
743+ def test_get_package_revision(self):
744+ response = get_package(SNAP_PACKAGE_NAME, revision=SNAP_PACKAGE_REVISION)
745+ self.assert_success(response)
746+ self.assert_package(
747+ response, name=SNAP_PACKAGE_NAME, revision=SNAP_PACKAGE_REVISION,
748+ exclude_fields=['channel'])
749+
750+ def test_get_private_package_revision_when_not_authorized(self):
751+ response = get_package(PRIVATE_SNAP_PACKAGE_SNAP_ID,
752+ revision=PRIVATE_SNAP_PACKAGE_REVISION)
753+ self.assert_not_found(response, message='No such package')
754+
755+ def test_get_private_package_when_authorized(self):
756+ response = get_package(PRIVATE_SNAP_PACKAGE_SNAP_ID,
757+ revision=PRIVATE_SNAP_PACKAGE_REVISION, auth=True)
758+ self.assert_success(response)
759+ self.assert_package(
760+ response, snap_id=PRIVATE_SNAP_PACKAGE_SNAP_ID,
761+ name=PRIVATE_SNAP_PACKAGE_NAME, exclude_fields=['channel'])
762+
763+ def test_get_package_revision_not_found(self):
764+ response = get_package(SNAP_PACKAGE_NAME, revision=-1)
765+ self.assert_not_found(response, message='No such package')

Subscribers

People subscribed via source and target branches

to all changes: