blob: 82eda143212f8699f6ce6a0ba26b635dfcbf04ed [file] [log] [blame]
Graham Hayesc392cf92017-02-23 16:06:04 -05001..
2 Copyright 2016 Hewlett Packard Enterprise Development Company, L.P.
3
4 Licensed under the Apache License, Version 2.0 (the "License"); you may
5 not use this file except in compliance with the License. You may obtain
6 a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 License for the specific language governing permissions and limitations
14 under the License.
15
16.. _tempest:
17
Tim Simmonsb990d6f2017-03-07 20:16:20 +000018========================
19Designate Tempest Plugin
20========================
Graham Hayesc392cf92017-02-23 16:06:04 -050021
Tim Simmonsb990d6f2017-03-07 20:16:20 +000022The Designate team maintains a set of Tempest tests to exercise the Designate
Graham Hayesc392cf92017-02-23 16:06:04 -050023service and APIs.
24
25Intro and References
26====================
27* `Tempest Docs`_ - Tempest docs
28* `Tempest HACKING`_ - General tempest style and coding guidelines
29* `Tempest Plugins`_ - Tempest Test Plugin Interface guide
30
31Quick Start
32===========
33
34To run all tests from this plugin, install the plugin into your environment
35and from the tempest repo, run::
36
37 $ tox -e all-plugin -- designate
38
39If that doesn't run any tests, ensure that the designate-tempest-plugin is installed
40in the tox venv. Replace ``../designate-tempest-plugin/`` with the path to the plugin
41on your system. Then execute the above tox command again::
42
43 $ .tox/all-plugin/bin/pip install ../designate-tempest-plugin/
44 $ tox -e all-plugin -- designate
45
46.. note:: This is not necessary if ``designate-tempest-plugin`` is installed to
47 site-packages before the ``all-plugin`` tox venv is created.
48
49To run a single test case, run with the test case name, for example::
50
51 $ tox -e all-plugin -- designate_tempest_plugin.tests.api.v2.test_zones.ZonesAdminTest.test_get_other_tenant_zone
52
53To run all tempest tests including this plugin, run::
54
55 $ tox -e all-plugin
56
57Writing new tests
58=================
59
60Writing new tests is easy, and we encourage contributors to write tests for
61any new or changed functionality. Most of the patterns you will find in the
62Designate tests will look familiar if you have contributed to tempest, so rather
63than re-type all their docs here, please have a read of the `Tempest Docs`_.
64
65Test Clients
66------------
67
68In Tempest tests, it is forbidden to use a services python bindings or client,
69as doing so would allow API changes to go unnoticed when the server and client
70are updated. As such, each service is expected to have a minimal in-tree
71client. Designate's client can be found in:
72
73.. code-block:: bash
74
75 $ tree -P "*_client.py" designate_tempest_plugin/services/dns/
76
77 designate_tempest_plugin/services/dns/
78 ├── json
79 │   └── versions_client.py
80 └── v2
81 └── json
82 ├── recordsets_client.py
83 └── zones_client.py
84
85An example client, in this case for a subset of /v2/zones is included below:
86
87.. code-block:: python
88
89 class ZonesClient(base.DnsClientV2Base):
90 """API V2 Tempest REST client for Designate API"""
91
92 @base.handle_errors
93 def create_zone(self, name=None, email=None, ttl=None, description=None,
94 wait_until=False, params=None):
95 """Create a zone with the specified parameters.
96
97 :param name: The name of the zone.
98 Default: Random Value
99 :param email: The email for the zone.
100 Default: Random Value
101 :param ttl: The ttl for the zone.
102 Default: Random Value
103 :param description: A description of the zone.
104 Default: Random Value
105 :param wait_until: Block until the zone reaches the desired status
106 :param params: A Python dict that represents the query parameters to
107 include in the request URI.
108 :return: A tuple with the server response and the created zone.
109 """
110 zone = {
111 'name': name or dns_data_utils.rand_zone_name(),
112 'email': email or dns_data_utils.rand_email(),
113 'ttl': ttl or dns_data_utils.rand_ttl(),
114 'description': description or data_utils.rand_name('test-zone'),
115 }
116
117 resp, body = self._create_request('zones', zone, params=params)
118
119 if wait_until:
120 waiters.wait_for_zone_status(self, body['id'], wait_until)
121
122 return resp, body
123
124Some items to note, client methods should be wrapped in the
125`base.handle_errors` decorator, which is used to allow for ignoring certain
126types of errors, in certain cases. Most commonly, this will be ignoring 404's
127when cleaning up resources.
128
129Test Cases
130----------
131
132Designate's tests can be found in:
133
134.. code-block:: bash
135
136 $ tree -P "test_*.py" designate_tempest_plugin/tests/
137
138 designate_tempest_plugin/tests/
139 ├── api
140 │   ├── test_versions.py
141 │   └── v2
142 │   ├── test_recordsets.py
143 │   └── test_zones.py
144 └── scenario
145    └── v2
146    ├── test_recordsets.py
147    └── test_zones.py
148
149There are two groupings of tests here "api" and "scenario". **API tests**
150should be quick, and simple. Testing as small a surface area of the API as is
151possible while still getting the job done. Additionally, API tests should avoid
152waiting for resources to become ACTIVE etc, as this typically pushes test time
153out significantly, and would only duplicate scenario tests. **Scenario tests**
154should cover common real world uses cases. For example, creating a zone,
155waiting for it to become ACTIVE, adding some records, waiting for ACTIVE,
156querying the DNS servers themselves, and finally deleting the zone and waiting
157for it to 404.
158
159An example test, in this case for a subset of /v2/zones functionality is
160included below:
161
162.. code-block:: python
163
164 class ZonesTest(BaseZonesTest):
165 @classmethod
166 def setup_clients(cls):
167 super(ZonesTest, cls).setup_clients()
168
Graham Hayesbbc01e32018-02-15 14:40:54 +0000169 cls.client = cls.os_primary.zones_client
Graham Hayesc392cf92017-02-23 16:06:04 -0500170
ghanshyamf4b07242017-07-25 05:07:05 +0300171 @decorators.attr(type='smoke')
Graham Hayesc392cf92017-02-23 16:06:04 -0500172 @decorators.idempotent_id('fbabd6af-238a-462e-b923-de4d736b90a7')
173 def test_create_zone(self):
174 LOG.info('Create a zone')
175 _, zone = self.client.create_zone()
176 self.addCleanup(self.client.delete_zone, zone['id'])
177
178 LOG.info('Ensure we respond with CREATE+PENDING')
179 self.assertEqual('CREATE', zone['action'])
180 self.assertEqual('PENDING', zone['status'])
181
182 LOG.info('Ensure the fetched response matches the created zone')
183 self._assertExpected(zone, body)
184
185
186Test Cases - Alternative Credentials
187------------------------------------
188
189Some tests require more than just a "standard" cloud user, e.g. those tests
190checking admin only functionality. We can ensure both user and admin
191credentials are available using the class level "credentials" property like so:
192
193
194.. code-block:: python
195
196 class ZonesAdminTest(BaseZonesTest):
197 credentials = ['primary', 'admin']
198
199 @classmethod
200 def setup_clients(cls):
201 super(ZonesAdminTest, cls).setup_clients()
202
Graham Hayesbbc01e32018-02-15 14:40:54 +0000203 cls.client = cls.os_primary.zones_client
204 cls.adm_client = cls.os_admin.zones_client
Graham Hayesc392cf92017-02-23 16:06:04 -0500205
206 @decorators.idempotent_id('6477f92d-70ba-46eb-bd6c-fc50c405e222')
207 def test_get_other_tenant_zone(self):
208 LOG.info('Create a zone as a user')
209 _, zone = self.client.create_zone()
210 self.addCleanup(self.client.delete_zone, zone['id'])
211
212 LOG.info('Fetch the zone as an admin')
213 _, body = self.adm_client.show_zone(
214 zone['id'], params={'all_tenants': True})
215
216 LOG.info('Ensure the fetched response matches the created zone')
217 self._assertExpected(zone, body)
218
219
220Test Decorators
221---------------
222
223Several different test decorators are used within the test cases, this attempts
224to explain their purpose and correct usage.
225
226
227@decorators.idempotent_id
228~~~~~~~~~~~~~~~~~~~~~~~~~
229
230The `idempotent_id` decorator allows for tracking of tests even after they have
231been renamed. The UUID should be randomly generated as the test is first
232written, e.g. with `uuidgen` on most linux hosts, and should not be changed
233when the test is renamed.
234
235Every test should have a unique idempotent_id assigned.
236
237Example:
238
239.. code-block:: python
240
241 class ZonesTest(BaseZonesTest):
242 @decorators.idempotent_id('fbabd6af-238a-462e-b923-de4d736b90a7')
243 def test_create_zone(self):
244 pass
245
246
ghanshyamf4b07242017-07-25 05:07:05 +0300247@decorators.attr
Nguyen Hai7462c372018-08-17 15:03:18 +0900248~~~~~~~~~~~~~~~~
Graham Hayesc392cf92017-02-23 16:06:04 -0500249
250The `attr` decorator is used to set test attributes, this is most commonly used
251to set the test type. Currently, we use one test type "smoke", which should be
252applied to any tests which test the most basic functionality Designate
253provides, allowing for the core functionality to be tested quickly, without
254having to run the entire suite. Another type we use is "slow", which should be
255applied to tests which take on average 5 seconds or more.
256
257Example:
258
259.. code-block:: python
260
261 class ZonesTest(BaseZonesTest):
ghanshyamf4b07242017-07-25 05:07:05 +0300262 @decorators.attr(type='smoke')
Graham Hayesc392cf92017-02-23 16:06:04 -0500263 def test_create_zone(self):
264 pass
265
ghanshyamf4b07242017-07-25 05:07:05 +0300266 @decorators.attr(type='slow')
Graham Hayesc392cf92017-02-23 16:06:04 -0500267 def test_something_else(self):
268 pass
269
270@test.services
271~~~~~~~~~~~~~~
272
273The `services` decorator is used to indicate which services are exercised by
274a given test. The `services` decorator may only be used on scenario tests, and
275(for now) should not include "dns" itself. For example, given a scenario test
276that interactions with Designate's Reverse DNS APIs, which in turn talk to
277Neutron, we would use something like the below:
278
279Example:
280
281.. code-block:: python
282
283 class ReverseTest(BaseDnsTest):
284 @test.services('network')
285 def test_reverse_dns_for_fips(self):
286 pass
287
288
ShangXiao7a68f732018-03-14 00:52:53 -0700289.. _Tempest Docs: https://docs.openstack.org/tempest/latest/
290.. _Tempest HACKING: https://docs.openstack.org/tempest/latest/HACKING.html
291.. _Tempest Plugins: https://docs.openstack.org/tempest/latest/plugin.html