Merge lp:~canonical-ci-engineering/adt-request-proxy/trunk-additional-testing into lp:adt-request-proxy

Proposed by Thomi Richards
Status: Merged
Approved by: Thomi Richards
Approved revision: 5
Merged at revision: 4
Proposed branch: lp:~canonical-ci-engineering/adt-request-proxy/trunk-additional-testing
Merge into: lp:adt-request-proxy
Diff against target: 268 lines (+149/-24)
3 files modified
adt_request_proxy/__init__.py (+8/-7)
adt_request_proxy/queue.py (+9/-11)
adt_request_proxy/tests/test_functional.py (+132/-6)
To merge this branch: bzr merge lp:~canonical-ci-engineering/adt-request-proxy/trunk-additional-testing
Reviewer Review Type Date Requested Status
Celso Providelo (community) Approve
Review via email: mp+252528@code.launchpad.net

Commit message

Add some more tests for the adt-request-proxy service.

Description of the change

Add some more tests for the adt-request-proxy service.

To post a comment you must log in.
5. By Thomi Richards

Remove unused import.

Revision history for this message
Celso Providelo (cprov) wrote :

Cool, Thomi, specially the "memory://" magic kombu URI ;)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'adt_request_proxy/__init__.py'
2--- adt_request_proxy/__init__.py 2015-03-10 20:41:29 +0000
3+++ adt_request_proxy/__init__.py 2015-03-11 01:37:05 +0000
4@@ -26,7 +26,7 @@
5 from adt_request_proxy import errors
6 from adt_request_proxy.queue import RabbitQueuer
7
8-def create_flask_app():
9+def create_flask_app(config):
10 app = flask.Flask('adt_cloud_worker')
11
12 app.add_url_rule(
13@@ -42,6 +42,8 @@
14 response.status_code = error.status
15 return response
16
17+ app.extensions['rabbit'] = RabbitQueuer(config)
18+ app.config['SWIFT_URL'] = config['swift']['public_url']
19 return app
20
21
22@@ -53,15 +55,14 @@
23 parser.add_argument('-c', '--conf', default='.adt-service.conf',
24 help='Configuration file path')
25 args = parser.parse_args()
26-
27-
28- app = create_flask_app()
29-
30 # Load configuration options & create queuer
31 config = configparser.ConfigParser()
32 config.read(args.conf)
33- app.extensions['rabbit'] = RabbitQueuer(config)
34- app.config['SWIFT_URL'] = config['swift']['public_url']
35+
36+
37+ app = create_flask_app(config)
38+
39+
40
41 app.run()
42
43
44=== modified file 'adt_request_proxy/queue.py'
45--- adt_request_proxy/queue.py 2015-03-09 03:29:34 +0000
46+++ adt_request_proxy/queue.py 2015-03-11 01:37:05 +0000
47@@ -26,14 +26,12 @@
48 """A class that can send things to the rabbit queue. An instance of this
49 is stored in 'flask.g'.
50
51- TODO: This code can almost certainly be improved - for example, maybe
52- don't create a new connection every time we queue something?
53-
54 """
55
56 def __init__(self, config):
57- self.amqp_uris = config.get('amqp', 'uris').split()
58+ self.amqp_uris = config['amqp']['uris'].split()
59 self.adt_exchange = kombu.Exchange("adt.exchange", type="topic")
60+ self.connection = kombu.Connection(self.amqp_uris)
61
62 def queue_test(self, payload):
63 """Take 'payload' and enqueue it on the rabbit queue.
64@@ -47,7 +45,8 @@
65 On error, an exception will be raised.
66
67 """
68- routing_key = payload['arch'] + '.' + payload['platform']
69+ payload_copy = payload.copy()
70+ routing_key = payload_copy['arch'] + '.' + payload_copy['platform']
71 queue = kombu.Queue(
72 'adt.requests.{}'.format(routing_key),
73 self.adt_exchange, routing_key=routing_key)
74@@ -56,10 +55,9 @@
75 # available works the request is preserved.
76 declare = [self.adt_exchange, queue]
77
78- connection = kombu.Connection(self.amqp_uris)
79- payload['request_id'] = uuid()
80+ payload_copy['request_id'] = uuid()
81
82- with producers[connection].acquire(block=True) as producer:
83+ with producers[self.connection].acquire(block=True) as producer:
84
85 def errback(exc, interval):
86 # TODO: This should raise an exception if something goes wrong.
87@@ -69,12 +67,12 @@
88 logger.error('Error: %r', exc, exc_info=1)
89 logger.info('Retry in %s seconds.', interval)
90
91- publish = connection.ensure(
92+ publish = self.connection.ensure(
93 producer, producer.publish,
94 errback=errback, max_retries=3)
95 publish(
96- payload,
97+ payload_copy,
98 exchange=self.adt_exchange,
99 routing_key=routing_key,
100 declare=declare)
101- return payload['request_id']
102+ return payload_copy['request_id']
103
104=== modified file 'adt_request_proxy/tests/test_functional.py'
105--- adt_request_proxy/tests/test_functional.py 2015-03-10 20:41:29 +0000
106+++ adt_request_proxy/tests/test_functional.py 2015-03-11 01:37:05 +0000
107@@ -17,13 +17,22 @@
108
109 """Full-stack functional tests for the web application."""
110
111+import json
112+import uuid
113+
114+import kombu
115 import testtools
116-from testtools.matchers import Equals
117+from testtools.matchers import (
118+ Contains,
119+ Equals,
120+ HasLength,
121+)
122
123 import adt_request_proxy
124-
125-
126-class FunctionalWebAppTests(testtools.TestCase):
127+from adt_request_proxy.queue import RabbitQueuer
128+
129+
130+class RequestProxyTestBase(testtools.TestCase):
131
132 """A suite of functional tests for the web app.
133
134@@ -40,10 +49,26 @@
135
136 def setUp(self):
137 super().setUp()
138- self.server_app = adt_request_proxy.create_flask_app()
139+ test_config = {
140+ 'swift': {
141+ 'public_url': 'http://swift.test.com',
142+ },
143+ 'amqp': {
144+ 'uris': 'memory://'
145+ },
146+ }
147+ self.server_app = adt_request_proxy.create_flask_app(test_config)
148 self.server_app.config['TESTING'] = True
149 self.client = self.server_app.test_client()
150
151+
152+class WebEndpointTests(RequestProxyTestBase):
153+
154+ def set_queuer_double(self):
155+ """Create and install the QueuerDouble, returning an instance of it."""
156+ instance = self.server_app.extensions['rabbit'] = QueuerDouble()
157+ return instance
158+
159 def assertEndpointDisallowsMethods(self, endpoint, methods):
160 for method in methods:
161 fn = getattr(self.client, method.lower(), None)
162@@ -59,4 +84,105 @@
163 ['GET', 'PATCH', 'DELETE']
164 )
165
166- # TODO: add more tests here ;)
167+ def test_missing_args_causes_400_error(self):
168+ q = self.set_queuer_double()
169+ resp = self.client.post('/v1/test')
170+
171+ self.assertThat(resp.status_code, Equals(400))
172+ self.assertThat(q.queued_payloads, HasLength(0))
173+
174+ def test_can_queue_test_with_all_parameters_present(self):
175+ """Test that we can enqueue test payloads, and that the response we
176+ get is sensible.
177+
178+ This should perhaps be broken down into smaller tests if it grows
179+ much more.
180+
181+ """
182+ q = self.set_queuer_double()
183+ resp = self.client.post(
184+ '/v1/test',
185+ data = {
186+ 'package-name': 'librepng',
187+ 'architecture': 'i386',
188+ 'platform': 'nova',
189+ }
190+ )
191+ self.assertThat(resp.status_code, Equals(200))
192+ self.assertThat(q.queued_payloads, HasLength(1))
193+ self.assertThat(resp.mimetype, Equals("application/json"))
194+ data = json.loads(resp.data.decode())
195+ self.assertThat(
196+ data['result_url'],
197+ Equals('http://swift.test.com/{}/results.tgz'.format(
198+ q.generated_uuids[0]
199+ ))
200+ )
201+
202+
203+class QueuerDouble(object):
204+
205+ """A test double for adt_request_proxy.queue.RabbitQueuer.
206+
207+ Simply adds queued payloads to a list, and implements methods to make
208+ assertions on the queued payloads.
209+
210+ """
211+
212+ def __init__(self):
213+ self.queued_payloads = []
214+ self.generated_uuids = []
215+
216+ def queue_test(self, payload):
217+ request_id = str(uuid.uuid1())
218+ payload['request_id'] = request_id
219+ self.queued_payloads.append(payload)
220+ self.generated_uuids.append(request_id)
221+ return payload['request_id']
222+
223+
224+class QueuerTests(RequestProxyTestBase):
225+
226+ """Tests for the rabbit queue class.
227+
228+ The RabbitQueuer is highly stateful, so it's almost impossible to test with
229+ unit tests.
230+
231+ What we can do, which is a reasonable half-measure is to configure the
232+ queuer class with an in-memory transport, and attempt to rigerously test the
233+ interface between the queuer and it's caller.
234+
235+ """
236+ def test_queueing_does_not_alter_payload(self):
237+ q = RabbitQueuer(dict(amqp=dict(uris='memory://')))
238+
239+ test_payload = {
240+ 'package_name': 'librepng',
241+ 'arch': 'amd64',
242+ 'platform': 'nova',
243+ 'nova_image': 'some-nova-image',
244+ }
245+ copy_payload = test_payload.copy()
246+ request_id = q.queue_test(copy_payload)
247+ self.assertEqual(test_payload, copy_payload)
248+
249+ def test_attempt_queue_test(self):
250+ q = RabbitQueuer(dict(amqp=dict(uris='memory://')))
251+
252+ test_payload = {
253+ 'package_name': 'librepng',
254+ 'arch': 'amd64',
255+ 'platform': 'nova',
256+ 'nova_image': 'some-nova-image',
257+ }
258+ request_id = q.queue_test(test_payload)
259+
260+ # make sure the message got on the channel:
261+ conn = kombu.Connection("memory://")
262+ consumer_queue = conn.SimpleQueue("adt.requests.amd64.nova")
263+ message = consumer_queue.get(timeout=10.0)
264+ message.ack()
265+
266+ # message must have request_id set:
267+ test_payload['request_id'] = request_id
268+ self.assertEqual(test_payload, message.payload)

Subscribers

People subscribed via source and target branches

to all changes: