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
=== modified file 'adt_request_proxy/__init__.py'
--- adt_request_proxy/__init__.py 2015-03-10 20:41:29 +0000
+++ adt_request_proxy/__init__.py 2015-03-11 01:37:05 +0000
@@ -26,7 +26,7 @@
26from adt_request_proxy import errors26from adt_request_proxy import errors
27from adt_request_proxy.queue import RabbitQueuer27from adt_request_proxy.queue import RabbitQueuer
2828
29def create_flask_app():29def create_flask_app(config):
30 app = flask.Flask('adt_cloud_worker')30 app = flask.Flask('adt_cloud_worker')
3131
32 app.add_url_rule(32 app.add_url_rule(
@@ -42,6 +42,8 @@
42 response.status_code = error.status42 response.status_code = error.status
43 return response43 return response
4444
45 app.extensions['rabbit'] = RabbitQueuer(config)
46 app.config['SWIFT_URL'] = config['swift']['public_url']
45 return app47 return app
4648
4749
@@ -53,15 +55,14 @@
53 parser.add_argument('-c', '--conf', default='.adt-service.conf',55 parser.add_argument('-c', '--conf', default='.adt-service.conf',
54 help='Configuration file path')56 help='Configuration file path')
55 args = parser.parse_args()57 args = parser.parse_args()
56
57
58 app = create_flask_app()
59
60 # Load configuration options & create queuer58 # Load configuration options & create queuer
61 config = configparser.ConfigParser()59 config = configparser.ConfigParser()
62 config.read(args.conf)60 config.read(args.conf)
63 app.extensions['rabbit'] = RabbitQueuer(config)61
64 app.config['SWIFT_URL'] = config['swift']['public_url']62
63 app = create_flask_app(config)
64
65
6566
66 app.run()67 app.run()
6768
6869
=== modified file 'adt_request_proxy/queue.py'
--- adt_request_proxy/queue.py 2015-03-09 03:29:34 +0000
+++ adt_request_proxy/queue.py 2015-03-11 01:37:05 +0000
@@ -26,14 +26,12 @@
26 """A class that can send things to the rabbit queue. An instance of this26 """A class that can send things to the rabbit queue. An instance of this
27 is stored in 'flask.g'.27 is stored in 'flask.g'.
2828
29 TODO: This code can almost certainly be improved - for example, maybe
30 don't create a new connection every time we queue something?
31
32 """29 """
3330
34 def __init__(self, config):31 def __init__(self, config):
35 self.amqp_uris = config.get('amqp', 'uris').split()32 self.amqp_uris = config['amqp']['uris'].split()
36 self.adt_exchange = kombu.Exchange("adt.exchange", type="topic")33 self.adt_exchange = kombu.Exchange("adt.exchange", type="topic")
34 self.connection = kombu.Connection(self.amqp_uris)
3735
38 def queue_test(self, payload):36 def queue_test(self, payload):
39 """Take 'payload' and enqueue it on the rabbit queue.37 """Take 'payload' and enqueue it on the rabbit queue.
@@ -47,7 +45,8 @@
47 On error, an exception will be raised.45 On error, an exception will be raised.
4846
49 """47 """
50 routing_key = payload['arch'] + '.' + payload['platform']48 payload_copy = payload.copy()
49 routing_key = payload_copy['arch'] + '.' + payload_copy['platform']
51 queue = kombu.Queue(50 queue = kombu.Queue(
52 'adt.requests.{}'.format(routing_key),51 'adt.requests.{}'.format(routing_key),
53 self.adt_exchange, routing_key=routing_key)52 self.adt_exchange, routing_key=routing_key)
@@ -56,10 +55,9 @@
56 # available works the request is preserved.55 # available works the request is preserved.
57 declare = [self.adt_exchange, queue]56 declare = [self.adt_exchange, queue]
5857
59 connection = kombu.Connection(self.amqp_uris)58 payload_copy['request_id'] = uuid()
60 payload['request_id'] = uuid()
6159
62 with producers[connection].acquire(block=True) as producer:60 with producers[self.connection].acquire(block=True) as producer:
6361
64 def errback(exc, interval):62 def errback(exc, interval):
65 # TODO: This should raise an exception if something goes wrong.63 # TODO: This should raise an exception if something goes wrong.
@@ -69,12 +67,12 @@
69 logger.error('Error: %r', exc, exc_info=1)67 logger.error('Error: %r', exc, exc_info=1)
70 logger.info('Retry in %s seconds.', interval)68 logger.info('Retry in %s seconds.', interval)
7169
72 publish = connection.ensure(70 publish = self.connection.ensure(
73 producer, producer.publish,71 producer, producer.publish,
74 errback=errback, max_retries=3)72 errback=errback, max_retries=3)
75 publish(73 publish(
76 payload,74 payload_copy,
77 exchange=self.adt_exchange,75 exchange=self.adt_exchange,
78 routing_key=routing_key,76 routing_key=routing_key,
79 declare=declare)77 declare=declare)
80 return payload['request_id']78 return payload_copy['request_id']
8179
=== modified file 'adt_request_proxy/tests/test_functional.py'
--- adt_request_proxy/tests/test_functional.py 2015-03-10 20:41:29 +0000
+++ adt_request_proxy/tests/test_functional.py 2015-03-11 01:37:05 +0000
@@ -17,13 +17,22 @@
1717
18"""Full-stack functional tests for the web application."""18"""Full-stack functional tests for the web application."""
1919
20import json
21import uuid
22
23import kombu
20import testtools24import testtools
21from testtools.matchers import Equals25from testtools.matchers import (
26 Contains,
27 Equals,
28 HasLength,
29)
2230
23import adt_request_proxy31import adt_request_proxy
2432from adt_request_proxy.queue import RabbitQueuer
2533
26class FunctionalWebAppTests(testtools.TestCase):34
35class RequestProxyTestBase(testtools.TestCase):
2736
28 """A suite of functional tests for the web app.37 """A suite of functional tests for the web app.
2938
@@ -40,10 +49,26 @@
4049
41 def setUp(self):50 def setUp(self):
42 super().setUp()51 super().setUp()
43 self.server_app = adt_request_proxy.create_flask_app()52 test_config = {
53 'swift': {
54 'public_url': 'http://swift.test.com',
55 },
56 'amqp': {
57 'uris': 'memory://'
58 },
59 }
60 self.server_app = adt_request_proxy.create_flask_app(test_config)
44 self.server_app.config['TESTING'] = True61 self.server_app.config['TESTING'] = True
45 self.client = self.server_app.test_client()62 self.client = self.server_app.test_client()
4663
64
65class WebEndpointTests(RequestProxyTestBase):
66
67 def set_queuer_double(self):
68 """Create and install the QueuerDouble, returning an instance of it."""
69 instance = self.server_app.extensions['rabbit'] = QueuerDouble()
70 return instance
71
47 def assertEndpointDisallowsMethods(self, endpoint, methods):72 def assertEndpointDisallowsMethods(self, endpoint, methods):
48 for method in methods:73 for method in methods:
49 fn = getattr(self.client, method.lower(), None)74 fn = getattr(self.client, method.lower(), None)
@@ -59,4 +84,105 @@
59 ['GET', 'PATCH', 'DELETE']84 ['GET', 'PATCH', 'DELETE']
60 )85 )
6186
62 # TODO: add more tests here ;)87 def test_missing_args_causes_400_error(self):
88 q = self.set_queuer_double()
89 resp = self.client.post('/v1/test')
90
91 self.assertThat(resp.status_code, Equals(400))
92 self.assertThat(q.queued_payloads, HasLength(0))
93
94 def test_can_queue_test_with_all_parameters_present(self):
95 """Test that we can enqueue test payloads, and that the response we
96 get is sensible.
97
98 This should perhaps be broken down into smaller tests if it grows
99 much more.
100
101 """
102 q = self.set_queuer_double()
103 resp = self.client.post(
104 '/v1/test',
105 data = {
106 'package-name': 'librepng',
107 'architecture': 'i386',
108 'platform': 'nova',
109 }
110 )
111 self.assertThat(resp.status_code, Equals(200))
112 self.assertThat(q.queued_payloads, HasLength(1))
113 self.assertThat(resp.mimetype, Equals("application/json"))
114 data = json.loads(resp.data.decode())
115 self.assertThat(
116 data['result_url'],
117 Equals('http://swift.test.com/{}/results.tgz'.format(
118 q.generated_uuids[0]
119 ))
120 )
121
122
123class QueuerDouble(object):
124
125 """A test double for adt_request_proxy.queue.RabbitQueuer.
126
127 Simply adds queued payloads to a list, and implements methods to make
128 assertions on the queued payloads.
129
130 """
131
132 def __init__(self):
133 self.queued_payloads = []
134 self.generated_uuids = []
135
136 def queue_test(self, payload):
137 request_id = str(uuid.uuid1())
138 payload['request_id'] = request_id
139 self.queued_payloads.append(payload)
140 self.generated_uuids.append(request_id)
141 return payload['request_id']
142
143
144class QueuerTests(RequestProxyTestBase):
145
146 """Tests for the rabbit queue class.
147
148 The RabbitQueuer is highly stateful, so it's almost impossible to test with
149 unit tests.
150
151 What we can do, which is a reasonable half-measure is to configure the
152 queuer class with an in-memory transport, and attempt to rigerously test the
153 interface between the queuer and it's caller.
154
155 """
156 def test_queueing_does_not_alter_payload(self):
157 q = RabbitQueuer(dict(amqp=dict(uris='memory://')))
158
159 test_payload = {
160 'package_name': 'librepng',
161 'arch': 'amd64',
162 'platform': 'nova',
163 'nova_image': 'some-nova-image',
164 }
165 copy_payload = test_payload.copy()
166 request_id = q.queue_test(copy_payload)
167 self.assertEqual(test_payload, copy_payload)
168
169 def test_attempt_queue_test(self):
170 q = RabbitQueuer(dict(amqp=dict(uris='memory://')))
171
172 test_payload = {
173 'package_name': 'librepng',
174 'arch': 'amd64',
175 'platform': 'nova',
176 'nova_image': 'some-nova-image',
177 }
178 request_id = q.queue_test(test_payload)
179
180 # make sure the message got on the channel:
181 conn = kombu.Connection("memory://")
182 consumer_queue = conn.SimpleQueue("adt.requests.amd64.nova")
183 message = consumer_queue.get(timeout=10.0)
184 message.ack()
185
186 # message must have request_id set:
187 test_payload['request_id'] = request_id
188 self.assertEqual(test_payload, message.payload)

Subscribers

People subscribed via source and target branches

to all changes: