Merge lp:~ricardokirkner/ols-store-tests/more-acceptance-tests into lp:~ubuntuone-pqm-team/ols-store-tests/store-acceptance-tests
- more-acceptance-tests
- Merge into 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 |
Related bugs: |
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
Description of the change
To post a comment you must log in.
- 25. By Ricardo Kirkner
-
refactor tests to use constant
- 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') |
Looking good, added a few comments/questions.