diff --git a/src/openeo_aggregator/backend.py b/src/openeo_aggregator/backend.py index 0fcbcf3b..fdc1a5a0 100644 --- a/src/openeo_aggregator/backend.py +++ b/src/openeo_aggregator/backend.py @@ -854,7 +854,6 @@ def create_service(self, user_id: str, process_graph: dict, service_type: str, a configuration: dict) -> str: """ https://openeo.org/documentation/1.0/developers/api/reference.html#operation/create-service - :return: (location, openeo_identifier) """ backend_id = self._processing.get_backend_for_process_graph( @@ -864,7 +863,10 @@ def create_service(self, user_id: str, process_graph: dict, service_type: str, a con = self._backends.get_connection(backend_id) try: + # create_service can raise ServiceUnsupportedException and OpenEOApiException. service = con.create_service(graph=process_graph, type=service_type) + + # TODO: This exception handling was copy-pasted. What do we actually need here? except OpenEoApiError as e: for exc_class in [ProcessGraphMissingException, ProcessGraphInvalidException]: if e.code == exc_class.code: diff --git a/tests/conftest.py b/tests/conftest.py index 5ce45fee..04aaf2b8 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -136,6 +136,16 @@ def backend_implementation(flask_app) -> AggregatorBackendImplementation: return flask_app.config["OPENEO_BACKEND_IMPLEMENTATION"] +@pytest.fixture(params=["0.4.0", "1.0.0"]) +def api_version(request): + """To go through all relevant API versions""" + return request.param + + +def get_api_version(flask_app, api_version) -> ApiTester: + return ApiTester(api_version=api_version, client=flask_app.test_client()) + + def get_api040(flask_app: flask.Flask) -> ApiTester: return ApiTester(api_version="0.4.0", client=flask_app.test_client()) @@ -144,6 +154,15 @@ def get_api100(flask_app: flask.Flask) -> ApiTester: return ApiTester(api_version="1.0.0", client=flask_app.test_client()) +@pytest.fixture +def api(flask_app, api_version) -> ApiTester: + """Get an ApiTester for each version. + + Useful when it easy to test several API versions with (mostly) the same test code. + But when the difference is too big, just write separate tests. + """ + return get_api_version(flask_app, api_version) + @pytest.fixture def api040(flask_app: flask.Flask) -> ApiTester: return get_api040(flask_app) diff --git a/tests/test_backend.py b/tests/test_backend.py index abf4ddea..e1811648 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -142,10 +142,10 @@ def test_service_types_simple(self, multi_backend_connection, config, backend1, } requests_mock.get(backend1 + "/service_types", json=single_service_type) requests_mock.get(backend2 + "/service_types", json=single_service_type) - implementation = AggregatorBackendImplementation( + abe_implementation = AggregatorBackendImplementation( backends=multi_backend_connection, config=config ) - service_types = implementation.service_types() + service_types = abe_implementation.service_types() assert service_types == single_service_type def test_service_types_merging(self, multi_backend_connection, config, backend1, backend2, requests_mock): @@ -180,13 +180,15 @@ def test_service_types_merging(self, multi_backend_connection, config, backend1, } requests_mock.get(backend1 + "/service_types", json=service_1) requests_mock.get(backend2 + "/service_types", json=service_2) - implementation = AggregatorBackendImplementation( + abe_implementation = AggregatorBackendImplementation( backends=multi_backend_connection, config=config ) - service_types = implementation.service_types() - expected = dict(service_1) - expected.update(service_2) - assert service_types == expected + + actual_service_types = abe_implementation.service_types() + + expected_service_types = dict(service_1) + expected_service_types.update(service_2) + assert actual_service_types == expected_service_types # TODO: eliminate TEST_SERVICES (too long) when I find a better way to set up the test. TEST_SERVICES = { @@ -531,9 +533,9 @@ def test_list_services_simple(self, multi_backend_connection, config, backend1, services2 = {} requests_mock.get(backend1 + "/services", json=services1) requests_mock.get(backend2 + "/services", json=services2) - implementation = AggregatorBackendImplementation(backends=multi_backend_connection, config=config) + abe_implementation = AggregatorBackendImplementation(backends=multi_backend_connection, config=config) - actual_services = implementation.list_services(user_id=TEST_USER) + actual_services = abe_implementation.list_services(user_id=TEST_USER) # Construct expected result. We have get just data from the service in services1 # (there is only one) for conversion to a ServiceMetadata. @@ -561,9 +563,9 @@ def test_list_services_merged(self, multi_backend_connection, config, backend1, services2 = {"services": [serv_metadata_wmts_foo.prepare_for_json()], "links": []} requests_mock.get(backend1 + "/services", json=services1) requests_mock.get(backend2 + "/services", json=services2) - implementation = AggregatorBackendImplementation(backends=multi_backend_connection, config=config) + abe_implementation = AggregatorBackendImplementation(backends=multi_backend_connection, config=config) - actual_services = implementation.list_services(user_id=TEST_USER) + actual_services = abe_implementation.list_services(user_id=TEST_USER) # Construct expected result. We have get just data from the service in # services1 (there is only one) for conversion to a ServiceMetadata. @@ -607,11 +609,11 @@ def test_list_services_merged_multiple(self, multi_backend_connection, config, b } requests_mock.get(backend1 + "/services", json=services1) requests_mock.get(backend2 + "/services", json=services2) - implementation = AggregatorBackendImplementation( + abe_implementation = AggregatorBackendImplementation( backends=multi_backend_connection, config=config ) - actual_services = implementation.list_services(user_id=TEST_USER) + actual_services = abe_implementation.list_services(user_id=TEST_USER) # Construct expected result. We have get just data from the service in # services1 (there is only one) for conversion to a ServiceMetadata. @@ -651,14 +653,14 @@ def test_service_info(self, multi_backend_connection, config, backend1, backend2 ) requests_mock.get(backend1 + "/services/wmts-foo", json=service1.prepare_for_json()) requests_mock.get(backend2 + "/services/wms-bar", json=service2.prepare_for_json()) - implementation = AggregatorBackendImplementation( + abe_implementation = AggregatorBackendImplementation( backends=multi_backend_connection, config=config ) - actual_service1 = implementation.service_info(user_id=TEST_USER, service_id="wmts-foo") + actual_service1 = abe_implementation.service_info(user_id=TEST_USER, service_id="wmts-foo") assert actual_service1 == service1 - actual_service2 = implementation.service_info(user_id=TEST_USER, service_id="wms-bar") + actual_service2 = abe_implementation.service_info(user_id=TEST_USER, service_id="wms-bar") assert actual_service2 == service2 def test_service_info_wrong_id(self, multi_backend_connection, config, backend1, backend2, requests_mock): @@ -688,12 +690,12 @@ def test_service_info_wrong_id(self, multi_backend_connection, config, backend1, ) requests_mock.get(backend1 + "/services/wmts-foo", json=service1.prepare_for_json()) requests_mock.get(backend2 + "/services/wms-bar", json=service2.prepare_for_json()) - implementation = AggregatorBackendImplementation( + abe_implementation = AggregatorBackendImplementation( backends=multi_backend_connection, config=config ) with pytest.raises(ServiceNotFoundException): - _ = implementation.service_info(user_id=TEST_USER, service_id="doesnotexist") + _ = abe_implementation.service_info(user_id=TEST_USER, service_id="doesnotexist") @pytest.mark.parametrize("api_version", ["0.4.0", "1.0.0", "1.1.0"]) def test_create_service(self, multi_backend_connection, config, backend1, requests_mock, api_version): @@ -711,9 +713,9 @@ def test_create_service(self, multi_backend_connection, config, backend1, reques }, status_code=201) - implementation = AggregatorBackendImplementation(backends=multi_backend_connection, config=config) + abe_implementation = AggregatorBackendImplementation(backends=multi_backend_connection, config=config) - actual_openeo_id = implementation.create_service( + actual_openeo_id = abe_implementation.create_service( user_id=TEST_USER, process_graph=process_graph, service_type="WMTS", @@ -723,6 +725,61 @@ def test_create_service(self, multi_backend_connection, config, backend1, reques assert actual_openeo_id == expected_openeo_id + # TODO: test a failing case for test_create_service + @pytest.mark.parametrize("api_version", ["0.4.0", "1.0.0", "1.1.0"]) + def test_create_service_backend_returns_error( + self, multi_backend_connection, config, backend1, requests_mock, api_version + ): + from openeo.rest import OpenEoApiError, OpenEoRestError + # TODO: Two exception types should be re-raised: ProcessGraphMissingException, ProcessGraphInvalidException + + for exc_class in [OpenEoApiError, OpenEoRestError]: + # Set up responses for creating the service in backend 1 + process_graph = {"foo": {"process_id": "foo", "arguments": {}}} + requests_mock.post( + backend1 + "/services", + exc=exc_class("Some server error"), + ) + + abe_implementation = AggregatorBackendImplementation(backends=multi_backend_connection, config=config) + + with pytest.raises(OpenEOApiException): + _ = abe_implementation.create_service( + user_id=TEST_USER, + process_graph=process_graph, + service_type="WMTS", + api_version=api_version, + configuration={} + ) + + @pytest.mark.parametrize("api_version", ["0.4.0", "1.0.0", "1.1.0"]) + def test_create_service_backend_reraises( + self, multi_backend_connection, config, backend1, requests_mock, api_version + ): + from openeo_driver.errors import ProcessGraphMissingException, ProcessGraphInvalidException, ServiceUnsupportedException + + for exc_class in [ProcessGraphMissingException, + ProcessGraphInvalidException, + ServiceUnsupportedException]: + # Set up responses for creating the service in backend 1 + process_graph = {"foo": {"process_id": "foo", "arguments": {}}} + requests_mock.post( + backend1 + "/services", + exc=exc_class("Some server error"), + ) + + abe_implementation = AggregatorBackendImplementation(backends=multi_backend_connection, config=config) + + # These exception types should be re-raised, not become an OpenEOApiException. + with pytest.raises(exc_class): + _ = abe_implementation.create_service( + user_id=TEST_USER, + process_graph=process_graph, + service_type="WMTS", + api_version=api_version, + configuration={} + ) + class TestInternalCollectionMetadata: diff --git a/tests/test_views.py b/tests/test_views.py index ce654d97..eb343a31 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1182,10 +1182,79 @@ def post_jobs(request: requests.Request, context): }}} ] + class TestSecondaryServices: # TODO: add view tests for list service types, list_services, servicfe_info + def test_service_types_simple(self, api, backend1, backend2, requests_mock): + single_service_type = { + "WMTS": { + "configuration": { + "colormap": { + "default": "YlGn", + "description": + "The colormap to apply to single band layers", + "type": "string" + }, + "version": { + "default": "1.0.0", + "description": "The WMTS version to use.", + "enum": ["1.0.0"], + "type": "string" + } + }, + "links": [], + "process_parameters": [], + "title": "Web Map Tile Service" + } + } + requests_mock.get(backend1 + "/service_types", json=single_service_type) + requests_mock.get(backend2 + "/service_types", json=single_service_type) + + resp = api.get('/service_types').assert_status_code(200) + assert resp.json == single_service_type + + def test_service_types_merging(self, api, backend1, backend2, requests_mock): + service_type_1 = { + "WMTS": { + "configuration": { + "colormap": { + "default": "YlGn", + "description": + "The colormap to apply to single band layers", + "type": "string" + }, + "version": { + "default": "1.0.0", + "description": "The WMTS version to use.", + "enum": ["1.0.0"], + "type": "string" + } + }, + "links": [], + "process_parameters": [], + "title": "Web Map Tile Service" + } + } + service_type_2 = { + "WMS": { + "title": "OGC Web Map Service", + "configuration": {}, + "process_parameters": [], + "links": [] + } + } + requests_mock.get(backend1 + "/service_types", json=service_type_1) + requests_mock.get(backend2 + "/service_types", json=service_type_2) + + resp = api.get("/service_types").assert_status_code(200) + actual_service_types = resp.json + + expected_service_types = dict(service_type_1) + expected_service_types.update(service_type_2) + assert actual_service_types == expected_service_types + def test_create_wmts_040(self, api040, requests_mock, backend1): api040.set_auth_bearer_token(TEST_USER_BEARER_TOKEN)