Merge "Test cases for: "FloatingIPs PTR" resords."
diff --git a/designate_tempest_plugin/services/dns/v2/json/transfer_accepts_client.py b/designate_tempest_plugin/services/dns/v2/json/transfer_accepts_client.py
index a0612e9..e09f775 100644
--- a/designate_tempest_plugin/services/dns/v2/json/transfer_accepts_client.py
+++ b/designate_tempest_plugin/services/dns/v2/json/transfer_accepts_client.py
@@ -19,18 +19,20 @@
 
     @base.handle_errors
     def create_transfer_accept(self, transfer_accept_data,
-                               params=None):
+                               params=None, headers=None):
         """Create a zone transfer_accept.
         :param transfer_accept_data: A python dictionary representing
                                 data for the zone transfer accept.
         :param params: A Python dict that represents the query paramaters to
                        include in the accept URI.
+        :param headers (dict): The headers to use for the request.
         :return: Serialized accepted zone transfer as a dictionary.
         """
 
         transfer_accept_uri = 'zones/tasks/transfer_accepts'
         resp, body = self._create_request(
-            transfer_accept_uri, transfer_accept_data, params=params)
+            transfer_accept_uri, transfer_accept_data,
+            params=params, headers=headers)
 
         # Create Transfer accept should Return a HTTP 201
         self.expected_success(201, resp.status)
@@ -38,15 +40,17 @@
         return resp, body
 
     @base.handle_errors
-    def show_transfer_accept(self, uuid, params=None):
+    def show_transfer_accept(self, uuid, params=None, headers=None):
         """Gets a specific accepted zone transfer..
         :param uuid: Unique identifier of the transfer_accept.
         :param params: A Python dict that represents the query paramaters to
                        include in the accept URI.
+        :param headers (dict): The headers to use for the request.
         :return: Serialized accepted zone transfer as a dictionary.
         """
         return self._show_request(
-            'zones/tasks/transfer_accepts', uuid, params=params)
+            'zones/tasks/transfer_accepts', uuid,
+            params=params, headers=headers)
 
     @base.handle_errors
     def list_transfer_accept(self, params=None, headers=None):
diff --git a/designate_tempest_plugin/services/dns/v2/json/zones_client.py b/designate_tempest_plugin/services/dns/v2/json/zones_client.py
index ec84aa3..4becb58 100644
--- a/designate_tempest_plugin/services/dns/v2/json/zones_client.py
+++ b/designate_tempest_plugin/services/dns/v2/json/zones_client.py
@@ -54,10 +54,14 @@
         """
 
         zone = {
-            'name': name or dns_data_utils.rand_zone_name(),
-            'email': email or dns_data_utils.rand_email(),
-            'ttl': ttl or dns_data_utils.rand_ttl(),
-            'description': description or data_utils.rand_name('test-zone'),
+            'name': name or dns_data_utils.rand_zone_name()
+            if name != '' else '',
+            'email': email or dns_data_utils.rand_email()
+            if email != '' else '',
+            'ttl': ttl or dns_data_utils.rand_ttl()
+            if ttl != 0 else 0,
+            'description': description or data_utils.rand_name('test-zone')
+            if description != '' else '',
             'attributes': attributes or {
                 'attribute_key': data_utils.rand_name('attribute_value')}
         }
@@ -117,14 +121,16 @@
         return self._list_request('zones', params=params, headers=headers)
 
     @base.handle_errors
-    def delete_zone(self, uuid, params=None):
+    def delete_zone(self, uuid, params=None, headers=None):
         """Deletes a zone having the specified UUID.
         :param uuid: The unique identifier of the zone.
         :param params: A Python dict that represents the query paramaters to
                        include in the request URI.
+        :param headers (dict): The headers to use for the request.
         :return: A tuple with the server response and the response body.
         """
-        resp, body = self._delete_request('zones', uuid, params=params)
+        resp, body = self._delete_request(
+            'zones', uuid, params=params, headers=headers)
 
         # Delete Zone should Return a HTTP 202
         self.expected_success(202, resp.status)
diff --git a/designate_tempest_plugin/tests/api/v2/test_pool.py b/designate_tempest_plugin/tests/api/v2/test_pool.py
index d127006..e2516af 100644
--- a/designate_tempest_plugin/tests/api/v2/test_pool.py
+++ b/designate_tempest_plugin/tests/api/v2/test_pool.py
@@ -208,3 +208,79 @@
         self.assertEqual("invalid_uuid", resp_body['type'])
         self.assertEqual("Invalid UUID pool_id: foo",
                          resp_body['message'])
+
+
+class TestPoolAdminNegative(BasePoolTest):
+
+    credentials = ["admin"]
+
+    @classmethod
+    def setup_credentials(cls):
+        # Do not create network resources for these test.
+        cls.set_network_resources()
+        super(TestPoolAdminNegative, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
+        super(TestPoolAdminNegative, cls).setup_clients()
+        cls.admin_client = cls.os_admin.pool_client
+
+    @decorators.idempotent_id('0a8cdc1e-ac02-11eb-ae06-74e5f9e2a801')
+    def test_create_pool_invalid_name(self):
+        LOG.info('Create a pool using a huge size string for name)')
+        with self.assertRaisesDns(lib_exc.BadRequest, 'invalid_object', 400):
+            self.admin_client.create_pool(
+                pool_name=data_utils.rand_name(name="Huge_size_name") * 10000)
+
+    @decorators.idempotent_id('9a787d0e-ac04-11eb-ae06-74e5f9e2a801')
+    def test_create_pool_invalid_hostname_in_ns_records(self):
+        LOG.info('Create a pool using invalid hostname in ns_records')
+        with self.assertRaisesDns(lib_exc.BadRequest, 'invalid_object', 400):
+            self.admin_client.create_pool(
+                ns_records=[{"hostname": "ns1_example_org_", "priority": 1}])
+
+    @decorators.idempotent_id('9a787d0e-ac04-11eb-ae06-74e5f9e2a801')
+    def test_create_pool_invalid_priority_in_ns_records(self):
+        LOG.info('Create a pool using invalid priority in ns_records')
+        with self.assertRaisesDns(lib_exc.BadRequest, 'invalid_object', 400):
+            self.admin_client.create_pool(
+                ns_records=[{"hostname": "ns1.example.org.", "priority": -1}])
+
+    @decorators.idempotent_id('cc378e4c-ac05-11eb-ae06-74e5f9e2a801')
+    # Note: Update pool API is deprecated for removal.
+    def test_update_pool_with_invalid_name(self):
+        LOG.info('Create a pool')
+        pool = self.admin_client.create_pool()[1]
+        self.addCleanup(self.admin_client.delete_pool, pool['id'])
+
+        LOG.info('Update the pool using a name that is too long')
+        with self.assertRaisesDns(lib_exc.BadRequest, 'invalid_object', 400):
+            self.admin_client.update_pool(
+                pool['id'],
+                pool_name=data_utils.rand_name(name="Huge_size_name") * 10000)
+
+    @decorators.idempotent_id('2e496596-ac07-11eb-ae06-74e5f9e2a801')
+    def test_update_pool_with_invalid_hostname_in_ns_records(self):
+        # Note: Update pool API is deprecated for removal.
+        LOG.info('Create a pool')
+        pool = self.admin_client.create_pool()[1]
+        self.addCleanup(self.admin_client.delete_pool, pool['id'])
+
+        LOG.info('Update the pool using invalid hostname in ns_records')
+        with self.assertRaisesDns(lib_exc.BadRequest, 'invalid_object', 400):
+            self.admin_client.update_pool(
+                pool['id'],
+                ns_records=[{"hostname": "ns1_example_org_", "priority": 1}])
+
+    @decorators.idempotent_id('3e934624-ac07-11eb-ae06-74e5f9e2a801')
+    def test_update_pool_with_invalid_priority_in_ns_records(self):
+        # Note: Update pool API is deprecated for removal.
+        LOG.info('Create a pool')
+        pool = self.admin_client.create_pool()[1]
+        self.addCleanup(self.admin_client.delete_pool, pool['id'])
+
+        LOG.info('Update the pool using invalid priority in ns_records')
+        with self.assertRaisesDns(lib_exc.BadRequest, 'invalid_object', 400):
+            self.admin_client.update_pool(
+                pool['id'],
+                ns_records=[{"hostname": "ns1.example.org.", "priority": -1}])
diff --git a/designate_tempest_plugin/tests/api/v2/test_transfer_accepts.py b/designate_tempest_plugin/tests/api/v2/test_transfer_accepts.py
index 1f7c1e1..5f43ed6 100644
--- a/designate_tempest_plugin/tests/api/v2/test_transfer_accepts.py
+++ b/designate_tempest_plugin/tests/api/v2/test_transfer_accepts.py
@@ -12,6 +12,7 @@
 #    License for the specific language governing permissions and limitations
 #    under the License.
 from oslo_log import log as logging
+from tempest.lib.common.utils import data_utils
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
 
@@ -169,7 +170,7 @@
             transfer_accept = self.alt_accept_client.create_transfer_accept(
                 data)[1]
 
-            LOG.info('Ensure we respond with ACTIVE status')
+            LOG.info('Ensure we respond with COMPLETE status')
             self.assertEqual('COMPLETE', transfer_accept['status'])
             transfer_request_ids.append(transfer_accept['id'])
 
@@ -216,3 +217,108 @@
             admin_client_accept_ids,
             "Failed, filtered list should be empty, but actually it's not, "
             "filtered IDs:{} ".format(admin_client_accept_ids))
+
+    @decorators.idempotent_id('b6ac770e-a1d3-11eb-b534-74e5f9e2a801')
+    def test_show_transfer_accept_impersonate_another_project(self):
+        LOG.info('Create a zone as primary tenant')
+        zone = self.prm_zone_client.create_zone()[1]
+
+        # In case when something goes wrong with the test and E2E
+        # scenario fails for some reason, we'll use Admin tenant
+        # to activate Cleanup for a zone.
+        # Note: "ignore_errors=lib_exc.NotFound" is used to prevent a
+        # failure in case when E2E scenario was successfully completed.
+        # Means that Alt tenant has already been able to run a cleanup
+        # for a zone.
+        self.addCleanup(
+            self.wait_zone_delete, self.admin_zone_client, zone['id'],
+            headers={'x-auth-all-projects': True},
+            ignore_errors=lib_exc.NotFound)
+
+        LOG.info('Create a zone transfer_request as primary tenant')
+        transfer_request = self.prm_request_client.create_transfer_request(
+                                  zone['id'])[1]
+        self.addCleanup(self.prm_request_client.delete_transfer_request,
+                        transfer_request['id'])
+        data = {
+            "key": transfer_request['key'],
+            "zone_transfer_request_id": transfer_request['id']
+        }
+
+        LOG.info('Create a zone transfer_accept for Alt tenant, using '
+                 'Admin client and "sudo" option')
+        transfer_accept = self.admin_accept_client.create_transfer_accept(
+            data, headers={
+                'x-auth-sudo-project-id': self.os_alt.credentials.project_id,
+                'content-type': 'application/json'})[1]
+
+        LOG.info('Fetch the transfer_accept as Alt tenant')
+        body = self.alt_accept_client.show_transfer_accept(
+            transfer_accept['id'])[1]
+
+        LOG.info('Ensure the fetched response matches the '
+                 'created transfer_accept')
+        self.assertExpected(transfer_accept, body, self.excluded_keys)
+
+        # E2E accept zone transfer is done, therefore Alt tenant
+        # should be able to "cleanup" a transferred zone.
+        self.addCleanup(
+            self.wait_zone_delete, self.alt_zone_client, zone['id'])
+
+
+class TransferAcceptTestNegative(BaseTransferAcceptTest):
+
+    credentials = ['primary', 'alt', 'admin']
+
+    @classmethod
+    def setup_credentials(cls):
+        # Do not create network resources for these test.
+        cls.set_network_resources()
+        super(TransferAcceptTestNegative, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
+        super(TransferAcceptTestNegative, cls).setup_clients()
+        cls.zone_client = cls.os_primary.zones_client
+        cls.request_client = cls.os_primary.transfer_request_client
+        cls.client = cls.os_primary.transfer_accept_client
+
+    @decorators.idempotent_id('324a3e80-a1cc-11eb-b534-74e5f9e2a801')
+    def test_create_transfer_accept_using_invalid_key(self):
+        LOG.info('Create a zone')
+        zone = self.zone_client.create_zone()[1]
+        self.addCleanup(self.wait_zone_delete, self.zone_client, zone['id'])
+
+        LOG.info('Create a zone transfer_request')
+        transfer_request = self.request_client.create_transfer_request(
+                                  zone['id'])[1]
+
+        data = {"key": data_utils.rand_password(len(transfer_request['key'])),
+                "zone_transfer_request_id": transfer_request['id']}
+
+        LOG.info('Create a zone transfer_accept using invalid key')
+        self.assertRaises(
+            lib_exc.Forbidden, self.client.create_transfer_accept,
+            transfer_accept_data=data)
+
+    @decorators.idempotent_id('23afb948-a1ce-11eb-b534-74e5f9e2a801')
+    def test_create_transfer_accept_using_deleted_transfer_request_id(self):
+        LOG.info('Create a zone')
+        zone = self.zone_client.create_zone()[1]
+        self.addCleanup(self.wait_zone_delete, self.zone_client, zone['id'])
+
+        LOG.info('Create a zone transfer_request')
+        transfer_request = self.request_client.create_transfer_request(
+                                  zone['id'])[1]
+
+        data = {
+                 "key": transfer_request['key'],
+                 "zone_transfer_request_id": transfer_request['id']
+        }
+
+        LOG.info('Delete transfer request')
+        self.request_client.delete_transfer_request(transfer_request['id'])
+
+        LOG.info('Ensure 404 when accepting non existing request ID')
+        self.assertRaises(lib_exc.NotFound,
+            lambda: self.client.create_transfer_accept(data))
diff --git a/designate_tempest_plugin/tests/api/v2/test_zones.py b/designate_tempest_plugin/tests/api/v2/test_zones.py
index c7e2fb1..78b8d4a 100644
--- a/designate_tempest_plugin/tests/api/v2/test_zones.py
+++ b/designate_tempest_plugin/tests/api/v2/test_zones.py
@@ -337,3 +337,58 @@
         LOG.info('Create a zone as an alt user with existing superdomain')
         self.assertRaises(lib_exc.Forbidden,
             self.alt_client.create_zone, name=zone_name)
+
+
+class ZonesNegativeTest(BaseZonesTest):
+    @classmethod
+    def setup_credentials(cls):
+        # Do not create network resources for these test.
+        cls.set_network_resources()
+        super(ZonesNegativeTest, cls).setup_credentials()
+
+    @classmethod
+    def setup_clients(cls):
+        super(ZonesNegativeTest, cls).setup_clients()
+        cls.client = cls.os_primary.zones_client
+
+    @decorators.idempotent_id('551853c0-8593-11eb-8c8a-74e5f9e2a801')
+    def test_no_valid_zone_name(self):
+        no_valid_names = ['a' * 1000, '___', '!^%&^#%^!@#', 'ggg', '.a', '']
+        for name in no_valid_names:
+            LOG.info('Trying to create a zone named: {} '.format(name))
+            self.assertRaisesDns(
+                lib_exc.BadRequest, 'invalid_object', 400,
+                self.client.create_zone, name=name)
+
+    @decorators.idempotent_id('551853c0-8593-11eb-8c8a-74e5f9e2a801')
+    def test_no_valid_email(self):
+        no_valid_emails = [
+            'zahlabut#gmail.com', '123456', '___', '', '*&^*^%$']
+        for email in no_valid_emails:
+            LOG.info(
+                'Trying to create a zone using: {} as email'
+                ' value: '.format(email))
+            self.assertRaisesDns(
+                lib_exc.BadRequest, 'invalid_object', 400,
+                self.client.create_zone, email=email)
+
+    @decorators.idempotent_id('551853c0-8593-11eb-8c8a-74e5f9e2a801')
+    def test_no_valid_ttl(self):
+        no_valid_tls = ['zahlabut', -60000,
+                        2147483647 + 10]  # Max valid TTL is 2147483647
+
+        for ttl in no_valid_tls:
+            LOG.info(
+                'Trying to create a zone using: {} as TTL'
+                ' value: '.format(ttl))
+            self.assertRaisesDns(
+                lib_exc.BadRequest, 'invalid_object', 400,
+                self.client.create_zone, ttl=ttl)
+
+    @decorators.idempotent_id('a3b0a928-a682-11eb-9899-74e5f9e2a801')
+    def test_huge_size_description(self):
+        LOG.info('Trying to create a zone using huge size description')
+        self.assertRaisesDns(
+            lib_exc.BadRequest, 'invalid_object', 400,
+            self.client.create_zone,
+            description=dns_data_utils.rand_zone_name() * 10000)
diff --git a/designate_tempest_plugin/tests/api/v2/test_zones_imports.py b/designate_tempest_plugin/tests/api/v2/test_zones_imports.py
index 6c71306..950e021 100644
--- a/designate_tempest_plugin/tests/api/v2/test_zones_imports.py
+++ b/designate_tempest_plugin/tests/api/v2/test_zones_imports.py
@@ -72,10 +72,8 @@
         zone_import = self.client.create_zone_import(
             zonefile_data=dns_data_utils.rand_zonefile_data(ttl='zahlabut'))[1]
         self.addCleanup(self.clean_up_resources, zone_import['id'])
-        self.assertEqual(
-            const.ERROR,
-            self.client.show_zone_import(zone_import['id'])[1]['status'],
-            'Failed, expected status is: ERROR')
+        waiters.wait_for_zone_import_status(
+            self.client, zone_import['id'], "ERROR")
 
     @decorators.idempotent_id('31eaf25a-9532-11eb-a55d-74e5f9e2a801')
     def test_create_zone_import_invalid_name(self):
@@ -83,10 +81,8 @@
         zone_import = self.client.create_zone_import(
             zonefile_data=dns_data_utils.rand_zonefile_data(name='@@@'))[1]
         self.addCleanup(self.clean_up_resources, zone_import['id'])
-        self.assertEqual(
-            const.ERROR,
-            self.client.show_zone_import(zone_import['id'])[1]['status'],
-            'Failed, expected status is: ERROR')
+        waiters.wait_for_zone_import_status(
+            self.client, zone_import['id'], "ERROR")
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('c8909558-0dc6-478a-9e91-eb97b52e59e0')