Merge "Adding new test cases for zone export showfile API"
diff --git a/designate_tempest_plugin/services/dns/v2/json/zone_exports_client.py b/designate_tempest_plugin/services/dns/v2/json/zone_exports_client.py
index 8915ceb..4057b7e 100644
--- a/designate_tempest_plugin/services/dns/v2/json/zone_exports_client.py
+++ b/designate_tempest_plugin/services/dns/v2/json/zone_exports_client.py
@@ -23,7 +23,7 @@
         """Create a zone export.
 
         :param uuid: Unique identifier of the zone in UUID format.
-        :param params: A Python dict that represents the query paramaters to
+        :param params: A Python dict that represents the query parameters to
                        include in the request URI.
         :param wait_until: Block until the exported zone reaches the
                            desired status
@@ -47,7 +47,7 @@
         """Get the zone export task
 
         :param uuid: Unique identifier of the zone export task in UUID format.
-        :param params: A Python dict that represents the query paramaters to
+        :param params: A Python dict that represents the query parameters to
                        include in the request URI.
         :param headers (dict): The headers to use for the request.
         :return: Serialized exported zone as a dictionary.
@@ -56,25 +56,42 @@
              'zones/tasks/exports', uuid, params=params, headers=headers)
 
     @base.handle_errors
-    def show_exported_zonefile(self, uuid, params=None):
+    def show_exported_zonefile(self, uuid, params=None, headers=None):
+
         """Get the exported zone file
 
-        :param uuid: Unique identifier of the zone exprot task in UUID format.
-        :param params: A Python dict that represents the query paramaters to
+        :param uuid: Unique identifier of the zone export task in UUID format.
+        :param params: A Python dict that represents the query parameters to
                        include in the request URI.
+        :param headers: 3 options to send headers:
+                       1) If headers dict provided is missing "Accept" key -
+                          "{Accept:text/dns}" will be added.
+                       2) If header is None -
+                          "{Accept:text/dns}" will be sent.
+                       3) If function is called with no headers,
+                           means empty dict {} -
+                          no headers will be sent (empty dictionary)
+
         :return: Serialized exported zone as a dictionary.
         """
-        headers = {'Accept': 'text/dns'}
+
+        if headers:
+            if 'accept' not in [key.lower() for key in headers.keys()]:
+                headers['Accept'] = 'text/dns'
+        elif headers is None:
+            headers = {'Accept': 'text/dns'}
+        else:
+            headers = {}
 
         return self._show_request(
-            'zones/tasks/exports/{0}/export'.format(uuid), uuid='',
-            headers=headers, params=params)
+            'zones/tasks/exports/{0}/export'.format(uuid),
+            uuid='', headers=headers, params=params)
 
     @base.handle_errors
     def list_zone_exports(self, params=None, headers=None):
         """List zone export tasks
 
-        :param params: A Python dict that represents the query paramaters to
+        :param params: A Python dict that represents the query parameters to
                        include in the request URI.
         :param headers (dict): The headers to use for the request.
         :return: Serialized exported zone as a list.
@@ -87,7 +104,7 @@
         """Deletes the zone export task with the specified UUID.
 
         :param uuid: The unique identifier of the exported zone.
-        :param params: A Python dict that represents the query paramaters to
+        :param params: A Python dict that represents the query parameters to
                        include in the request URI.
         :return: A tuple with the server response and the response body.
         """
diff --git a/designate_tempest_plugin/tests/api/v2/test_zones_exports.py b/designate_tempest_plugin/tests/api/v2/test_zones_exports.py
index 4841d18..31da506 100644
--- a/designate_tempest_plugin/tests/api/v2/test_zones_exports.py
+++ b/designate_tempest_plugin/tests/api/v2/test_zones_exports.py
@@ -16,8 +16,11 @@
 from tempest import config
 from tempest.lib import decorators
 from tempest.lib import exceptions as lib_exc
+from tempest.lib.common.utils import data_utils
 
 from designate_tempest_plugin.tests import base
+from designate_tempest_plugin.common import waiters
+from designate_tempest_plugin.common import constants as const
 
 CONF = config.CONF
 LOG = logging.getLogger(__name__)
@@ -49,32 +52,32 @@
         cls.client = cls.os_primary.dns_v2.ZoneExportsClient()
         cls.alt_client = cls.os_alt.dns_v2.ZoneExportsClient()
 
-    @decorators.idempotent_id('2dd8a9a0-98a2-4bf6-bb51-286583b30f40')
-    def test_create_zone_export(self):
+    def _create_zone_export(self):
         LOG.info('Create a zone')
-        _, zone = self.zone_client.create_zone()
+        zone = self.zone_client.create_zone()[1]
         self.addCleanup(self.wait_zone_delete, self.zone_client, zone['id'])
 
         LOG.info('Create a zone export')
-        _, zone_export = self.client.create_zone_export(zone['id'])
+        zone_export = self.client.create_zone_export(zone['id'])[1]
         self.addCleanup(self.client.delete_zone_export, zone_export['id'])
+        waiters.wait_for_zone_export_status(
+            self.client, zone_export['id'], const.COMPLETE)
+        return zone, zone_export
+
+    @decorators.idempotent_id('2dd8a9a0-98a2-4bf6-bb51-286583b30f40')
+    def test_create_zone_export(self):
+        zone_export = self._create_zone_export()[1]
 
         LOG.info('Ensure we respond with PENDING')
-        self.assertEqual('PENDING', zone_export['status'])
+        self.assertEqual(const.PENDING, zone_export['status'])
 
     @decorators.attr(type='smoke')
     @decorators.idempotent_id('2d29a2a9-1941-4b7e-9d8a-ad6c2140ea68')
     def test_show_zone_export(self):
-        LOG.info('Create a zone')
-        _, zone = self.zone_client.create_zone()
-        self.addCleanup(self.wait_zone_delete, self.zone_client, zone['id'])
-
-        LOG.info('Create a zone export')
-        resp, zone_export = self.client.create_zone_export(zone['id'])
-        self.addCleanup(self.client.delete_zone_export, zone_export['id'])
+        zone_export = self._create_zone_export()[1]
 
         LOG.info('Re-Fetch the zone export')
-        _, body = self.client.show_zone_export(zone_export['id'])
+        body = self.client.show_zone_export(zone_export['id'])[1]
 
         LOG.info('Ensure the fetched response matches the zone export')
         self.assertExpected(zone_export, body, self.excluded_keys)
@@ -116,20 +119,16 @@
         _, body = self.client.delete_zone_export(zone_export['id'])
 
         LOG.info('Ensure the zone export has been successfully deleted')
-        self.assertRaises(lib_exc.NotFound,
-            lambda: self.client.show_zone_export(zone_export['id']))
+        self.assertRaises(
+            lib_exc.NotFound,
+            self.client.show_zone_export, zone_export['id'])
 
     @decorators.idempotent_id('476bfdfe-58c8-46e2-b376-8403c0fff440')
     def test_list_zone_exports(self):
-        LOG.info('Create a zone')
-        _, zone = self.zone_client.create_zone()
-        self.addCleanup(self.wait_zone_delete, self.zone_client, zone['id'])
-
-        _, export = self.client.create_zone_export(zone['id'])
-        self.addCleanup(self.client.delete_zone_export, export['id'])
+        self._create_zone_export()[1]
 
         LOG.info('List zone exports')
-        _, body = self.client.list_zone_exports()
+        body = self.client.list_zone_exports()[1]
 
         self.assertGreater(len(body['exports']), 0)
 
@@ -183,8 +182,10 @@
         alt_export = self.alt_client.create_zone_export(alt_zone['id'])[1]
         self.alt_client.delete_zone_export(alt_export['id'])
         LOG.info('Ensure the zone export has been successfully deleted')
-        self.assertRaises(lib_exc.NotFound,
-            lambda: self.alt_client.show_zone_export(alt_export['id']))
+        self.assertRaises(
+            lib_exc.NotFound,
+            self.alt_client.show_zone_export,
+            alt_export['id'])
 
         LOG.info('Filter out "export zones" in status:ZAHLABUT,'
                  ' expected: empty list')
@@ -236,6 +237,18 @@
         cls.client = cls.os_primary.dns_v2.ZoneExportsClient()
         cls.alt_client = cls.os_alt.dns_v2.ZoneExportsClient()
 
+    def _create_zone_export(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 export')
+        zone_export = self.client.create_zone_export(zone['id'])[1]
+        self.addCleanup(self.client.delete_zone_export, zone_export['id'])
+        waiters.wait_for_zone_export_status(
+            self.client, zone_export['id'], const.COMPLETE)
+        return zone, zone_export
+
     @decorators.idempotent_id('76ab8ec4-95fd-11eb-b1cd-74e5f9e2a801')
     def test_create_zone_export_using_invalid_zone_id(self):
         self.assertRaises(
@@ -260,7 +273,6 @@
         zone = self.zone_client.create_zone()[1]
         self.addCleanup(self.wait_zone_delete, self.zone_client, zone['id'],
                         ignore_errors=lib_exc.NotFound)
-
         LOG.info("Delete the zone and wait till it's done.")
         self.zone_client.delete_zone(zone['id'])[1]
         self.wait_zone_delete(self.zone_client, zone['id'])
@@ -268,3 +280,21 @@
         LOG.info('Ensure we respond with NotFound exception')
         self.assertRaises(
             lib_exc.NotFound, self.client.create_zone_export, zone['id'])
+
+    @decorators.idempotent_id('9a878646-f66b-4fa4-ae95-f3ac3f8e3d31')
+    def test_show_zonefile_using_not_existing_zone_export_id(self):
+        LOG.info('Expected: 404 Not Found zone export')
+        self.assertRaises(lib_exc.NotFound,
+                          self.client.show_exported_zonefile,
+                          data_utils.rand_uuid())
+
+    @decorators.idempotent_id('52a1fee0-c338-4ed9-b9f9-41ee7fd73375')
+    def test_show_zonefile_not_supported_accept_value(self):
+        zone, zone_export = self._create_zone_export()
+        # Tempest-lib _error_checker will raise UnexpectedResponseCode
+        e = self.assertRaises(
+            lib_exc.UnexpectedResponseCode, self.client.show_exported_zonefile,
+            zone_export['id'], headers={'Accept': 'image/jpeg'})
+        self.assertEqual(406, e.resp.status,
+                         "Failed, actual response code is:{0}"
+                         "but expected is: 406".format(e.resp.status))
diff --git a/designate_tempest_plugin/tests/scenario/v2/test_zones_export.py b/designate_tempest_plugin/tests/scenario/v2/test_zones_export.py
index 916e634..5d0de56 100644
--- a/designate_tempest_plugin/tests/scenario/v2/test_zones_export.py
+++ b/designate_tempest_plugin/tests/scenario/v2/test_zones_export.py
@@ -13,48 +13,61 @@
 # under the License.
 
 from oslo_log import log as logging
+from tempest import config
 from tempest.lib import decorators
-
 from designate_tempest_plugin.common import waiters
 from designate_tempest_plugin.tests.api.v2.test_zones_exports import \
     BaseZoneExportsTest
+from designate_tempest_plugin.common import constants as const
 
+CONF = config.CONF
 LOG = logging.getLogger(__name__)
 
 
 class ZonesExportTest(BaseZoneExportsTest):
+    credentials = ["primary", "admin", "system_admin"]
+
+    @classmethod
+    def setup_credentials(cls):
+        # Do not create network resources for these test.
+        cls.set_network_resources()
+        super(ZonesExportTest, cls).setup_credentials()
 
     @classmethod
     def setup_clients(cls):
         super(ZonesExportTest, cls).setup_clients()
-
+        if CONF.enforce_scope.designate:
+            cls.admin_client = cls.os_system_admin.dns_v2.ZoneExportsClient()
+        else:
+            cls.admin_client = cls.os_admin.dns_v2.ZoneExportsClient()
         cls.client = cls.os_primary.dns_v2.ZoneExportsClient()
         cls.zones_client = cls.os_primary.dns_v2.ZonesClient()
 
-    @decorators.attr(type='slow')
-    @decorators.idempotent_id('0484c3c4-df57-458e-a6e5-6eb63e0475e0')
-    def test_create_zone_export_and_show_exported_zonefile(self):
-        LOG.info('Create a zone to be exported')
-        _, zone = self.zones_client.create_zone()
+    def _create_zone_export(self):
+        LOG.info('Create a zone')
+        zone = self.zones_client.create_zone()[1]
         self.addCleanup(self.wait_zone_delete, self.zones_client, zone['id'])
 
         LOG.info('Create a zone export')
-        _, zone_export = self.client.create_zone_export(zone['id'])
+        zone_export = self.client.create_zone_export(zone['id'])[1]
         self.addCleanup(self.client.delete_zone_export, zone_export['id'])
+        waiters.wait_for_zone_export_status(
+            self.client, zone_export['id'], const.COMPLETE)
+        return zone, zone_export
 
-        self.assertEqual('PENDING', zone_export['status'])
+    @decorators.idempotent_id('0484c3c4-df57-458e-a6e5-6eb63e0475e0')
+    def test_create_zone_export_and_show_exported_zonefile(self):
+        zone, zone_export = self._create_zone_export()
+
+        self.assertEqual(const.PENDING, zone_export['status'])
         self.assertEqual(zone['id'], zone_export['zone_id'])
         self.assertIsNone(zone_export['links'].get('export'))
         self.assertIsNone(zone_export['location'])
 
-        LOG.info('Wait for the zone export to COMPLETE')
-        waiters.wait_for_zone_export_status(
-            self.client, zone_export['id'], 'COMPLETE')
-
         LOG.info('Check the zone export looks good')
         _, zone_export = self.client.show_zone_export(zone_export['id'])
 
-        self.assertEqual('COMPLETE', zone_export['status'])
+        self.assertEqual(const.COMPLETE, zone_export['status'])
         self.assertEqual(zone['id'], zone_export['zone_id'])
         self.assertIsNotNone(zone_export['links'].get('export'))
         self.assertIsNotNone(zone_export['location'])
@@ -63,3 +76,54 @@
         _, zonefile = self.client.show_exported_zonefile(zone_export['id'])
         self.assertEqual(zone['name'], zonefile.origin)
         self.assertEqual(zone['ttl'], zonefile.ttl)
+
+    @decorators.idempotent_id('56b8f30e-cd45-4c7a-bc0c-bbf92d7dc697')
+    def test_show_exported_zonefile_impersonate_another_project(self):
+        zone, zone_export = self._create_zone_export()
+
+        LOG.info('As Admin impersonate "primary" client,'
+                 ' to show exported zone file')
+        response = self.admin_client.show_exported_zonefile(
+            zone_export['id'], headers={
+                'x-auth-sudo-project-id': zone['project_id']})[1]
+        self.assertEqual(zone['name'], response.origin)
+        self.assertEqual(zone['ttl'], response.ttl)
+
+    @decorators.idempotent_id('c2e55514-ff2e-41d9-a3cc-9e78873254c9')
+    def test_show_exported_zonefile_all_projects(self):
+        zone, zone_export = self._create_zone_export()
+        resp_headers, resp_data = self.admin_client.show_exported_zonefile(
+            zone_export['id'], headers={
+                'x-auth-all-projects': True
+            })
+        self.assertEqual(zone['name'], resp_data.origin)
+        self.assertEqual(zone['ttl'], resp_data.ttl)
+
+    @decorators.idempotent_id('9746b7f2-2df4-448c-8a85-5ab6bf74f1fe')
+    def test_show_exported_zonefile_any_mime_type(self):
+        zone, zone_export = self._create_zone_export()
+        resp_headers, resp_data = self.client.show_exported_zonefile(
+            zone_export['id'], headers={'Accept': '*/*'})
+
+        LOG.info('Ensure Content-Type: text/dns')
+        self.assertIn(
+            'text/dns', resp_headers['content-type'],
+            "Failed, the expected 'Content-type:text/dns wasn't received.")
+
+        LOG.info('Ensure exported data ia as expected')
+        self.assertEqual(zone['name'], resp_data.origin)
+        self.assertEqual(zone['ttl'], resp_data.ttl)
+
+    @decorators.idempotent_id('dc7a9dde-d287-4e22-9788-26578f0d3bf0')
+    def test_missing_accept_headers(self):
+        zone, zone_export = self._create_zone_export()
+        resp_headers, resp_data = self.client.show_exported_zonefile(
+            zone_export['id'], headers={})
+        LOG.info('Ensure Content-Type: text/dns')
+        self.assertIn(
+            'text/dns', resp_headers['content-type'],
+            "Failed, the expected 'Content-type:text/dns wasn't received.")
+
+        LOG.info('Ensure exported data ia as expected')
+        self.assertEqual(zone['name'], resp_data.origin)
+        self.assertEqual(zone['ttl'], resp_data.ttl)