Merge lp:~canonical-platform-qa/snappy-ecosystem-tests/test-release-snap-multiple-channels into lp:snappy-ecosystem-tests

Proposed by Heber Parrucci
Status: Merged
Approved by: Omer Akram
Approved revision: 54
Merged at revision: 55
Proposed branch: lp:~canonical-platform-qa/snappy-ecosystem-tests/test-release-snap-multiple-channels
Merge into: lp:snappy-ecosystem-tests
Diff against target: 268 lines (+184/-10)
6 files modified
pylint.cfg (+1/-1)
snappy_ecosystem_tests/helpers/snapcraft/client.py (+31/-1)
snappy_ecosystem_tests/helpers/store_apis/rest_apis.py (+30/-5)
snappy_ecosystem_tests/tests/test_push_snap.py (+100/-0)
snappy_ecosystem_tests/tests/test_store_apis_login.py (+3/-3)
snappy_ecosystem_tests/unittests/__init__.py (+19/-0)
To merge this branch: bzr merge lp:~canonical-platform-qa/snappy-ecosystem-tests/test-release-snap-multiple-channels
Reviewer Review Type Date Requested Status
Omer Akram (community) Approve
platform-qa-bot continuous-integration Approve
Review via email: mp+321342@code.launchpad.net

Commit message

Adding test cases:
* Automate Push and release a snap to a multiple channels via command and verify it via RESTful and verify it is available in all channels where it was published
* Automate Snapcraft can push against a new snap registered via RESTful API
* Push an invalid snap file via command and check it was not updated via RESTful API

Description of the change

Automate the scenarios:

* Push and release a snap to a multiple channels via command and verify it via RESTful and verify it is available in all channels where it was published

* Snapcraft can push against a new snap registered via RESTful API

* Push an invalid snap file via command and check it was not updated via RESTful API

For details refer to:

https://prod.practitest.com/p/2505/tests/781225/edit
https://prod.practitest.com/p/2505/tests/781207/edit
https://prod.practitest.com/p/2505/tests/781219/edit

@run_tests: snappy_ecosystem_tests/tests/test_push_snap.py::PushSnapTestCase

To post a comment you must log in.
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
50. By Heber Parrucci

Adding test_push_snap and re-organizing files

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
51. By Heber Parrucci

merge from trunk

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
52. By Heber Parrucci

Adding test:
push invalid snap file

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Omer Akram (om26er) wrote :

Code looks good to me, will wait for test results.

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
53. By Heber Parrucci

merge from trunk

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
54. By Heber Parrucci

merge from trunk

Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
platform-qa-bot (platform-qa-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Omer Akram (om26er) wrote :

\o/

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'pylint.cfg'
2--- pylint.cfg 2017-03-08 17:59:46 +0000
3+++ pylint.cfg 2017-04-04 20:02:43 +0000
4@@ -280,7 +280,7 @@
5 ignore-docstrings=yes
6
7 # Ignore imports when computing similarities.
8-ignore-imports=no
9+ignore-imports=yes
10
11
12 [MISCELLANEOUS]
13
14=== modified file 'snappy_ecosystem_tests/helpers/snapcraft/client.py'
15--- snappy_ecosystem_tests/helpers/snapcraft/client.py 2017-03-30 16:38:42 +0000
16+++ snappy_ecosystem_tests/helpers/snapcraft/client.py 2017-04-04 20:02:43 +0000
17@@ -57,7 +57,7 @@
18
19 COMMAND_REGISTER = 'register'
20 COMMAND_LIST_REGISTERED = 'list-registered'
21-
22+COMMAND_PUSH = 'push'
23 LOGGER = logging.getLogger(__name__)
24
25 HOSTNAME, USERNAME, PORT = get_snapcraft_remote_host_credentials()
26@@ -92,6 +92,7 @@
27 GRADE_SNAP_DEFAULT = 'stable'
28 CONFINEMENT_SNAP_DEFAULT = 'strict'
29
30+
31 class Snapcraft:
32 """Contain Snapcraft specific functionality to use via command
33 line interface"""
34@@ -311,6 +312,35 @@
35 self.ssh.pull(remote_snap_path, output_path_absolute)
36 return output_path_absolute
37
38+ def push(self, snap_file, channels=None):
39+ """
40+ Push a snap. If channels are provided it will release as well.
41+ :param snap_file: the snap's full path
42+ :param channels: the list of channels to release the snap.
43+ """
44+ command = '{} {}'.format(COMMAND_PUSH, snap_file)
45+ if channels:
46+ command = '{} {} {}'.format(command,
47+ '--release', ','.join(channels))
48+ self._run_snapcraft_command_ssh(command)
49+
50+ def make_dummy_snap_file(self, name, version, arch=None):
51+ """
52+ Create an empty snap file
53+ :param name: the snap name
54+ :param version: the snap version
55+ :param arch: the snap architecture,
56+ if None it takes the host one.
57+ :return: the snap file full path
58+ """
59+ tempdir = self.ssh.run_command('mktemp -d')
60+ file_name = '{name}_{version}_{arch}.snap'.format(
61+ name=name,
62+ version=version,
63+ arch=arch or self.ssh.run_command('dpkg --print-architecture'))
64+ self.ssh.run_command('touch {}'.format(file_name), cwd=tempdir)
65+ return os.path.join(tempdir, file_name)
66+
67
68 def build_snapcraft_command(*args, base_command=PATH_SNAPCRAFT):
69 """
70
71=== modified file 'snappy_ecosystem_tests/helpers/store_apis/rest_apis.py'
72--- snappy_ecosystem_tests/helpers/store_apis/rest_apis.py 2017-03-10 10:18:26 +0000
73+++ snappy_ecosystem_tests/helpers/store_apis/rest_apis.py 2017-04-04 20:02:43 +0000
74@@ -154,14 +154,10 @@
75 if save:
76 self.conf.save()
77
78- @property
79- def config(self):
80- """Get current config"""
81- return self.conf
82-
83 def load_config(self):
84 """Load current config"""
85 self.conf.load()
86+ return self.conf
87
88 def _extract_caveat_id(self, root_macaroon):
89 """Extract caveat id for the given macaroon
90@@ -299,6 +295,35 @@
91
92 return response
93
94+ def is_snap_published(self, snap_name, version, revision, channels,
95+ series=None, arch=None, **kwargs):
96+ """
97+ Check if snap revision and version is published in all given channels
98+
99+ :param snap_name: the snap to check
100+ :param version: the snap version to check
101+ :param revision: the snap revision to check
102+ :param channels: the list of channels to check
103+ :param series: the snap series
104+ :param arch: the snap architecture
105+ :return: True if the snap revision is published in all given channels,
106+ False otherwise
107+ """
108+ retries = kwargs.get('retries', 10)
109+ wait = kwargs.get('wait', 2)
110+ attempt = 0
111+ channels_published = []
112+ while attempt < retries:
113+ status = self.get_snap_status(snap_name, series=series, arch=arch)
114+ for arch, details in status.items():
115+ channels_published = [d['channel'] for d in details if d.get(
116+ 'revision') == revision and d.get('version') == version]
117+ if sorted(channels_published) == sorted(channels):
118+ return True
119+ sleep(wait)
120+ attempt += 1
121+ return False
122+
123 def close_channels(self, snap_id, channel_names):
124 """Close channels for the given snap_id.
125 First refresh macaroon if necessary"""
126
127=== added file 'snappy_ecosystem_tests/tests/test_push_snap.py'
128--- snappy_ecosystem_tests/tests/test_push_snap.py 1970-01-01 00:00:00 +0000
129+++ snappy_ecosystem_tests/tests/test_push_snap.py 2017-04-04 20:02:43 +0000
130@@ -0,0 +1,100 @@
131+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
132+
133+#
134+# Snappy Ecosystem Tests
135+# Copyright (C) 2017 Canonical
136+#
137+# This program is free software: you can redistribute it and/or modify
138+# it under the terms of the GNU General Public License as published by
139+# the Free Software Foundation, either version 3 of the License, or
140+# (at your option) any later version.
141+#
142+# This program is distributed in the hope that it will be useful,
143+# but WITHOUT ANY WARRANTY; without even the implied warranty of
144+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
145+# GNU General Public License for more details.
146+#
147+# You should have received a copy of the GNU General Public License
148+# along with this program. If not, see <http://www.gnu.org/licenses/>.
149+#
150+
151+"""Tests for pushing snaps to the store."""
152+
153+from testtools import ExpectedException
154+
155+from snappy_ecosystem_tests.helpers.snapcraft.client import Snapcraft
156+from snappy_ecosystem_tests.helpers.store_apis.errors import (
157+ StoreSnapHistoryError
158+)
159+from snappy_ecosystem_tests.helpers.store_apis.rest_apis import Store
160+from snappy_ecosystem_tests.helpers.test_base import SnappyEcosystemTestCase
161+from snappy_ecosystem_tests.tests.commons import get_random_snap_name
162+from snappy_ecosystem_tests.utils.storeconfig import get_store_credentials
163+
164+
165+class PushSnapTestCase(SnappyEcosystemTestCase):
166+
167+ def setUp(self):
168+ super().setUp()
169+ self.store = Store()
170+ self.snapcraft = Snapcraft()
171+ email, password = get_store_credentials()
172+ self.assertTrue(self.snapcraft.login(email, password))
173+ self.addCleanup(self.snapcraft.logout)
174+ self.store.login(email, password)
175+ self.addCleanup(self.store.logout)
176+
177+ def test_push_snap(self):
178+ """Verify that snapcraft can push a snap against a new snap registered
179+ via RESTful API
180+ """
181+ snap_name = get_random_snap_name()
182+ revision = 1
183+ version = '0.1'
184+ self.assertTrue(self.store.register(snap_name),
185+ 'Unable to register snap')
186+ # Push the snap via Snapcraft
187+ self.snapcraft.push(self.snapcraft.build(snap_name, version))
188+ # Check using REST API if snap was pushed
189+ snap_history = self.store.get_snap_history(snap_name)
190+ self.assertEqual(1, len(snap_history))
191+ snap = snap_history[0]
192+ self.assertEqual(revision, snap['revision'])
193+ self.assertEqual(version, snap['version'])
194+
195+ def test_push_and_release_snap_multiple_channels(self):
196+ """Test push and release a snap to multiple channels and verify REST API
197+ and verify it was published via REST API
198+ """
199+ snap_name = get_random_snap_name()
200+ channels = ['candidate', 'stable']
201+ revision = 1
202+ version = '0.1'
203+ self.assertTrue(self.store.register(snap_name),
204+ 'Unable to register snap')
205+ # Push and release the snap to multiple channels
206+ self.snapcraft.push(self.snapcraft.build(snap_name, version),
207+ channels=channels)
208+ # Check using REST API if snap was released to all channels
209+ self.assertTrue(self.store.is_snap_published(snap_name,
210+ version,
211+ revision,
212+ channels))
213+
214+ def test_push_invalid_snap(self):
215+ """Push an invalid snap file via command and check it was not updated
216+ via RESTful API
217+ """
218+ snap_name = get_random_snap_name()
219+ version = '0.1'
220+ self.assertTrue(self.store.register(snap_name),
221+ 'Unable to register snap')
222+ # Create and Push the invalid snap via Snapcraft
223+ snap_path = self.snapcraft.make_dummy_snap_file(snap_name, version)
224+ with ExpectedException(ValueError,
225+ value_re='.*Read on filesystem failed.*'):
226+ self.snapcraft.push(snap_path)
227+ # Verify that the snap was not pushed using REST API
228+ with ExpectedException(StoreSnapHistoryError,
229+ value_re='.*Snap not found.*'):
230+ self.store.get_snap_history(snap_name)
231
232=== modified file 'snappy_ecosystem_tests/tests/test_store_apis_login.py'
233--- snappy_ecosystem_tests/tests/test_store_apis_login.py 2017-02-17 18:30:58 +0000
234+++ snappy_ecosystem_tests/tests/test_store_apis_login.py 2017-04-04 20:02:43 +0000
235@@ -34,6 +34,6 @@
236 def test_api_login_logout(self):
237 """Test login and logout functionality in store REST APIs"""
238 self.client.login(*get_store_credentials())
239- self.client.load_config()
240- self.assertIsNotNone(self.client.config.get('macaroon'))
241- self.assertIsNotNone(self.client.config.get('unbound_discharge'))
242+ config = self.client.load_config()
243+ self.assertIsNotNone(config.get('macaroon'))
244+ self.assertIsNotNone(config.get('unbound_discharge'))
245
246=== modified file 'snappy_ecosystem_tests/unittests/__init__.py'
247--- snappy_ecosystem_tests/unittests/__init__.py 2017-02-10 18:18:45 +0000
248+++ snappy_ecosystem_tests/unittests/__init__.py 2017-04-04 20:02:43 +0000
249@@ -0,0 +1,19 @@
250+# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
251+
252+#
253+# Snappy Ecosystem Tests
254+# Copyright (C) 2017 Canonical
255+#
256+# This program is free software: you can redistribute it and/or modify
257+# it under the terms of the GNU General Public License as published by
258+# the Free Software Foundation, either version 3 of the License, or
259+# (at your option) any later version.
260+#
261+# This program is distributed in the hope that it will be useful,
262+# but WITHOUT ANY WARRANTY; without even the implied warranty of
263+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
264+# GNU General Public License for more details.
265+#
266+# You should have received a copy of the GNU General Public License
267+# along with this program. If not, see <http://www.gnu.org/licenses/>.
268+#

Subscribers

People subscribed via source and target branches