blob: ed1a773485bcfc93a6880cdc766a2cb2c497e826 [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
Lukáš Piwowarskid858e4e2022-10-07 15:06:35 +020034From the tempest directory, setup the tempest virtual environment for the designate tempest plugin::
Graham Hayesc392cf92017-02-23 16:06:04 -050035
Lukáš Piwowarskid858e4e2022-10-07 15:06:35 +020036 $ tox -e venv-tempest -- pip3 install -e <path to designate-tempest-plugin>
Graham Hayesc392cf92017-02-23 16:06:04 -050037
Lukáš Piwowarskid858e4e2022-10-07 15:06:35 +020038To run all tests from this plugin, install designate into your environment and from the tempest repo, run::
Graham Hayesc392cf92017-02-23 16:06:04 -050039
Lukáš Piwowarskid858e4e2022-10-07 15:06:35 +020040 $ tox -e all -- designate
Graham Hayesc392cf92017-02-23 16:06:04 -050041
42To run a single test case, run with the test case name, for example::
43
Lukáš Piwowarskid858e4e2022-10-07 15:06:35 +020044 $ tox -e all -- designate_tempest_plugin.tests.api.v2.test_zones.ZonesAdminTest.test_get_other_tenant_zone
Graham Hayesc392cf92017-02-23 16:06:04 -050045
46To run all tempest tests including this plugin, run::
47
Lukáš Piwowarskid858e4e2022-10-07 15:06:35 +020048 $ tox -e all
Graham Hayesc392cf92017-02-23 16:06:04 -050049
50Writing new tests
51=================
52
53Writing new tests is easy, and we encourage contributors to write tests for
54any new or changed functionality. Most of the patterns you will find in the
55Designate tests will look familiar if you have contributed to tempest, so rather
56than re-type all their docs here, please have a read of the `Tempest Docs`_.
57
58Test Clients
59------------
60
61In Tempest tests, it is forbidden to use a services python bindings or client,
62as doing so would allow API changes to go unnoticed when the server and client
63are updated. As such, each service is expected to have a minimal in-tree
64client. Designate's client can be found in:
65
66.. code-block:: bash
67
68 $ tree -P "*_client.py" designate_tempest_plugin/services/dns/
69
70 designate_tempest_plugin/services/dns/
71 ├── json
72 │   └── versions_client.py
73 └── v2
74 └── json
75 ├── recordsets_client.py
76 └── zones_client.py
77
78An example client, in this case for a subset of /v2/zones is included below:
79
80.. code-block:: python
81
82 class ZonesClient(base.DnsClientV2Base):
83 """API V2 Tempest REST client for Designate API"""
84
85 @base.handle_errors
86 def create_zone(self, name=None, email=None, ttl=None, description=None,
87 wait_until=False, params=None):
88 """Create a zone with the specified parameters.
89
90 :param name: The name of the zone.
91 Default: Random Value
92 :param email: The email for the zone.
93 Default: Random Value
94 :param ttl: The ttl for the zone.
95 Default: Random Value
96 :param description: A description of the zone.
97 Default: Random Value
98 :param wait_until: Block until the zone reaches the desired status
99 :param params: A Python dict that represents the query parameters to
100 include in the request URI.
101 :return: A tuple with the server response and the created zone.
102 """
103 zone = {
104 'name': name or dns_data_utils.rand_zone_name(),
105 'email': email or dns_data_utils.rand_email(),
106 'ttl': ttl or dns_data_utils.rand_ttl(),
107 'description': description or data_utils.rand_name('test-zone'),
108 }
109
110 resp, body = self._create_request('zones', zone, params=params)
111
112 if wait_until:
113 waiters.wait_for_zone_status(self, body['id'], wait_until)
114
115 return resp, body
116
117Some items to note, client methods should be wrapped in the
118`base.handle_errors` decorator, which is used to allow for ignoring certain
119types of errors, in certain cases. Most commonly, this will be ignoring 404's
120when cleaning up resources.
121
122Test Cases
123----------
124
125Designate's tests can be found in:
126
127.. code-block:: bash
128
129 $ tree -P "test_*.py" designate_tempest_plugin/tests/
130
131 designate_tempest_plugin/tests/
132 ├── api
133 │   ├── test_versions.py
134 │   └── v2
135 │   ├── test_recordsets.py
136 │   └── test_zones.py
137 └── scenario
138    └── v2
139    ├── test_recordsets.py
140    └── test_zones.py
141
142There are two groupings of tests here "api" and "scenario". **API tests**
143should be quick, and simple. Testing as small a surface area of the API as is
144possible while still getting the job done. Additionally, API tests should avoid
145waiting for resources to become ACTIVE etc, as this typically pushes test time
146out significantly, and would only duplicate scenario tests. **Scenario tests**
147should cover common real world uses cases. For example, creating a zone,
148waiting for it to become ACTIVE, adding some records, waiting for ACTIVE,
149querying the DNS servers themselves, and finally deleting the zone and waiting
150for it to 404.
151
152An example test, in this case for a subset of /v2/zones functionality is
153included below:
154
155.. code-block:: python
156
157 class ZonesTest(BaseZonesTest):
158 @classmethod
159 def setup_clients(cls):
160 super(ZonesTest, cls).setup_clients()
161
Graham Hayesbbc01e32018-02-15 14:40:54 +0000162 cls.client = cls.os_primary.zones_client
Graham Hayesc392cf92017-02-23 16:06:04 -0500163
ghanshyamf4b07242017-07-25 05:07:05 +0300164 @decorators.attr(type='smoke')
Graham Hayesc392cf92017-02-23 16:06:04 -0500165 @decorators.idempotent_id('fbabd6af-238a-462e-b923-de4d736b90a7')
166 def test_create_zone(self):
167 LOG.info('Create a zone')
168 _, zone = self.client.create_zone()
169 self.addCleanup(self.client.delete_zone, zone['id'])
170
171 LOG.info('Ensure we respond with CREATE+PENDING')
172 self.assertEqual('CREATE', zone['action'])
173 self.assertEqual('PENDING', zone['status'])
174
175 LOG.info('Ensure the fetched response matches the created zone')
176 self._assertExpected(zone, body)
177
178
179Test Cases - Alternative Credentials
180------------------------------------
181
182Some tests require more than just a "standard" cloud user, e.g. those tests
183checking admin only functionality. We can ensure both user and admin
184credentials are available using the class level "credentials" property like so:
185
186
187.. code-block:: python
188
189 class ZonesAdminTest(BaseZonesTest):
190 credentials = ['primary', 'admin']
191
192 @classmethod
193 def setup_clients(cls):
194 super(ZonesAdminTest, cls).setup_clients()
195
Graham Hayesbbc01e32018-02-15 14:40:54 +0000196 cls.client = cls.os_primary.zones_client
197 cls.adm_client = cls.os_admin.zones_client
Graham Hayesc392cf92017-02-23 16:06:04 -0500198
199 @decorators.idempotent_id('6477f92d-70ba-46eb-bd6c-fc50c405e222')
200 def test_get_other_tenant_zone(self):
201 LOG.info('Create a zone as a user')
202 _, zone = self.client.create_zone()
203 self.addCleanup(self.client.delete_zone, zone['id'])
204
205 LOG.info('Fetch the zone as an admin')
206 _, body = self.adm_client.show_zone(
207 zone['id'], params={'all_tenants': True})
208
209 LOG.info('Ensure the fetched response matches the created zone')
210 self._assertExpected(zone, body)
211
212
213Test Decorators
214---------------
215
216Several different test decorators are used within the test cases, this attempts
217to explain their purpose and correct usage.
218
219
220@decorators.idempotent_id
221~~~~~~~~~~~~~~~~~~~~~~~~~
222
223The `idempotent_id` decorator allows for tracking of tests even after they have
224been renamed. The UUID should be randomly generated as the test is first
225written, e.g. with `uuidgen` on most linux hosts, and should not be changed
226when the test is renamed.
227
228Every test should have a unique idempotent_id assigned.
229
230Example:
231
232.. code-block:: python
233
234 class ZonesTest(BaseZonesTest):
235 @decorators.idempotent_id('fbabd6af-238a-462e-b923-de4d736b90a7')
236 def test_create_zone(self):
237 pass
238
239
ghanshyamf4b07242017-07-25 05:07:05 +0300240@decorators.attr
Nguyen Hai7462c372018-08-17 15:03:18 +0900241~~~~~~~~~~~~~~~~
Graham Hayesc392cf92017-02-23 16:06:04 -0500242
243The `attr` decorator is used to set test attributes, this is most commonly used
244to set the test type. Currently, we use one test type "smoke", which should be
245applied to any tests which test the most basic functionality Designate
246provides, allowing for the core functionality to be tested quickly, without
247having to run the entire suite. Another type we use is "slow", which should be
248applied to tests which take on average 5 seconds or more.
249
250Example:
251
252.. code-block:: python
253
254 class ZonesTest(BaseZonesTest):
ghanshyamf4b07242017-07-25 05:07:05 +0300255 @decorators.attr(type='smoke')
Graham Hayesc392cf92017-02-23 16:06:04 -0500256 def test_create_zone(self):
257 pass
258
ghanshyamf4b07242017-07-25 05:07:05 +0300259 @decorators.attr(type='slow')
Graham Hayesc392cf92017-02-23 16:06:04 -0500260 def test_something_else(self):
261 pass
262
263@test.services
264~~~~~~~~~~~~~~
265
266The `services` decorator is used to indicate which services are exercised by
267a given test. The `services` decorator may only be used on scenario tests, and
268(for now) should not include "dns" itself. For example, given a scenario test
269that interactions with Designate's Reverse DNS APIs, which in turn talk to
270Neutron, we would use something like the below:
271
272Example:
273
274.. code-block:: python
275
276 class ReverseTest(BaseDnsTest):
277 @test.services('network')
278 def test_reverse_dns_for_fips(self):
279 pass
280
281
ShangXiao7a68f732018-03-14 00:52:53 -0700282.. _Tempest Docs: https://docs.openstack.org/tempest/latest/
283.. _Tempest HACKING: https://docs.openstack.org/tempest/latest/HACKING.html
284.. _Tempest Plugins: https://docs.openstack.org/tempest/latest/plugin.html