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

Proposed by Ricardo Kirkner
Status: Merged
Merged at revision: 20
Proposed branch: lp:~ricardokirkner/ols-store-tests/more-acceptance-tests-1
Merge into: lp:~ubuntuone-pqm-team/ols-store-tests/store-acceptance-tests
Diff against target: 492 lines (+304/-64)
6 files modified
tests/api/cpi/helpers.py (+41/-0)
tests/api/cpi/test_metadata.py (+18/-17)
tests/api/cpi/test_package.py (+1/-36)
tests/api/cpi/test_recommendation.py (+35/-0)
tests/api/cpi/test_search.py (+68/-11)
tests/api/cpi/test_snaps.py (+141/-0)
To merge this branch: bzr merge lp:~ricardokirkner/ols-store-tests/more-acceptance-tests-1
Reviewer Review Type Date Requested Status
Matias Bordese (community) Approve
Review via email: mp+299462@code.launchpad.net

Commit message

added more smoke tests

added tests for:

- search
- snaps
- recommendations

To post a comment you must log in.
Revision history for this message
Matias Bordese (matiasb) wrote :

LGTM, with a suggestion.

review: Approve
30. By Ricardo Kirkner

decouple click from snap search endpoints

Revision history for this message
Ricardo Kirkner (ricardokirkner) :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'tests/api/cpi/helpers.py'
2--- tests/api/cpi/helpers.py 2016-07-07 15:41:03 +0000
3+++ tests/api/cpi/helpers.py 2016-07-07 19:50:23 +0000
4@@ -1,7 +1,10 @@
5 import getpass
6+import json
7 import os
8 import unittest
9
10+import requests
11+import requests_oauthlib
12
13 CPI_ROOT_URL = os.getenv(
14 'CPI_ROOT_URL', 'https://search.apps.staging.ubuntu.com')
15@@ -26,6 +29,39 @@
16 PRIVATE_SNAP_PACKAGE_REVISION = 1
17
18
19+def get_oauth_token_data(email, password):
20+ url = '{}/api/v2/tokens/oauth'.format(SSO_ROOT_URL)
21+ data = {
22+ 'email': email,
23+ 'password': password,
24+ 'token_name': 'store-acceptance-tests',
25+ }
26+ headers = {
27+ 'Content-Type': 'application/json',
28+ 'Accept': 'application/json',
29+ }
30+ response = requests.post(url, data=json.dumps(data), headers=headers)
31+ assert response.ok, 'Failed to get oauth token: %r' % response.content
32+
33+ data = response.json()
34+ return {
35+ 'client_key': data['consumer_key'],
36+ 'client_secret': data['consumer_secret'],
37+ 'resource_owner_key': data['token_key'],
38+ 'resource_owner_secret': data['token_secret'],
39+ }
40+
41+
42+def get_oauth_client():
43+ credentials = get_oauth_token_data(TEST_USER_EMAIL, TEST_USER_PASSWORD)
44+ client = requests_oauthlib.OAuth1Session(**credentials)
45+ client.headers.update({
46+ 'Content-Type': 'application/json',
47+ 'Cache-Control': 'no-cache',
48+ })
49+ return client
50+
51+
52 class APITestCase(unittest.TestCase):
53
54 def assert_success(self, response, content_type='application/hal+json'):
55@@ -50,3 +86,8 @@
56 'result': 'error',
57 'errors': [message],
58 })
59+
60+ def assert_unauthorized(self, response):
61+ self.assertEqual(response.status_code, 401)
62+ self.assertEqual(response.headers['Content-Type'], 'text/plain')
63+ self.assertIn('WWW-Authenticate', response.headers)
64
65=== modified file 'tests/api/cpi/test_metadata.py'
66--- tests/api/cpi/test_metadata.py 2016-07-07 18:53:00 +0000
67+++ tests/api/cpi/test_metadata.py 2016-07-07 19:50:23 +0000
68@@ -85,19 +85,20 @@
69 response, message='No JSON object could be decoded')
70
71
72-def get_metadata(snaps=None, fields=None):
73- url = '{}/api/v1/metadata'.format(CPI_ROOT_URL)
74- data = {}
75- if snaps is not None:
76- data['snaps'] = snaps
77- if fields is not None:
78- data['fields'] = fields
79- response = requests.post(url, data=json.dumps(data),
80- headers={'Content-Type': 'application/json'})
81- return response
82-
83-
84 class MetadataTestCase(APITestCase):
85+ endpoint = '/api/v1/metadata'
86+
87+ def get_metadata(self, snaps=None, fields=None):
88+ url = '{}{}'.format(CPI_ROOT_URL, self.endpoint)
89+ data = {}
90+ if snaps is not None:
91+ data['snaps'] = snaps
92+ if fields is not None:
93+ data['fields'] = fields
94+ response = requests.post(
95+ url, data=json.dumps(data),
96+ headers={'Content-Type': 'application/json'})
97+ return response
98
99 def assert_metadata(self, response, fields=None, snap_id=None):
100 body = response.json()
101@@ -144,25 +145,25 @@
102 self._assert_package_data(body, fields=[])
103
104 def test_get_metadata(self):
105- response = get_metadata(
106+ response = self.get_metadata(
107 snaps=[{'snap_id': SNAP_PACKAGE_SNAP_ID}])
108 self.assert_success(response)
109 self.assert_metadata(response, snap_id=SNAP_PACKAGE_SNAP_ID)
110
111 def test_get_metadata_no_snaps(self):
112- response = get_metadata()
113+ response = self.get_metadata()
114 self.assert_bad_request(
115 response,
116 message='Packages must be specified in parameter "snaps".')
117
118 def test_get_metadata_missing_snap_id(self):
119- response = get_metadata(snaps=[{}])
120+ response = self.get_metadata(snaps=[{}])
121 self.assert_bad_request(
122 response, message='Missing snap_id for snap.')
123
124 def test_get_metadata_filter_fields(self):
125 fields = ['channel', 'revision', 'snap_id']
126- response = get_metadata(
127+ response = self.get_metadata(
128 snaps=[{'snap_id': SNAP_PACKAGE_SNAP_ID}],
129 fields=fields)
130 self.assert_success(response)
131@@ -170,7 +171,7 @@
132 response, fields=fields, snap_id=SNAP_PACKAGE_SNAP_ID)
133
134 def test_get_metadata_invalid_package(self):
135- response = get_metadata(
136+ response = self.get_metadata(
137 snaps=[{'snap_id': 'invalid'}])
138 self.assert_success(response)
139 self.assert_no_results(response)
140
141=== modified file 'tests/api/cpi/test_package.py'
142--- tests/api/cpi/test_package.py 2016-07-07 15:41:03 +0000
143+++ tests/api/cpi/test_package.py 2016-07-07 19:50:23 +0000
144@@ -1,7 +1,4 @@
145-import json
146-
147 import requests
148-import requests_oauthlib
149
150 from .helpers import (
151 CPI_ROOT_URL,
152@@ -16,6 +13,7 @@
153 TEST_USER_EMAIL,
154 TEST_USER_PASSWORD,
155 APITestCase,
156+ get_oauth_client,
157 )
158
159
160@@ -35,39 +33,6 @@
161 return response
162
163
164-def get_oauth_token_data(email, password):
165- url = '{}/api/v2/tokens/oauth'.format(SSO_ROOT_URL)
166- data = {
167- 'email': email,
168- 'password': password,
169- 'token_name': 'store-acceptance-tests',
170- }
171- headers = {
172- 'Content-Type': 'application/json',
173- 'Accept': 'application/json',
174- }
175- response = requests.post(url, data=json.dumps(data), headers=headers)
176- assert response.ok, 'Failed to get oauth token: %r' % response.content
177-
178- data = response.json()
179- return {
180- 'client_key': data['consumer_key'],
181- 'client_secret': data['consumer_secret'],
182- 'resource_owner_key': data['token_key'],
183- 'resource_owner_secret': data['token_secret'],
184- }
185-
186-
187-def get_oauth_client():
188- credentials = get_oauth_token_data(TEST_USER_EMAIL, TEST_USER_PASSWORD)
189- client = requests_oauthlib.OAuth1Session(**credentials)
190- client.headers.update({
191- 'Content-Type': 'application/json',
192- 'Cache-Control': 'no-cache',
193- })
194- return client
195-
196-
197 class PackageBaseTestCase(APITestCase):
198
199 def assert_package(self, response, snap_id=None, name=None,
200
201=== added file 'tests/api/cpi/test_recommendation.py'
202--- tests/api/cpi/test_recommendation.py 1970-01-01 00:00:00 +0000
203+++ tests/api/cpi/test_recommendation.py 2016-07-07 19:50:23 +0000
204@@ -0,0 +1,35 @@
205+import requests
206+
207+from .helpers import (
208+ CPI_ROOT_URL,
209+ APITestCase,
210+)
211+
212+
213+def get_recommendation(slug):
214+ url = '{}/api/v1/recommendations/{}'.format(CPI_ROOT_URL, slug)
215+ response = requests.get(url)
216+ return response
217+
218+
219+class RecommendationsTestCase(APITestCase):
220+
221+ def assert_recommendation(self, response, slug=None):
222+ body = response.json()
223+ # assert result data structure
224+ self.assertIn('applications', body)
225+ self.assertGreater(len(body['applications']), 0)
226+ self.assertIn('hotwords', body)
227+ self.assertIn('slug', body)
228+ if slug is not None:
229+ self.assertEqual(body['slug'], slug)
230+
231+ def test_get_recommendation(self):
232+ response = get_recommendation('hello')
233+ self.assert_success(response)
234+ self.assert_recommendation(response, 'hello')
235+
236+ def test_get_recommendation_not_found(self):
237+ response = get_recommendation('missing')
238+ self.assert_not_found(
239+ response, message="No such recommendation u'missing'")
240
241=== modified file 'tests/api/cpi/test_search.py'
242--- tests/api/cpi/test_search.py 2016-06-28 19:20:09 +0000
243+++ tests/api/cpi/test_search.py 2016-07-07 19:50:23 +0000
244@@ -1,24 +1,50 @@
245+from urllib.parse import urlencode
246+
247 import requests
248
249 from .helpers import (
250 CPI_ROOT_URL,
251+ PRIVATE_SNAP_PACKAGE_NAME,
252 APITestCase,
253+ get_oauth_client,
254 )
255
256
257-def search(name=''):
258- url = '{}/api/v1/search?q=name:{}'.format(CPI_ROOT_URL, name)
259- response = requests.get(url)
260- return response
261-
262-
263 class SearchTestCase(APITestCase):
264+ endpoint = '/api/v1/search'
265
266 def setUp(self):
267 super(SearchTestCase, self).setUp()
268 self.name = 'hello'
269
270- def assert_body_success(self, response, name):
271+ def search(self, name=None, fields=None, auth=False):
272+ query_data = {}
273+ if name is not None:
274+ query_data['name'] = name
275+
276+ query = {}
277+ if query_data:
278+ query['q'] = '&'.join(
279+ '{}:{}'.format(key, value)
280+ for key, value in query_data.items())
281+ if fields:
282+ query['fields'] = fields
283+
284+ return self._search(query=query, auth=auth)
285+
286+ def _search(self, query=None, auth=False):
287+ url = '{}{}'.format(CPI_ROOT_URL, self.endpoint)
288+ if query:
289+ qs = urlencode(query)
290+ url += '?' + qs
291+ if auth:
292+ with get_oauth_client() as client:
293+ response = client.get(url)
294+ else:
295+ response = requests.get(url)
296+ return response
297+
298+ def assert_results(self, response, name=None):
299 body = response.json()
300 # assert generic embedded data structure
301 self.assertIn('_embedded', body)
302@@ -27,9 +53,40 @@
303 self.assertGreater(len(body['_embedded']['clickindex:package']), 0)
304 # assert result data structure
305 item = body['_embedded']['clickindex:package'][0]
306- self.assertIn(name, item['name'])
307+ if name is not None:
308+ self.assertIn(name, item['name'])
309+
310+ def assert_no_results(self, response, name):
311+ body = response.json()
312+ embedded = body.get('_embedded', {})
313+ results = embedded.get('clickindex:package', [])
314+ for item in results:
315+ self.assertNotIn(name, item['name'])
316+
317+ def assert_recommendations(self, response):
318+ body = response.json()
319+ self.assertIn('_embedded', body)
320+ self.assertIn('clickindex:recommendation', body['_embedded'])
321+ # assert there is at least one result
322+ self.assertGreater(
323+ len(body['_embedded']['clickindex:recommendation']), 0)
324
325 def test_search_by_name(self):
326- response = search(name=self.name)
327- self.assert_success(response)
328- self.assert_body_success(response, self.name)
329+ response = self.search(name=self.name)
330+ self.assert_success(response)
331+ self.assert_results(response, self.name)
332+
333+ def test_search_all(self):
334+ response = self.search()
335+ self.assert_success(response)
336+ self.assert_results(response)
337+
338+ def test_search_private_not_authorized(self):
339+ response = self.search(name=PRIVATE_SNAP_PACKAGE_NAME)
340+ self.assert_success(response)
341+ self.assert_no_results(response, name=PRIVATE_SNAP_PACKAGE_NAME)
342+
343+ def test_search_private(self):
344+ response = self.search(name=PRIVATE_SNAP_PACKAGE_NAME, auth=True)
345+ self.assert_success(response)
346+ self.assert_results(response, name=PRIVATE_SNAP_PACKAGE_NAME)
347
348=== added file 'tests/api/cpi/test_snaps.py'
349--- tests/api/cpi/test_snaps.py 1970-01-01 00:00:00 +0000
350+++ tests/api/cpi/test_snaps.py 2016-07-07 19:50:23 +0000
351@@ -0,0 +1,141 @@
352+import requests
353+
354+from .helpers import (
355+ CPI_ROOT_URL,
356+ SNAP_PACKAGE_NAME,
357+ PRIVATE_SNAP_PACKAGE_NAME,
358+ APITestCase,
359+ get_oauth_client,
360+)
361+from .test_metadata import MetadataTestCase
362+from .test_search import SearchTestCase
363+
364+
365+def get_snap_detail(name, series='16', auth=False):
366+ url = '{}/api/v1/snaps/details/{}'.format(CPI_ROOT_URL, name)
367+ headers = {}
368+ if series:
369+ headers['X-Ubuntu-Series'] = series
370+ if auth:
371+ with get_oauth_client() as client:
372+ response = client.get(url, headers=headers)
373+ else:
374+ response = requests.get(url, headers=headers)
375+ return response
376+
377+
378+class SnapDetailTestCase(APITestCase):
379+
380+ def assert_snap_detail(self, response, name):
381+ body = response.json()
382+ fields = [
383+ 'alias',
384+ 'allow_unauthenticated',
385+ 'anon_download_url',
386+ 'architecture',
387+ 'binary_filesize',
388+ 'blacklist_country_codes',
389+ 'changelog',
390+ 'channel',
391+ 'channels',
392+ 'click_framework',
393+ 'click_version',
394+ 'company_name',
395+ 'confinement',
396+ 'content',
397+ 'date_published',
398+ 'department',
399+ 'description',
400+ 'developer_id',
401+ 'developer_name',
402+ 'download_sha512',
403+ 'download_url',
404+ 'epoch',
405+ 'framework',
406+ 'icon_url',
407+ 'icon_urls',
408+ 'id',
409+ 'is_published',
410+ 'keywords',
411+ 'last_updated',
412+ 'license',
413+ 'name',
414+ 'origin',
415+ 'package_name',
416+ 'plugs',
417+ 'price',
418+ 'prices',
419+ 'promoted',
420+ 'publisher',
421+ 'ratings_average',
422+ 'release',
423+ 'revision',
424+ 'screenshot_url',
425+ 'screenshot_urls',
426+ 'slots',
427+ 'snap_id',
428+ 'status',
429+ 'summary',
430+ 'support_url',
431+ 'terms_of_service',
432+ 'title',
433+ 'version',
434+ 'video_urls',
435+ 'website',
436+ 'whitelist_country_codes',
437+ ]
438+ for field in fields:
439+ self.assertIn(field, body)
440+ self.assertEqual(body['package_name'], name)
441+
442+ def test_get_snap_detail(self):
443+ response = get_snap_detail(SNAP_PACKAGE_NAME)
444+ self.assert_success(response)
445+ self.assert_snap_detail(response, SNAP_PACKAGE_NAME)
446+
447+ def test_get_snap_detail_for_private_package(self):
448+ response = get_snap_detail(PRIVATE_SNAP_PACKAGE_NAME, auth=True)
449+ self.assert_success(response)
450+ self.assert_snap_detail(response, PRIVATE_SNAP_PACKAGE_NAME)
451+
452+ def test_get_snap_detail_without_series(self):
453+ response = get_snap_detail(SNAP_PACKAGE_NAME, series=None)
454+ self.assert_bad_request(
455+ response, message='X-Ubuntu-Series header is required.')
456+
457+ def test_get_snap_detail_unauthorized(self):
458+ response = get_snap_detail(PRIVATE_SNAP_PACKAGE_NAME, auth=False)
459+ self.assert_not_found(response, message='No such package')
460+
461+ def test_get_snap_detail_not_found(self):
462+ response = get_snap_detail('missing')
463+ self.assert_not_found(response, message='No such package')
464+
465+
466+class SnapMetadataTestCase(MetadataTestCase):
467+ endpoint = '/api/v1/snaps/metadata'
468+
469+
470+class SnapSearchTestCase(SearchTestCase):
471+ endpoint = '/api/v1/snaps/search'
472+
473+ def search(self, name=None, fields=None, private=False, auth=False):
474+ query = {}
475+ if name is not None:
476+ query['name'] = name
477+ if private:
478+ query['private'] = 'true'
479+ if fields is not None:
480+ query['fields'] = fields
481+ return super(SnapSearchTestCase, self)._search(
482+ query=query, auth=auth)
483+
484+ def test_search_private_not_authorized(self):
485+ response = self.search(name=PRIVATE_SNAP_PACKAGE_NAME, private=True)
486+ self.assert_unauthorized(response)
487+
488+ def test_search_private(self):
489+ response = self.search(name=PRIVATE_SNAP_PACKAGE_NAME, private=True,
490+ auth=True)
491+ self.assert_success(response)
492+ self.assert_results(response, name=PRIVATE_SNAP_PACKAGE_NAME)

Subscribers

People subscribed via source and target branches

to all changes: