class TestIdentityMimicOSKSCatalogAdminListExternalServices(
        SynchronousTestCase, IdentityAuthMixin):
    """
    Tests for ``/identity/v2.0/services``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = "/identity/v2.0/services"
        self.eeapi_name = u"externalServiceName"
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"GET"

    @ddt.data(0, 1, 10)
    def test_listing(self, api_entry_count):
        """
        GET will list the registered services.
        """
        # create the desired number of services per test parameter
        api_list = [
            ExternalApiStore(
                text_type(uuid.uuid4()),
                self.eeapi_name + text_type(uuid.uuid4()),
                'service-' + text_type(uuid.uuid4()),
            ) for ignored in range(api_entry_count)
        ]

        # add the services
        for api in api_list:
            self.core.add_api(api)

        # retrieve the listing using the REST interface
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        def validate_api(api_id, api_type, api_name):
            """
            Lookup the API in the test's set of APIs  and match the values
            """
            matching_apis = [api for api in api_list if api.uuid_key == api_id]
            self.assertEqual(len(matching_apis), 1)
            [matching_api] = matching_apis
            self.assertEqual(api_id, matching_api.uuid_key)
            self.assertEqual(api_type, matching_api.type_key)
            self.assertEqual(api_name, matching_api.name_key)

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body["OS-KSADM:services"]), len(api_list))
        # ensure all services in the response match one in the generated
        # initially generated set
        for entry in json_body["OS-KSADM:services"]:
            validate_api(entry['id'], entry['type'], entry['name'])
Beispiel #2
0
    def test_get_external_apis(self):
        """
        Validate retrieving the list of external APIs
        """
        eeapi = make_example_external_api(self,
                                          name=self.eeapi_name,
                                          set_enabled=True)

        core = MimicCore(Clock(), [])
        self.assertEqual(len(core.get_external_apis()), 0)

        core.add_api(eeapi)
        api_list = core.get_external_apis()
        self.assertEqual(len(api_list), 1)
        self.assertEqual(list(api_list), [eeapi.uuid_key])
Beispiel #3
0
    def test_get_external_apis(self):
        """
        Validate retrieving the list of external APIs
        """
        eeapi = make_example_external_api(
            self,
            name=self.eeapi_name,
            set_enabled=True
        )

        core = MimicCore(Clock(), [])
        self.assertEqual(len(core.get_external_apis()), 0)

        core.add_api(eeapi)
        api_list = core.get_external_apis()
        self.assertEqual(len(api_list), 1)
        self.assertEqual(list(api_list), [eeapi.uuid_key])
class TestIdentityOSKSCatalogTenantAdminEndpointTemplatesList(
        SynchronousTestCase, IdentityAuthMixin, ServiceIdHeaderMixin):
    """
    Tests for ``/identity/v2.0/<tenant-id>/OS-KSCATALOG/endpointTemplates``,
    provided by :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.tenant_id = 'some_tenant'
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = (
            "/identity/v2.0/tenants/" + self.tenant_id +
            "/OS-KSCATALOG/endpoints"
        )
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(
            self,
            name=self.eeapi_name,
            set_enabled=True
        )
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"GET"

    def test_list_only_internal_apis_available(self):
        """
        GET will not list Internal APIs.
        """
        self.core.add_api(make_example_internal_api(self))
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['endpoints']), 0)
        self.assertEqual(len(json_body['endpoints_links']), 0)

    def test_list_single_template(self):
        """
        GET will list an external API if it has a endpoint template.
        """
        self.core.add_api(self.eeapi)

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['endpoints']), 1)
        self.assertEqual(len(json_body['endpoints_links']), 0)

    def test_list_template_all_disabled(self):
        """
        GET will not list endpoint templates that are disabled.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)
        self.eeapi.endpoint_templates[id_key].enabled_key = False

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['endpoints']), 0)
        self.assertEqual(len(json_body['endpoints_links']), 0)

    def test_list_single_template_external_and_internal_apis(self):
        """
        GET will only list external API endpoint templates.
        """
        self.core.add_api(self.eeapi)
        self.core.add_api(make_example_internal_api(self))

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['endpoints']), 1)
        self.assertEqual(len(json_body['endpoints_links']), 0)

    def test_multiple_external_apis(self):
        """
        GET will list multiple external APIs.
        """
        api_list = [
            make_example_external_api(
                self,
                name=self.eeapi_name + text_type(uuid.uuid4()),
                service_type='service-' + text_type(uuid.uuid4()),
                set_enabled=True
            )
            for ignored in range(10)
        ]
        # eeapi should be the first entry in the list
        api_list.insert(0, self.eeapi)

        for api in api_list:
            self.core.add_api(api)

        self.assertEqual(len(self.core._uuid_to_api_external),
                         len(api_list))

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        def get_header(header_name):
            return response.headers.getRawHeaders(header_name)[0].decode("utf-8")

        self.assertEqual(response.code, 200)

        self.assertEqual(len(json_body['endpoints']),
                         len(api_list))
        self.assertEqual(len(json_body['endpoints_links']), 0)
class TestIdentityOSKSCatalogTenantAdminEndpointTemplatesCreate(
        SynchronousTestCase, IdentityAuthMixin, InvalidJsonMixin):
    """
    Tests for ``/identity/v2.0/<tenant-id>/OS-KSCATALOG/endpointTemplates``,
    provided by :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.tenant_id = 'some_tenant'
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = (
            "/identity/v2.0/tenants/" + self.tenant_id +
            "/OS-KSCATALOG/endpoints"
        )
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(
            self,
            name=self.eeapi_name,
            set_enabled=False
        )
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"POST"

    def test_json_body_missing_required_field_oskscatalog(self):
        """
        POST with the OS-KSCATALOG:endointTemplate body entirely missing
        results in 400.
        """
        data = {
            'id': text_type(uuid.uuid4()),
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertTrue(
            json_body['badRequest']['message'].startswith(
                "Invalid Content. OS-KSCATALOG:endpointTemplate:id is "
                "required."
            )
        )

    def test_json_body_missing_required_field_template_id(self):
        """
        POST with the OS-KSCATALOG:endointTemplate body  missing it's content
        results in 400.
        """
        data = {
            "OS-KSCATALOG:endpointTemplate": {
            }
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertTrue(
            json_body['badRequest']['message'].startswith(
                "Invalid Content. OS-KSCATALOG:endpointTemplate:id is "
                "required."
            )
        )

    def test_invalid_template_id(self):
        """
        POST with invalid endpointTemplate ID results in 404.
        """
        self.core.add_api(self.eeapi)
        data = {
            "OS-KSCATALOG:endpointTemplate": {
                "id": "some-id"
            }
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertTrue(
            json_body['itemNotFound']['message'].startswith(
                "Unable to locate an External API with the given Template ID."
            )
        )

    def test_enable_template(self):
        """
        POST can update an existing endpoint template resulting in a 201.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)
        data = {
            "OS-KSCATALOG:endpointTemplate": {
                "id": id_key
            }
        }

        req = request(self, self.root, self.verb,
                      self.uri,
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 201)
class TestIdentityOSKSCatalogAdminEndpointTemplatesDelete(SynchronousTestCase, IdentityAuthMixin):
    """
    Tests for ``/identity/v2.0/OS-KSCATALOG/endpointTemplates``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(
            self,
            name=self.eeapi_name,
            set_enabled=True
        )
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"DELETE"
        self.ept_template_id = get_template_id(self, self.eeapi)
        self.uri = (
            "/identity/v2.0/OS-KSCATALOG/endpointTemplates/" +
            self.ept_template_id
        )

    def test_invalid_template_id(self):
        """
        DELTE requires a valid endpoint template id, otherwise results in 404.
        """
        self.eeapi.remove_template(self.ept_template_id)
        self.core.add_api(self.eeapi)
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(
            json_body['itemNotFound']['message'],
            "Unable to locate an External API with the given Template ID."
        )

    def test_invalid_template_id_with_service_header(self):
        """
        DELETE requires the endpoint template to exist, otherwise results
        in 404.
        """
        self.eeapi.remove_template(self.ept_template_id)
        self.core.add_api(self.eeapi)
        self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(
            json_body['itemNotFound']['message'],
            "Unable to locate an External API with the given Template ID."
        )

    @ddt.data(
        True, False
    )
    def test_remove_template_id(self, has_service_header):
        """
        DELETE removes an existing endpoint template, service id header is
        optional.
        """
        self.core.add_api(self.eeapi)
        if has_service_header:
            self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]
        req = request(self, self.root, self.verb,
                      self.uri,
                      headers=self.headers)
        response = self.successResultOf(req)
        self.assertEqual(response.code, 204)
class TestIdentityOSKSCatalogAdminEndpointTemplatesDelete(
        SynchronousTestCase, IdentityAuthMixin):
    """
    Tests for ``/identity/v2.0/OS-KSCATALOG/endpointTemplates``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(self,
                                               name=self.eeapi_name,
                                               set_enabled=True)
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"DELETE"
        self.ept_template_id = get_template_id(self, self.eeapi)
        self.uri = ("/identity/v2.0/OS-KSCATALOG/endpointTemplates/" +
                    self.ept_template_id)

    def test_invalid_template_id(self):
        """
        DELTE requires a valid endpoint template id, otherwise results in 404.
        """
        self.eeapi.remove_template(self.ept_template_id)
        self.core.add_api(self.eeapi)
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(
            json_body['itemNotFound']['message'],
            "Unable to locate an External API with the given Template ID.")

    def test_invalid_template_id_with_service_header(self):
        """
        DELETE requires the endpoint template to exist, otherwise results
        in 404.
        """
        self.eeapi.remove_template(self.ept_template_id)
        self.core.add_api(self.eeapi)
        self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(
            json_body['itemNotFound']['message'],
            "Unable to locate an External API with the given Template ID.")

    @ddt.data(True, False)
    def test_remove_template_id(self, has_service_header):
        """
        DELETE removes an existing endpoint template, service id header is
        optional.
        """
        self.core.add_api(self.eeapi)
        if has_service_header:
            self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]
        req = request(self,
                      self.root,
                      self.verb,
                      self.uri,
                      headers=self.headers)
        response = self.successResultOf(req)
        self.assertEqual(response.code, 204)
class TestIdentityOSKSCatalogAdminEndpointTemplatesList(
        SynchronousTestCase, IdentityAuthMixin, ServiceIdHeaderMixin):
    """
    Tests for ``/identity/v2.0/OS-KSCATALOG/endpointTemplates``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = "/identity/v2.0/OS-KSCATALOG/endpointTemplates"
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(self,
                                               name=self.eeapi_name,
                                               set_enabled=True)
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"GET"

    def test_list_only_internal_apis_available(self):
        """
        GET will return an empty listing when there are no External API
        endpoint templates.
        """
        self.core.add_api(make_example_internal_api(self))
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['OS-KSCATALOG']), 0)
        self.assertEqual(
            len(json_body['OS-KSCATALOG:endpointsTemplates_links']), 0)

    def test_list_single_template(self):
        """
        GET will return a endpoint template when there are External API
        endpoint templates.
        """
        self.core.add_api(self.eeapi)

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['OS-KSCATALOG']), 1)
        self.assertEqual(
            len(json_body['OS-KSCATALOG:endpointsTemplates_links']), 0)

    def test_list_single_template_external_and_internal_apis(self):
        """
        GET will only return the External API endpoint templates when they
        are available,
        even if there are also Internal APIs.
        """
        self.core.add_api(self.eeapi)
        self.core.add_api(make_example_internal_api(self))

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['OS-KSCATALOG']), 1)
        self.assertEqual(
            len(json_body['OS-KSCATALOG:endpointsTemplates_links']), 0)

    def test_multiple_external_apis(self):
        """
        GET can retrieve numerous External APIs that have External API Templates.
        """
        api_list = [
            make_example_external_api(
                self,
                name=self.eeapi_name + text_type(uuid.uuid4()),
                service_type='service-' + text_type(uuid.uuid4()))
            for ignored in range(10)
        ]
        #  eeapi needs to be the first in the list
        api_list.insert(0, self.eeapi)
        for api in api_list:
            self.core.add_api(api)

        self.assertEqual(len(self.core._uuid_to_api_external), len(api_list))

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)

        self.assertEqual(len(json_body['OS-KSCATALOG']), len(api_list))
        self.assertEqual(
            len(json_body['OS-KSCATALOG:endpointsTemplates_links']), 0)
class TestIdentityMimicOSKSCatalogAdminCreateExternalService(
        SynchronousTestCase, IdentityAuthMixin, InvalidJsonMixin):
    """
    Tests for ``/identity/v2.0/services``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = "/identity/v2.0/services"
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(
            self,
            name=self.eeapi_name,
            set_enabled=True
        )
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"POST"

    @ddt.data(
        'type', 'name'
    )
    def test_json_body_missing_required_field(self, remove_field):
        """
        POST requires 'name' field otherwise 400 is generated.
        """
        # normal JSON body
        data = {
            'type': 'some-type',
            'name': 'some-name'
        }
        # remove a portion of the body per the DDT data
        del data[remove_field]

        # POST the resulting JSON to the REST API
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        # API should return 400 since a required field is missing
        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertEqual(json_body['badRequest']['message'],
                         "Invalid Content. 'name' and 'type' fields are "
                         "required.")

    @ddt.data(
        (True, False, "Conflict: Service with the same name already exists."),
        (False, True, "Conflict: Service with the same uuid already exists."),
    )
    @ddt.unpack
    def test_service_name_or_id_already_exists(self, name_exists, id_exists, msg):
        """
        POST requires a unique UUID for the Service ID.
        """
        self.core.add_api(self.eeapi)
        data = {
            'id': self.eeapi.uuid_key if id_exists else text_type(uuid.uuid4()),
            'name': self.eeapi.name_key if name_exists else "some-other-name",
            'type': self.eeapi.type_key
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(json_body['conflict']['message'],
                         msg)

    @ddt.data(
        (True, True),
        (True, False),
        (False, True),
        (False, False)
    )
    @ddt.unpack
    def test_successfully_add_service(self, has_id_field, has_description):
        """
        POST accepts the service type and name regardless of whether
        an ID field is provided.
        """
        data = {
            'name': self.eeapi.name_key,
            'type': self.eeapi.type_key,
            'id': text_type(uuid.uuid4()),
            'description': 'testing external API'
        }
        if not has_id_field:
            del data['id']
        if not has_description:
            del data['description']

        req = request(self, self.root, self.verb,
                      "/identity/v2.0/services",
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 201)
class TestIdentityMimicOSKSCatalogAdminListExternalServices(SynchronousTestCase, IdentityAuthMixin):
    """
    Tests for ``/identity/v2.0/services``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = "/identity/v2.0/services"
        self.eeapi_name = u"externalServiceName"
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"GET"

    @ddt.data(
        0, 1, 10
    )
    def test_listing(self, api_entry_count):
        """
        GET will list the registered services.
        """
        # create the desired number of services per test parameter
        api_list = [
            ExternalApiStore(
                text_type(uuid.uuid4()),
                self.eeapi_name + text_type(uuid.uuid4()),
                'service-' + text_type(uuid.uuid4()),
            )
            for ignored in range(api_entry_count)
        ]

        # add the services
        for api in api_list:
            self.core.add_api(api)

        # retrieve the listing using the REST interface
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        def validate_api(api_id, api_type, api_name):
            """
            Lookup the API in the test's set of APIs  and match the values
            """
            matching_apis = [
                api for api in api_list if api.uuid_key == api_id
            ]
            self.assertEqual(len(matching_apis), 1)
            [matching_api] = matching_apis
            self.assertEqual(api_id, matching_api.uuid_key)
            self.assertEqual(api_type, matching_api.type_key)
            self.assertEqual(api_name, matching_api.name_key)

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body["OS-KSADM:services"]), len(api_list))
        # ensure all services in the response match one in the generated
        # initially generated set
        for entry in json_body["OS-KSADM:services"]:
            validate_api(entry['id'], entry['type'], entry['name'])
class TestIdentityMimicOSKSCatalogAdminDeleteExternalService(SynchronousTestCase, IdentityAuthMixin):
    """
    Tests for ``/identity/v2.0/services/<service-id>``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.eeapi_id = u"some-id"
        self.uri = "/identity/v2.0/services/" + self.eeapi_id
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(
            self,
            name=self.eeapi_name,
            set_enabled=True
        )
        self.eeapi2 = make_example_external_api(
            self,
            name=self.eeapi_name + " alternate"
        )
        self.eeapi.uuid_key = self.eeapi_id
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"DELETE"

    def test_invalid_service(self):
        """
        DELETE an unknown service will generate a 404.
        """
        data = {
            'name': 'some-name',
            'type': 'some-type',
            'id': 'some-id'
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(json_body['itemNotFound']['message'],
                         "Service not found. Unable to remove.")

    def test_service_has_template(self):
        """
        DELETE a service that still has a template results in 409.
        """
        self.core.add_api(self.eeapi)
        data = {
            'name': self.eeapi.name_key,
            'type': self.eeapi.type_key,
            'id': self.eeapi.uuid_key
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(json_body['conflict']['message'],
                         "Service still has endpoint templates.")

    def test_remove_service(self):
        """
        DELETE a service.
        """
        templates_to_remove = list(self.eeapi.endpoint_templates.keys())
        for template_id in templates_to_remove:
            self.eeapi.remove_template(template_id)

        self.core.add_api(self.eeapi)
        self.core.add_api(self.eeapi2)
        data = {
            'name': self.eeapi.name_key,
            'type': self.eeapi.type_key,
            'id': self.eeapi.uuid_key
        }

        req = request(self, self.root, self.verb,
                      self.uri,
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 204)
Beispiel #12
0
class TestIdentityOSKSCatalogTenantAdminEndpointTemplatesDelete(
        SynchronousTestCase, IdentityAuthMixin):
    """
    Tests for ``/identity/v2.0/<tenant-id>/OS-KSCATALOG/endpointTemplates``,
    provided by :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.tenant_id = 'some_tenant'
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(self, name=self.eeapi_name)
        self.template_id = get_template_id(self, self.eeapi)
        self.assertIsNotNone(self.template_id)
        self.uri = ("/identity/v2.0/tenants/" + self.tenant_id +
                    "/OS-KSCATALOG/endpoints/" + self.template_id)
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"DELETE"

    def test_invalid_template_id(self):
        """
        DELETE with an invalid endpoint template id results in 404.
        """
        self.eeapi.remove_template(self.template_id)
        self.core.add_api(self.eeapi)
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertTrue(json_body['itemNotFound']['message'].startswith(
            "Unable to locate an External API with the given Template ID."))

    def test_template_id_not_enabled_for_tenant(self):
        """
        DELETE for endpoint template not enabled for a tenant or globally
        results in 404.
        """
        self.core.add_api(self.eeapi)
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(json_body['itemNotFound']['message'],
                         "Template not enabled for tenant")

    def test_disable_template(self):
        """
        DELETE for endpoint template enabled for tenant results in 204.
        """
        self.core.add_api(self.eeapi)
        self.eeapi.enable_endpoint_for_tenant(self.tenant_id, self.template_id)
        eeapi2 = make_example_external_api(self,
                                           name="alternate " + self.eeapi_name)
        ept_id2 = get_template_id(self, eeapi2)
        eeapi2.remove_template(ept_id2)
        self.core.add_api(eeapi2)
        req = request(self,
                      self.root,
                      self.verb,
                      self.uri,
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 204)
Beispiel #13
0
class TestIdentityOSKSCatalogTenantAdminEndpointTemplatesList(
        SynchronousTestCase, IdentityAuthMixin, ServiceIdHeaderMixin):
    """
    Tests for ``/identity/v2.0/<tenant-id>/OS-KSCATALOG/endpointTemplates``,
    provided by :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.tenant_id = 'some_tenant'
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = ("/identity/v2.0/tenants/" + self.tenant_id +
                    "/OS-KSCATALOG/endpoints")
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(self,
                                               name=self.eeapi_name,
                                               set_enabled=True)
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"GET"

    def test_list_only_internal_apis_available(self):
        """
        GET will not list Internal APIs.
        """
        self.core.add_api(make_example_internal_api(self))
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['endpoints']), 0)
        self.assertEqual(len(json_body['endpoints_links']), 0)

    def test_list_single_template(self):
        """
        GET will list an external API if it has a endpoint template.
        """
        self.core.add_api(self.eeapi)

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['endpoints']), 1)
        self.assertEqual(len(json_body['endpoints_links']), 0)

    def test_list_template_all_disabled(self):
        """
        GET will not list endpoint templates that are disabled.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)
        self.eeapi.endpoint_templates[id_key].enabled_key = False

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['endpoints']), 0)
        self.assertEqual(len(json_body['endpoints_links']), 0)

    def test_list_single_template_external_and_internal_apis(self):
        """
        GET will only list external API endpoint templates.
        """
        self.core.add_api(self.eeapi)
        self.core.add_api(make_example_internal_api(self))

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['endpoints']), 1)
        self.assertEqual(len(json_body['endpoints_links']), 0)

    def test_multiple_external_apis(self):
        """
        GET will list multiple external APIs.
        """
        api_list = [
            make_example_external_api(
                self,
                name=self.eeapi_name + text_type(uuid.uuid4()),
                service_type='service-' + text_type(uuid.uuid4()),
                set_enabled=True) for ignored in range(10)
        ]
        # eeapi should be the first entry in the list
        api_list.insert(0, self.eeapi)

        for api in api_list:
            self.core.add_api(api)

        self.assertEqual(len(self.core._uuid_to_api_external), len(api_list))

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         headers=self.headers))

        def get_header(header_name):
            return response.headers.getRawHeaders(header_name)[0].decode(
                "utf-8")

        self.assertEqual(response.code, 200)

        self.assertEqual(len(json_body['endpoints']), len(api_list))
        self.assertEqual(len(json_body['endpoints_links']), 0)
Beispiel #14
0
class TestIdentityOSKSCatalogTenantAdminEndpointTemplatesCreate(
        SynchronousTestCase, IdentityAuthMixin, InvalidJsonMixin):
    """
    Tests for ``/identity/v2.0/<tenant-id>/OS-KSCATALOG/endpointTemplates``,
    provided by :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.tenant_id = 'some_tenant'
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = ("/identity/v2.0/tenants/" + self.tenant_id +
                    "/OS-KSCATALOG/endpoints")
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(self,
                                               name=self.eeapi_name,
                                               set_enabled=False)
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"POST"

    def test_json_body_missing_required_field_oskscatalog(self):
        """
        POST with the OS-KSCATALOG:endointTemplate body entirely missing
        results in 400.
        """
        data = {
            'id': text_type(uuid.uuid4()),
        }
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertTrue(json_body['badRequest']['message'].startswith(
            "Invalid Content. OS-KSCATALOG:endpointTemplate:id is "
            "required."))

    def test_json_body_missing_required_field_template_id(self):
        """
        POST with the OS-KSCATALOG:endointTemplate body  missing it's content
        results in 400.
        """
        data = {"OS-KSCATALOG:endpointTemplate": {}}
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertTrue(json_body['badRequest']['message'].startswith(
            "Invalid Content. OS-KSCATALOG:endpointTemplate:id is "
            "required."))

    def test_invalid_template_id(self):
        """
        POST with invalid endpointTemplate ID results in 404.
        """
        self.core.add_api(self.eeapi)
        data = {"OS-KSCATALOG:endpointTemplate": {"id": "some-id"}}
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertTrue(json_body['itemNotFound']['message'].startswith(
            "Unable to locate an External API with the given Template ID."))

    def test_enable_template(self):
        """
        POST can update an existing endpoint template resulting in a 201.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)
        data = {"OS-KSCATALOG:endpointTemplate": {"id": id_key}}

        req = request(self,
                      self.root,
                      self.verb,
                      self.uri,
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 201)
class TestIdentityMimicOSKSCatalogAdminCreateExternalService(
        SynchronousTestCase, IdentityAuthMixin, InvalidJsonMixin):
    """
    Tests for ``/identity/v2.0/services``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = "/identity/v2.0/services"
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(self,
                                               name=self.eeapi_name,
                                               set_enabled=True)
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"POST"

    @ddt.data('type', 'name')
    def test_json_body_missing_required_field(self, remove_field):
        """
        POST requires 'name' field otherwise 400 is generated.
        """
        # normal JSON body
        data = {'type': 'some-type', 'name': 'some-name'}
        # remove a portion of the body per the DDT data
        del data[remove_field]

        # POST the resulting JSON to the REST API
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        # API should return 400 since a required field is missing
        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertEqual(
            json_body['badRequest']['message'],
            "Invalid Content. 'name' and 'type' fields are "
            "required.")

    @ddt.data(
        (True, False, "Conflict: Service with the same name already exists."),
        (False, True, "Conflict: Service with the same uuid already exists."),
    )
    @ddt.unpack
    def test_service_name_or_id_already_exists(self, name_exists, id_exists,
                                               msg):
        """
        POST requires a unique UUID for the Service ID.
        """
        self.core.add_api(self.eeapi)
        data = {
            'id':
            self.eeapi.uuid_key if id_exists else text_type(uuid.uuid4()),
            'name': self.eeapi.name_key if name_exists else "some-other-name",
            'type': self.eeapi.type_key
        }
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(json_body['conflict']['message'], msg)

    @ddt.data((True, True), (True, False), (False, True), (False, False))
    @ddt.unpack
    def test_successfully_add_service(self, has_id_field, has_description):
        """
        POST accepts the service type and name regardless of whether
        an ID field is provided.
        """
        data = {
            'name': self.eeapi.name_key,
            'type': self.eeapi.type_key,
            'id': text_type(uuid.uuid4()),
            'description': 'testing external API'
        }
        if not has_id_field:
            del data['id']
        if not has_description:
            del data['description']

        req = request(self,
                      self.root,
                      self.verb,
                      "/identity/v2.0/services",
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 201)
class TestIdentityOSKSCatalogTenantAdminEndpointTemplatesDelete(SynchronousTestCase, IdentityAuthMixin):
    """
    Tests for ``/identity/v2.0/<tenant-id>/OS-KSCATALOG/endpointTemplates``,
    provided by :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.tenant_id = 'some_tenant'
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(
            self,
            name=self.eeapi_name
        )
        self.template_id = get_template_id(self, self.eeapi)
        self.assertIsNotNone(self.template_id)
        self.uri = (
            "/identity/v2.0/tenants/" + self.tenant_id +
            "/OS-KSCATALOG/endpoints/" + self.template_id
        )
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"DELETE"

    def test_invalid_template_id(self):
        """
        DELETE with an invalid endpoint template id results in 404.
        """
        self.eeapi.remove_template(self.template_id)
        self.core.add_api(self.eeapi)
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertTrue(
            json_body['itemNotFound']['message'].startswith(
                "Unable to locate an External API with the given Template ID."
            )
        )

    def test_template_id_not_enabled_for_tenant(self):
        """
        DELETE for endpoint template not enabled for a tenant or globally
        results in 404.
        """
        self.core.add_api(self.eeapi)
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(
            json_body['itemNotFound']['message'],
            "Template not enabled for tenant"
        )

    def test_disable_template(self):
        """
        DELETE for endpoint template enabled for tenant results in 204.
        """
        self.core.add_api(self.eeapi)
        self.eeapi.enable_endpoint_for_tenant(
            self.tenant_id,
            self.template_id
        )
        eeapi2 = make_example_external_api(
            self,
            name="alternate " + self.eeapi_name
        )
        ept_id2 = get_template_id(self, eeapi2)
        eeapi2.remove_template(ept_id2)
        self.core.add_api(eeapi2)
        req = request(self, self.root, self.verb,
                      self.uri,
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 204)
class TestIdentityOSKSCatalogAdminEndpointTemplatesAdd(SynchronousTestCase,
                                                       IdentityAuthMixin,
                                                       InvalidJsonMixin):
    """
    Tests for ``/identity/v2.0/OS-KSCATALOG/endpointTemplates``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = "/identity/v2.0/OS-KSCATALOG/endpointTemplates"
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(self,
                                               name=self.eeapi_name,
                                               set_enabled=True)
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"POST"

    @ddt.data('name', 'id', 'type', 'region')
    def test_json_body_missing_required_field(self, remove_field):
        """
        POST - required fields must be present otherwise 400 is generated.
        """
        data = {
            'id': text_type(uuid.uuid4()),
            'name': 'some-name',
            'type': 'some-type',
            'region': 'some-region'
        }
        del data[remove_field]
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertTrue(json_body['badRequest']['message'].startswith(
            "JSON body does not contain the required parameters:"))

    def test_invalid_service_id_in_json_body(self):
        """
        POST - Service ID must be valid, otherwise results in 404.
        """
        # Add a second API
        eeapi2 = make_example_external_api(
            self,
            name='d' + self.eeapi_name + text_type(uuid.uuid4()),
            service_type='service-' + text_type(uuid.uuid4()))
        eeapi2.id_key = '0'

        # ensure only one instance of the API has the endpoint template
        eeapi2.remove_template(get_template_id(self, eeapi2))
        self.core.add_api(eeapi2)
        self.core.add_api(self.eeapi)

        data = {
            'id': text_type(uuid.uuid4()),
            'name': 'some-name',
            'type': 'some-type',
            'region': 'some-region'
        }
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(json_body['itemNotFound']['message'],
                         "Service API for endoint template not found")

    def test_existing_endpoint_template(self):
        """
        POST does not overwrite an existing endpoint template, 409 is
        generated instead.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)

        data = {
            'id': id_key,
            'name': self.eeapi_name,
            'type': 'some-type',
            'region': 'some-region'
        }
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(
            json_body['conflict']['message'],
            "ID value is already assigned to an existing template")

    def test_new_endpoint_template_wrong_service_type(self):
        """
        POST requires that the endpoint template and service have the same
        service types.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)

        data = {
            'id': text_type(uuid.uuid4()),
            'name': self.eeapi_name,
            'type': 'some-type',
            'region': 'some-region'
        }
        self.assertNotEqual(id_key, data['id'])
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(
            json_body['conflict']['message'],
            "Endpoint already exists or service type does not match.")

    @ddt.data(True, False)
    def test_new_endpoint_template(self, has_service_header):
        """
        POST to add a endpoint template results in 201, service-id header is
        optional.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)

        eeapi2 = make_example_external_api(
            self,
            name=self.eeapi_name + text_type(uuid.uuid4()),
            service_type='service-' + text_type(uuid.uuid4()))
        eeapi2.remove_template(get_template_id(self, eeapi2))
        self.core.add_api(eeapi2)

        data = {
            'id': text_type(uuid.uuid4()),
            'name': self.eeapi_name,
            'type': self.eeapi.type_key,
            'region': 'some-region'
        }
        self.assertNotEqual(id_key, data['id'])

        if has_service_header:
            self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]

        req = request(self,
                      self.root,
                      self.verb,
                      self.uri,
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 201)
class TestIdentityOSKSCatalogAdminEndpointTemplatesAdd(
        SynchronousTestCase, IdentityAuthMixin, InvalidJsonMixin):
    """
    Tests for ``/identity/v2.0/OS-KSCATALOG/endpointTemplates``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = "/identity/v2.0/OS-KSCATALOG/endpointTemplates"
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(
            self,
            name=self.eeapi_name,
            set_enabled=True
        )
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"POST"

    @ddt.data(
        'name', 'id', 'type', 'region'
    )
    def test_json_body_missing_required_field(self, remove_field):
        """
        POST - required fields must be present otherwise 400 is generated.
        """
        data = {
            'id': text_type(uuid.uuid4()),
            'name': 'some-name',
            'type': 'some-type',
            'region': 'some-region'
        }
        del data[remove_field]
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertTrue(
            json_body['badRequest']['message'].startswith(
                "JSON body does not contain the required parameters:"
            )
        )

    def test_invalid_service_id_in_json_body(self):
        """
        POST - Service ID must be valid, otherwise results in 404.
        """
        # Add a second API
        eeapi2 = make_example_external_api(
            self,
            name='d' + self.eeapi_name + text_type(uuid.uuid4()),
            service_type='service-' + text_type(uuid.uuid4())
        )
        eeapi2.id_key = '0'

        # ensure only one instance of the API has the endpoint template
        eeapi2.remove_template(get_template_id(self, eeapi2))
        self.core.add_api(eeapi2)
        self.core.add_api(self.eeapi)

        data = {
            'id': text_type(uuid.uuid4()),
            'name': 'some-name',
            'type': 'some-type',
            'region': 'some-region'
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(
            json_body['itemNotFound']['message'],
            "Service API for endoint template not found"
        )

    def test_existing_endpoint_template(self):
        """
        POST does not overwrite an existing endpoint template, 409 is
        generated instead.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)

        data = {
            'id': id_key,
            'name': self.eeapi_name,
            'type': 'some-type',
            'region': 'some-region'
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(
            json_body['conflict']['message'],
            "ID value is already assigned to an existing template"
        )

    def test_new_endpoint_template_wrong_service_type(self):
        """
        POST requires that the endpoint template and service have the same
        service types.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)

        data = {
            'id': text_type(uuid.uuid4()),
            'name': self.eeapi_name,
            'type': 'some-type',
            'region': 'some-region'
        }
        self.assertNotEqual(id_key, data['id'])
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(
            json_body['conflict']['message'],
            "Endpoint already exists or service type does not match."
        )

    @ddt.data(
        True, False
    )
    def test_new_endpoint_template(self, has_service_header):
        """
        POST to add a endpoint template results in 201, service-id header is
        optional.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)

        eeapi2 = make_example_external_api(
            self,
            name=self.eeapi_name + text_type(uuid.uuid4()),
            service_type='service-' + text_type(uuid.uuid4())
        )
        eeapi2.remove_template(get_template_id(self, eeapi2))
        self.core.add_api(eeapi2)

        data = {
            'id': text_type(uuid.uuid4()),
            'name': self.eeapi_name,
            'type': self.eeapi.type_key,
            'region': 'some-region'
        }
        self.assertNotEqual(id_key, data['id'])

        if has_service_header:
            self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]

        req = request(self, self.root, self.verb,
                      self.uri,
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 201)
class TestIdentityOSKSCatalogAdminEndpointTemplatesUpdate(
        SynchronousTestCase, IdentityAuthMixin, InvalidJsonMixin):
    """
    Tests for ``/identity/v2.0/OS-KSCATALOG/endpointTemplates``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(self,
                                               name=self.eeapi_name,
                                               set_enabled=True)
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"PUT"
        self.ept_template_id = get_template_id(self, self.eeapi)
        self.uri = ("/identity/v2.0/OS-KSCATALOG/endpointTemplates/" +
                    self.ept_template_id)

    @ddt.data('id', 'name', 'type', 'region')
    def test_json_body_missing_required_field_name(self, remove_field):
        """
        PUT - required fields must be present otherwise 400 is generated.
        """
        data = {
            'id': self.ept_template_id,
            'name': 'some-name',
            'type': 'some-type',
            'region': 'some-region'
        }
        del data[remove_field]

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertTrue(json_body['badRequest']['message'].startswith(
            "JSON body does not contain the required parameters:"))

    def test_invalid_service_id_in_json_body(self):
        """
        PUT requires that the service id map to an existing service,
        otherwise results in a 404.
        """
        data = {
            'id': self.ept_template_id,
            'name': 'some-name',
            'type': 'some-type',
            'region': 'some-region'
        }
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(json_body['itemNotFound']['message'],
                         "Service API for endoint template not found")

    def test_new_endpoint_template_wrong_service_type(self):
        """
        PUT requires that the service matches, otherwise results in 409.
        """
        self.core.add_api(self.eeapi)

        data = {
            'id': self.ept_template_id,
            'name': self.eeapi_name,
            'type': 'some-type',
            'region': 'some-region'
        }
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(
            json_body['conflict']['message'],
            "Endpoint already exists and service id or service type does not "
            "match.")

    def test_json_body_id_value_not_matching_url(self):
        """
        PUT requires that the endpoint template id in the URL and JSON data
        match, otherwise results in 409.
        """
        self.core.add_api(self.eeapi)

        eeapi2 = make_example_external_api(
            self,
            name=self.eeapi_name + text_type(uuid.uuid4()),
            service_type='service-' + text_type(uuid.uuid4()))
        eeapi2.remove_template(get_template_id(self, eeapi2))
        self.core.add_api(eeapi2)

        data = {
            'id': 'some-random-key',
            'name': self.eeapi_name,
            'type': self.eeapi.type_key,
            'region': 'some-region'
        }

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(
            json_body['conflict']['message'],
            "Template ID in URL does not match that of the JSON body")

    def test_invalid_template_id(self):
        """
        PUT requires the endpoint template id to match an existing endpoint
        template, otherwise results in 404.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)
        self.eeapi.remove_template(id_key)

        data = {
            'id': id_key,
            'name': self.eeapi_name,
            'type': self.eeapi.type_key,
            'region': 'some-region'
        }

        self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]

        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(
            json_body['itemNotFound']['message'],
            "Unable to update non-existent template. Template must "
            "first be added before it can be updated.",
        )

    @ddt.data(True, False)
    def test_update_endpoint_template(self, has_service_header):
        """
        PUT to update an endpoint template results in 201, service-id
        header is optional.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)

        eeapi2 = make_example_external_api(
            self,
            name=self.eeapi_name + text_type(uuid.uuid4()),
            service_type='service-' + text_type(uuid.uuid4()))
        eeapi2.remove_template(get_template_id(self, eeapi2))
        self.core.add_api(eeapi2)

        data = {
            'id': id_key,
            'name': self.eeapi_name,
            'type': self.eeapi.type_key,
            'region': 'some-region'
        }

        if has_service_header:
            self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]

        req = request(self,
                      self.root,
                      self.verb,
                      self.uri,
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 201)
class TestIdentityOSKSCatalogAdminEndpointTemplatesList(
        SynchronousTestCase, IdentityAuthMixin, ServiceIdHeaderMixin):
    """
    Tests for ``/identity/v2.0/OS-KSCATALOG/endpointTemplates``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.uri = "/identity/v2.0/OS-KSCATALOG/endpointTemplates"
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(
            self,
            name=self.eeapi_name,
            set_enabled=True
        )
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"GET"

    def test_list_only_internal_apis_available(self):
        """
        GET will return an empty listing when there are no External API
        endpoint templates.
        """
        self.core.add_api(make_example_internal_api(self))
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['OS-KSCATALOG']), 0)
        self.assertEqual(
            len(json_body['OS-KSCATALOG:endpointsTemplates_links']),
            0)

    def test_list_single_template(self):
        """
        GET will return a endpoint template when there are External API
        endpoint templates.
        """
        self.core.add_api(self.eeapi)

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['OS-KSCATALOG']), 1)
        self.assertEqual(
            len(json_body['OS-KSCATALOG:endpointsTemplates_links']),
            0)

    def test_list_single_template_external_and_internal_apis(self):
        """
        GET will only return the External API endpoint templates when they
        are available,
        even if there are also Internal APIs.
        """
        self.core.add_api(self.eeapi)
        self.core.add_api(make_example_internal_api(self))

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)
        self.assertEqual(len(json_body['OS-KSCATALOG']), 1)
        self.assertEqual(
            len(json_body['OS-KSCATALOG:endpointsTemplates_links']),
            0)

    def test_multiple_external_apis(self):
        """
        GET can retrieve numerous External APIs that have External API Templates.
        """
        api_list = [
            make_example_external_api(
                self,
                name=self.eeapi_name + text_type(uuid.uuid4()),
                service_type='service-' + text_type(uuid.uuid4())
            )
            for ignored in range(10)
        ]
        #  eeapi needs to be the first in the list
        api_list.insert(0, self.eeapi)
        for api in api_list:
            self.core.add_api(api)

        self.assertEqual(len(self.core._uuid_to_api_external),
                         len(api_list))

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         headers=self.headers))

        self.assertEqual(response.code, 200)

        self.assertEqual(len(json_body['OS-KSCATALOG']),
                         len(api_list))
        self.assertEqual(
            len(json_body['OS-KSCATALOG:endpointsTemplates_links']),
            0)
class TestIdentityOSKSCatalogAdminEndpointTemplatesUpdate(
        SynchronousTestCase, IdentityAuthMixin, InvalidJsonMixin):
    """
    Tests for ``/identity/v2.0/OS-KSCATALOG/endpointTemplates``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(
            self,
            name=self.eeapi_name,
            set_enabled=True
        )
        self.headers = {
            b'X-Auth-Token': [b'ABCDEF987654321']
        }
        self.verb = b"PUT"
        self.ept_template_id = get_template_id(self, self.eeapi)
        self.uri = (
            "/identity/v2.0/OS-KSCATALOG/endpointTemplates/" +
            self.ept_template_id
        )

    @ddt.data(
        'id', 'name', 'type', 'region'
    )
    def test_json_body_missing_required_field_name(self, remove_field):
        """
        PUT - required fields must be present otherwise 400 is generated.
        """
        data = {
            'id': self.ept_template_id,
            'name': 'some-name',
            'type': 'some-type',
            'region': 'some-region'
        }
        del data[remove_field]

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 400)
        self.assertEqual(json_body['badRequest']['code'], 400)
        self.assertTrue(
            json_body['badRequest']['message'].startswith(
                "JSON body does not contain the required parameters:"
            )
        )

    def test_invalid_service_id_in_json_body(self):
        """
        PUT requires that the service id map to an existing service,
        otherwise results in a 404.
        """
        data = {
            'id': self.ept_template_id,
            'name': 'some-name',
            'type': 'some-type',
            'region': 'some-region'
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(
            json_body['itemNotFound']['message'],
            "Service API for endoint template not found"
        )

    def test_new_endpoint_template_wrong_service_type(self):
        """
        PUT requires that the service matches, otherwise results in 409.
        """
        self.core.add_api(self.eeapi)

        data = {
            'id': self.ept_template_id,
            'name': self.eeapi_name,
            'type': 'some-type',
            'region': 'some-region'
        }
        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(
            json_body['conflict']['message'],
            "Endpoint already exists and service id or service type does not "
            "match."
        )

    def test_json_body_id_value_not_matching_url(self):
        """
        PUT requires that the endpoint template id in the URL and JSON data
        match, otherwise results in 409.
        """
        self.core.add_api(self.eeapi)

        eeapi2 = make_example_external_api(
            self,
            name=self.eeapi_name + text_type(uuid.uuid4()),
            service_type='service-' + text_type(uuid.uuid4())
        )
        eeapi2.remove_template(get_template_id(self, eeapi2))
        self.core.add_api(eeapi2)

        data = {
            'id': 'some-random-key',
            'name': self.eeapi_name,
            'type': self.eeapi.type_key,
            'region': 'some-region'
        }

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(
            json_body['conflict']['message'],
            "Template ID in URL does not match that of the JSON body"
        )

    def test_invalid_template_id(self):
        """
        PUT requires the endpoint template id to match an existing endpoint
        template, otherwise results in 404.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)
        self.eeapi.remove_template(id_key)

        data = {
            'id': id_key,
            'name': self.eeapi_name,
            'type': self.eeapi.type_key,
            'region': 'some-region'
        }

        self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]

        (response, json_body) = self.successResultOf(
            json_request(self, self.root, self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(
            json_body['itemNotFound']['message'],
            "Unable to update non-existent template. Template must "
            "first be added before it can be updated.",
        )

    @ddt.data(
        True, False
    )
    def test_update_endpoint_template(self, has_service_header):
        """
        PUT to update an endpoint template results in 201, service-id
        header is optional.
        """
        self.core.add_api(self.eeapi)
        id_key = get_template_id(self, self.eeapi)

        eeapi2 = make_example_external_api(
            self,
            name=self.eeapi_name + text_type(uuid.uuid4()),
            service_type='service-' + text_type(uuid.uuid4())
        )
        eeapi2.remove_template(get_template_id(self, eeapi2))
        self.core.add_api(eeapi2)

        data = {
            'id': id_key,
            'name': self.eeapi_name,
            'type': self.eeapi.type_key,
            'region': 'some-region'
        }

        if has_service_header:
            self.headers[b'serviceid'] = [self.eeapi.uuid_key.encode('utf8')]

        req = request(self, self.root, self.verb,
                      self.uri,
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 201)
class TestIdentityMimicOSKSCatalogAdminDeleteExternalService(
        SynchronousTestCase, IdentityAuthMixin):
    """
    Tests for ``/identity/v2.0/services/<service-id>``, provided by
    :obj:`mimic.rest.idenity_api.IdentityApi`
    """
    def setUp(self):
        self.core = MimicCore(Clock(), [])
        self.root = MimicRoot(self.core).app.resource()
        self.eeapi_id = u"some-id"
        self.uri = "/identity/v2.0/services/" + self.eeapi_id
        self.eeapi_name = u"externalServiceName"
        self.eeapi = make_example_external_api(self,
                                               name=self.eeapi_name,
                                               set_enabled=True)
        self.eeapi2 = make_example_external_api(self,
                                                name=self.eeapi_name +
                                                " alternate")
        self.eeapi.uuid_key = self.eeapi_id
        self.headers = {b'X-Auth-Token': [b'ABCDEF987654321']}
        self.verb = b"DELETE"

    def test_invalid_service(self):
        """
        DELETE an unknown service will generate a 404.
        """
        data = {'name': 'some-name', 'type': 'some-type', 'id': 'some-id'}
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 404)
        self.assertEqual(json_body['itemNotFound']['code'], 404)
        self.assertEqual(json_body['itemNotFound']['message'],
                         "Service not found. Unable to remove.")

    def test_service_has_template(self):
        """
        DELETE a service that still has a template results in 409.
        """
        self.core.add_api(self.eeapi)
        data = {
            'name': self.eeapi.name_key,
            'type': self.eeapi.type_key,
            'id': self.eeapi.uuid_key
        }
        (response, json_body) = self.successResultOf(
            json_request(self,
                         self.root,
                         self.verb,
                         self.uri,
                         body=data,
                         headers=self.headers))

        self.assertEqual(response.code, 409)
        self.assertEqual(json_body['conflict']['code'], 409)
        self.assertEqual(json_body['conflict']['message'],
                         "Service still has endpoint templates.")

    def test_remove_service(self):
        """
        DELETE a service.
        """
        templates_to_remove = list(self.eeapi.endpoint_templates.keys())
        for template_id in templates_to_remove:
            self.eeapi.remove_template(template_id)

        self.core.add_api(self.eeapi)
        self.core.add_api(self.eeapi2)
        data = {
            'name': self.eeapi.name_key,
            'type': self.eeapi.type_key,
            'id': self.eeapi.uuid_key
        }

        req = request(self,
                      self.root,
                      self.verb,
                      self.uri,
                      body=json.dumps(data).encode("utf-8"),
                      headers=self.headers)

        response = self.successResultOf(req)
        self.assertEqual(response.code, 204)