diff --git a/Makefile b/Makefile index 7383feb16..cc754aa04 100644 --- a/Makefile +++ b/Makefile @@ -97,4 +97,4 @@ acceptance-tests: build-acceptance .PHONY: stop-acceptance-tests stop-acceptance-tests: build-acceptance - (cd compose/ && TAG=$(TAG) GS_USER=$(UID):$(GID) docker compose $(COMPOSE_ACCEPTANCE_DATADIR_OPTIONS) down -v) \ No newline at end of file + (cd compose/ && TAG=$(TAG) GS_USER=$(UID):$(GID) docker compose $(COMPOSE_ACCEPTANCE_DATADIR_OPTIONS) down -v) diff --git a/acceptance_tests/pyproject.toml b/acceptance_tests/pyproject.toml index e1a076859..956567cfa 100644 --- a/acceptance_tests/pyproject.toml +++ b/acceptance_tests/pyproject.toml @@ -9,7 +9,9 @@ packages = [{ include = "tests" }] [tool.poetry.dependencies] python = "^3.10" pytest = "^8.3.3" +psycopg2-binary = "^2.9.9" geoservercloud = "^0.2.5" +sqlalchemy = "^2.0.35" [build-system] diff --git a/acceptance_tests/tests/conftest.py b/acceptance_tests/tests/conftest.py index 02aefbbf5..e450c66ca 100644 --- a/acceptance_tests/tests/conftest.py +++ b/acceptance_tests/tests/conftest.py @@ -1,12 +1,60 @@ import os +from pathlib import Path import pytest +import sqlalchemy from geoservercloud import GeoServerCloud - GEOSERVER_URL = os.getenv("GEOSERVER_URL", "http://gateway:8080/geoserver/cloud") +RESOURCE_DIR = Path(__file__).parent / "resources" +# Database connection +PGHOST = "geodatabase" +PGPORT = 5432 +PGDATABASE = "geodata" +PGUSER = "geodata" +PGPASSWORD = "geodata" +PGSCHEMA = "test1" +WORKSPACE = "test_workspace" +DATASTORE = "test_datastore" + + +@pytest.fixture(scope="session", autouse=True) +def engine(): + yield sqlalchemy.create_engine( + f"postgresql://{PGUSER}:{PGPASSWORD}@{PGHOST}:{PGPORT}/{PGDATABASE}", + ) + + +@pytest.fixture(scope="session", autouse=True) +def db_session(engine): + with engine.connect() as connection: + connection.execute( + sqlalchemy.sql.text(f"CREATE SCHEMA IF NOT EXISTS {PGSCHEMA}") + ) + connection.execute(sqlalchemy.sql.text(f"SET SEARCH_PATH = {PGSCHEMA}")) + connection.commit() + yield connection + connection.execute( + sqlalchemy.sql.text(f"DROP SCHEMA IF EXISTS {PGSCHEMA} CASCADE") + ) + connection.commit() @pytest.fixture(scope="module") def geoserver(): - yield GeoServerCloud(GEOSERVER_URL) + geoserver = GeoServerCloud(GEOSERVER_URL) + geoserver.recreate_workspace(WORKSPACE, set_default_workspace=True) + geoserver.create_pg_datastore( + workspace=WORKSPACE, + datastore=DATASTORE, + pg_host=PGHOST, + pg_port=PGPORT, + pg_db=PGDATABASE, + pg_user=PGUSER, + pg_password=PGPASSWORD, + pg_schema=PGSCHEMA, + set_default_datastore=True, + ) + geoserver.publish_workspace(WORKSPACE) + yield geoserver + geoserver.delete_workspace(WORKSPACE) diff --git a/acceptance_tests/tests/lib/utils.py b/acceptance_tests/tests/lib/utils.py new file mode 100644 index 000000000..7212866d5 --- /dev/null +++ b/acceptance_tests/tests/lib/utils.py @@ -0,0 +1,15 @@ +from pathlib import Path + + +def write_actual_image(response, tag): + file = Path(f"/tmp/{tag}_actual.png") + file.parent.mkdir(parents=True, exist_ok=True) + with open(file, "wb") as fs: + fs.write(response.read()) + + +def compare_images(dir, tag): + actual = f"/tmp/{tag}_actual.png" + expected = f"{dir}/{tag}_expected.png" + with open(actual, "rb") as fs1, open(expected, "rb") as fs2: + assert fs1.read() == fs2.read() diff --git a/acceptance_tests/tests/resources/getmap_expected.png b/acceptance_tests/tests/resources/getmap_expected.png new file mode 100644 index 000000000..c93b6740a Binary files /dev/null and b/acceptance_tests/tests/resources/getmap_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/default_locale/default_value/language_None_expected.png b/acceptance_tests/tests/resources/labels/default_locale/default_value/language_None_expected.png new file mode 100644 index 000000000..3c3b1829f Binary files /dev/null and b/acceptance_tests/tests/resources/labels/default_locale/default_value/language_None_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/default_locale/default_value/language__expected.png b/acceptance_tests/tests/resources/labels/default_locale/default_value/language__expected.png new file mode 100644 index 000000000..3272ee184 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/default_locale/default_value/language__expected.png differ diff --git a/acceptance_tests/tests/resources/labels/default_locale/default_value/language_de_expected.png b/acceptance_tests/tests/resources/labels/default_locale/default_value/language_de_expected.png new file mode 100644 index 000000000..eb3c68630 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/default_locale/default_value/language_de_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/default_locale/default_value/language_fr_expected.png b/acceptance_tests/tests/resources/labels/default_locale/default_value/language_fr_expected.png new file mode 100644 index 000000000..3c3b1829f Binary files /dev/null and b/acceptance_tests/tests/resources/labels/default_locale/default_value/language_fr_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/default_locale/default_value/language_it_expected.png b/acceptance_tests/tests/resources/labels/default_locale/default_value/language_it_expected.png new file mode 100644 index 000000000..84cc65658 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/default_locale/default_value/language_it_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/default_locale/no_default_value/language_None_expected.png b/acceptance_tests/tests/resources/labels/default_locale/no_default_value/language_None_expected.png new file mode 100644 index 000000000..3c3b1829f Binary files /dev/null and b/acceptance_tests/tests/resources/labels/default_locale/no_default_value/language_None_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/default_locale/no_default_value/language__expected.png b/acceptance_tests/tests/resources/labels/default_locale/no_default_value/language__expected.png new file mode 100644 index 000000000..84cc65658 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/default_locale/no_default_value/language__expected.png differ diff --git a/acceptance_tests/tests/resources/labels/default_locale/no_default_value/language_it_expected.png b/acceptance_tests/tests/resources/labels/default_locale/no_default_value/language_it_expected.png new file mode 100644 index 000000000..84cc65658 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/default_locale/no_default_value/language_it_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_None_expected.png b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_None_expected.png new file mode 100644 index 000000000..3272ee184 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_None_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language__expected.png b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language__expected.png new file mode 100644 index 000000000..3272ee184 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language__expected.png differ diff --git a/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_de_expected.png b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_de_expected.png new file mode 100644 index 000000000..eb3c68630 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_de_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_fr_expected.png b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_fr_expected.png new file mode 100644 index 000000000..3c3b1829f Binary files /dev/null and b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_fr_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_it_expected.png b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_it_expected.png new file mode 100644 index 000000000..84cc65658 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/no_default_locale/default_value/language_it_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/no_default_locale/no_default_value/language_None_expected.png b/acceptance_tests/tests/resources/labels/no_default_locale/no_default_value/language_None_expected.png new file mode 100644 index 000000000..84cc65658 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/no_default_locale/no_default_value/language_None_expected.png differ diff --git a/acceptance_tests/tests/resources/labels/no_default_locale/no_default_value/language__expected.png b/acceptance_tests/tests/resources/labels/no_default_locale/no_default_value/language__expected.png new file mode 100644 index 000000000..84cc65658 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/no_default_locale/no_default_value/language__expected.png differ diff --git a/acceptance_tests/tests/resources/labels/no_default_locale/no_default_value/language_it_expected.png b/acceptance_tests/tests/resources/labels/no_default_locale/no_default_value/language_it_expected.png new file mode 100644 index 000000000..84cc65658 Binary files /dev/null and b/acceptance_tests/tests/resources/labels/no_default_locale/no_default_value/language_it_expected.png differ diff --git a/acceptance_tests/tests/resources/localized_labels.sld b/acceptance_tests/tests/resources/localized_labels.sld new file mode 100644 index 000000000..1dd2e011b --- /dev/null +++ b/acceptance_tests/tests/resources/localized_labels.sld @@ -0,0 +1,36 @@ + + + + localized_labels + + localized_labels + + + Localized labels + + + + #000000 + + + + + + + diff --git a/acceptance_tests/tests/resources/localized_no_default.sld b/acceptance_tests/tests/resources/localized_no_default.sld new file mode 100644 index 000000000..bc1d5a8ad --- /dev/null +++ b/acceptance_tests/tests/resources/localized_no_default.sld @@ -0,0 +1,37 @@ + + + + localized_style + + localized_no_default + + + Localized, no default value + + <Localized lang="en">English</Localized> + <Localized lang="de">Deutsch</Localized> + <Localized lang="fr">Français</Localized> + <Localized lang="it">Italiano</Localized> + + + + + circle + + #FF0000 + + + 6 + + + + + + + diff --git a/acceptance_tests/tests/resources/localized_with_default.sld b/acceptance_tests/tests/resources/localized_with_default.sld new file mode 100644 index 000000000..316966c6b --- /dev/null +++ b/acceptance_tests/tests/resources/localized_with_default.sld @@ -0,0 +1,37 @@ + + + + localized_style + + localized_with_default + + + Localized, with default value + + Default label<Localized lang="en">English</Localized> + <Localized lang="de">Deutsch</Localized> + <Localized lang="fr">Français</Localized> + <Localized lang="it">Italiano</Localized> + + + + + circle + + #FF0000 + + + 6 + + + + + + + diff --git a/acceptance_tests/tests/resources/wfs_payload.xml b/acceptance_tests/tests/resources/wfs_payload.xml new file mode 100644 index 000000000..eefd14e03 --- /dev/null +++ b/acceptance_tests/tests/resources/wfs_payload.xml @@ -0,0 +1,16 @@ + + + + + + 2600000.0 1200000.0 + + + 2024-05-13T08:14:48.763Z + 10 + Title + + + \ No newline at end of file diff --git a/acceptance_tests/tests/test_datastore.py b/acceptance_tests/tests/test_datastore.py new file mode 100644 index 000000000..b44d8212c --- /dev/null +++ b/acceptance_tests/tests/test_datastore.py @@ -0,0 +1,24 @@ +from conftest import PGHOST, PGPORT, PGDATABASE, PGUSER, PGPASSWORD, PGSCHEMA + + +def test_create_get_and_delete_datastore(geoserver): + workspace = datastore = "test_create_pg_datastore" + geoserver.create_workspace(workspace) + response = geoserver.create_pg_datastore( + workspace=workspace, + datastore=datastore, + pg_host=PGHOST, + pg_port=PGPORT, + pg_db=PGDATABASE, + pg_user=PGUSER, + pg_password=PGPASSWORD, + pg_schema=PGSCHEMA, + set_default_datastore=True, + ) + assert response.status_code == 201 + response = geoserver.get_request( + f"/rest/workspaces/{workspace}/datastores/{datastore}.json" + ) + assert response.status_code == 200 + response = geoserver.delete_workspace(workspace) + assert response.status_code == 200 diff --git a/acceptance_tests/tests/test_gwc.py b/acceptance_tests/tests/test_gwc.py new file mode 100644 index 000000000..d5af14f84 --- /dev/null +++ b/acceptance_tests/tests/test_gwc.py @@ -0,0 +1,47 @@ +import pytest + +WORKSPACE = "test_gwc" +WMTS_STORE = "test_gwc_store" +WMTS_LAYER = "ch.swisstopo.swissimage" + + +@pytest.fixture(scope="module") +def geoserver_with_gwc_layers(geoserver): + geoserver.create_workspace(WORKSPACE) + geoserver.create_wmts_store( + WORKSPACE, + WMTS_STORE, + capabilities="https://wmts.geo.admin.ch/EPSG/4326/1.0.0/WMTSCapabilities.xml", + ) + geoserver.create_wmts_layer(WORKSPACE, WMTS_STORE, WMTS_LAYER) + geoserver.get_request( + f"/rest/workspaces/{WORKSPACE}/wmtsstores/{WMTS_STORE}/layers/{WMTS_LAYER}.json" + ) + response = geoserver.publish_gwc_layer(WORKSPACE, WMTS_LAYER) + assert response.status_code == 200 + yield geoserver + geoserver.delete_workspace(WORKSPACE) + + +def test_tile_cache(geoserver_with_gwc_layers): + + response = geoserver_with_gwc_layers.get_tile( + format="image/png", + layer=f"{WORKSPACE}:{WMTS_LAYER}", + tile_matrix_set="EPSG:4326", + tile_matrix="EPSG:4326:9", + row=122, + column=534, + ) + assert response.info().get("Content-Type") == "image/png" + assert response.info().get("Geowebcache-Cache-Result") == "MISS" + + response = geoserver_with_gwc_layers.get_tile( + format="image/png", + layer=f"{WORKSPACE}:{WMTS_LAYER}", + tile_matrix_set="EPSG:4326", + tile_matrix="EPSG:4326:9", + row=122, + column=534, + ) + assert response.info().get("Geowebcache-Cache-Result") == "HIT" diff --git a/acceptance_tests/tests/test_i18n.py b/acceptance_tests/tests/test_i18n.py new file mode 100755 index 000000000..81640659e --- /dev/null +++ b/acceptance_tests/tests/test_i18n.py @@ -0,0 +1,417 @@ +#!/bin/env python + +import pytest +from conftest import RESOURCE_DIR, WORKSPACE +from lib.utils import compare_images, write_actual_image +from requests.exceptions import JSONDecodeError +from sqlalchemy.sql import text + + +def international_title(default=True, de=True, fr=True, it=True, rm=True): + title = {} + if default: + title["default"] = "Default title" + if de: + title["de"] = "Punkte" + if fr: + title["fr"] = "Points" + if it: + title["it"] = "Punti" + if rm: + title["rm"] = "Puncts" + return title + + +def assert_legend(geoserver, style, language, expected_label): + response = geoserver.get_legend_graphic( + "i18n_legend", + format="application/json", + language=language, + style=style, + workspace=WORKSPACE, + ) + try: + label = response.json()["Legend"][0]["rules"][0]["title"] + assert label == expected_label + except (KeyError, JSONDecodeError): + print(f"Invalid response for language '{language}:'\n{response.content}") + assert False + + +@pytest.fixture(scope="module") +def geoserver_with_i18n_layers(geoserver): + + # Create feature type with all languages + layer1 = "layer_all_languages" + title1 = international_title(default=True, de=True, fr=True, it=True, rm=True) + geoserver.create_feature_type(layer1, title=title1, epsg=2056) + + # Create feature type without Rumantsch + layer2 = "layer_no_rumantsch" + title2 = international_title(default=True, de=True, fr=True, it=True, rm=False) + geoserver.create_feature_type(layer2, title=title2, epsg=2056) + + # Create feature type without default language nor Rumantsch + layer3 = "layer_no_default_no_rumantsch" + title3 = international_title(default=False, de=True, fr=True, it=True, rm=False) + geoserver.create_feature_type(layer3, title=title3, epsg=2056) + + yield geoserver + + +@pytest.fixture(scope="module") +def geoserver_default_locale_it(geoserver_with_i18n_layers): + geoserver_with_i18n_layers.set_default_locale_for_service(WORKSPACE, "it") + yield geoserver_with_i18n_layers + geoserver_with_i18n_layers.unset_default_locale_for_service(WORKSPACE) + + +@pytest.fixture(scope="module") +def geoserver_i18n_legend_layer(geoserver): + geoserver.create_workspace(WORKSPACE, set_default_workspace=True) + geoserver.create_feature_type("i18n_legend", epsg=2056) + geoserver.create_style_from_file( + "localized_with_default.sld", + f"{RESOURCE_DIR}/localized_with_default.sld", + workspace=WORKSPACE, + ) + geoserver.create_style_from_file( + "localized_no_default.sld", + f"{RESOURCE_DIR}/localized_no_default.sld", + workspace=WORKSPACE, + ) + yield geoserver + + +@pytest.fixture(scope="function") +def geoserver_i18n_legend_layer_and_default_locale_it(geoserver_i18n_legend_layer): + geoserver_i18n_legend_layer.set_default_locale_for_service(WORKSPACE, "it") + yield geoserver_i18n_legend_layer + geoserver_i18n_legend_layer.unset_default_locale_for_service(WORKSPACE) + + +@pytest.fixture(scope="module") +def geoserver_i18n_label_layer(geoserver, db_session): + feature_type = "i18n_labels" + style = "localized_labels" + file = f"{RESOURCE_DIR}/{style}.sld" + attributes = { + "geom": {"type": "Point", "required": True}, + "label_default": {"type": "string", "required": False}, + "label_de": {"type": "string", "required": False}, + "label_fr": {"type": "string", "required": False}, + } + geoserver.create_feature_type(feature_type, attributes=attributes, epsg=2056) + geoserver.create_style_from_file(style, file, workspace=WORKSPACE) + # Feature with labels in German, French and a default value + db_session.execute( + text( + f"INSERT INTO {feature_type} (geom, label_default, label_de, label_fr) VALUES " + "(public.ST_SetSRID(public.ST_MakePoint(2600000, 1200000), 2056), 'Default label', 'Deutsches Label', 'Étiquette française')" + ) + ) + # Feature with labels in German, French and no default value + db_session.execute( + text( + f"INSERT INTO {feature_type} (geom, label_de, label_fr) VALUES " + "(public.ST_SetSRID(public.ST_MakePoint(2700000, 1300000), 2056), 'Deutsches Label', 'Étiquette française')" + ) + ) + db_session.commit() + yield geoserver + + +@pytest.fixture(scope="module") +def geoserver_i18n_label_default_locale_fr(geoserver_i18n_label_layer): + geoserver_i18n_label_layer.set_default_locale_for_service(WORKSPACE, "fr") + yield geoserver_i18n_label_layer + geoserver_i18n_label_layer.unset_default_locale_for_service(WORKSPACE) + + +@pytest.mark.parametrize( + "language,expected_titles", + [ + ( + "de", + { + "layer_all_languages": "Punkte", + "layer_no_rumantsch": "Punkte", + "layer_no_default_no_rumantsch": "Punkte", + }, + ), + ( + "de,fr", + { + "layer_all_languages": "Punkte", + "layer_no_rumantsch": "Punkte", + "layer_no_default_no_rumantsch": "Punkte", + }, + ), + ( + "fr,de", + { + "layer_all_languages": "Points", + "layer_no_rumantsch": "Points", + "layer_no_default_no_rumantsch": "Points", + }, + ), + ( + "rm", + { + "layer_all_languages": "Puncts", + "layer_no_rumantsch": "Default title", + "layer_no_default_no_rumantsch": "DID NOT FIND i18n CONTENT FOR THIS ELEMENT", + }, + ), + ( + "en", + {}, + ), + ( + None, + { + "layer_all_languages": "Default title", + "layer_no_rumantsch": "Default title", + "layer_no_default_no_rumantsch": "Punkte", + }, + ), + ( + "foobar", + {}, + ), + ], +) +def test_i18n_layers(geoserver_with_i18n_layers, language, expected_titles): + capabilities = geoserver_with_i18n_layers.get_wms_layers(WORKSPACE, language) + layers = capabilities.get("Layer") + if type(layers) is list: + for expected_layer, expected_title in expected_titles.items(): + actual_layer = next( + (layer for layer in layers if layer["Name"] == expected_layer), {} + ) + assert actual_layer.get("Title") == expected_title + else: + print(capabilities) + assert expected_titles == {} + assert "ServiceExceptionReport" in capabilities + + +@pytest.mark.parametrize( + "language,expected_titles", + [ + ( + "de", + { + "layer_all_languages": "Punkte", + "layer_no_rumantsch": "Punkte", + "layer_no_default_no_rumantsch": "Punkte", + }, + ), + ( + "rm", + { + "layer_all_languages": "Puncts", + "layer_no_rumantsch": "Default title", + "layer_no_default_no_rumantsch": "DID NOT FIND i18n CONTENT FOR THIS ELEMENT", + }, + ), + ( + "en", + {}, + ), + ( + None, + { + "layer_all_languages": "Punti", + "layer_no_rumantsch": "Punti", + "layer_no_default_no_rumantsch": "Punti", + }, + ), + ], +) +@pytest.mark.skip(reason="Default locale is ignored in gs-cloud 1.6.1") +def test_i18n_layers_default_locale( + geoserver_default_locale_it, language, expected_titles +): + layers = geoserver_default_locale_it.get_wms_layers(WORKSPACE, language) + if type(layers) is list: + for expected_layer, expected_title in expected_titles.items(): + actual_layer = next( + (layer for layer in layers if layer["Name"] == expected_layer), {} + ) + print(actual_layer["Name"]) + assert actual_layer.get("Title") == expected_title + else: + print(layers) + assert expected_titles == {} + assert "ServiceExceptionReport" in layers + + +@pytest.mark.parametrize( + "language,expected_label", + [ + ("en", "English"), + ("de", "Deutsch"), + ("fr", "Français"), + ("it", "Italiano"), + ("rm", "Default label"), + (None, "Default label"), + ("ru", "Default label"), + ("foobar", "Default label"), + ("it,fr,de", "Default label"), + ], +) +def test_i18n_legend_with_default_value( + geoserver_i18n_legend_layer, language, expected_label +): + assert_legend( + geoserver_i18n_legend_layer, + "localized_with_default", + language, + expected_label, + ) + + +@pytest.mark.parametrize( + "language,expected_label", + [ + ("it", "Italiano"), + ("rm", ""), + (None, ""), + ("ru", ""), + ("foobar", ""), + ("it,fr,de", ""), + ], +) +def test_i18n_legend_no_default_value( + geoserver_i18n_legend_layer, language, expected_label +): + + assert_legend( + geoserver_i18n_legend_layer, + "localized_no_default", + language, + expected_label, + ) + + +@pytest.mark.parametrize( + "language,expected_label", + [ + ("en", "English"), + ("de", "Deutsch"), + ("fr", "Français"), + ("it", "Italiano"), + ("rm", "Default label"), + (None, "Default label"), + ("ru", "Default label"), + ("foobar", "Default label"), + ("it,fr,de", "Default label"), + ], +) +def test_i18n_legend_with_default_value_and_default_locale( + geoserver_i18n_legend_layer_and_default_locale_it, language, expected_label +): + assert_legend( + geoserver_i18n_legend_layer_and_default_locale_it, + "localized_with_default", + language, + expected_label, + ) + + +@pytest.mark.parametrize( + "language,expected_label", + [ + ("it", "Italiano"), + ("rm", ""), + (None, ""), + ("ru", ""), + ("foobar", ""), + ("it,fr,de", ""), + ], +) +def test_i18n_legend_no_default_value_default_locale( + geoserver_i18n_legend_layer_and_default_locale_it, language, expected_label +): + + assert_legend( + geoserver_i18n_legend_layer_and_default_locale_it, + "localized_no_default", + language, + expected_label, + ) + + +@pytest.mark.parametrize("language", ["de", "fr", "it", None, ""]) +def test_i18n_labels(geoserver_i18n_label_layer, language): + + response = geoserver_i18n_label_layer.get_map( + layers=["i18n_labels"], + bbox=(2599999.5, 1199999.5, 2600000.5, 1200000.5), + size=(300, 100), + format="image/png", + transparent=False, + styles=["localized_labels"], + language=language, + ) + + file_root = f"labels/no_default_locale/default_value/language_{language}" + write_actual_image(response, file_root) + compare_images(RESOURCE_DIR, file_root) + + +@pytest.mark.parametrize("language", ["it", "", None]) +def test_i18n_labels_no_default_value(geoserver_i18n_label_layer, language): + + response = geoserver_i18n_label_layer.get_map( + layers=["i18n_labels"], + bbox=(2699999.5, 1299999.5, 2700000.5, 1300000.5), + size=(300, 100), + format="image/png", + transparent=False, + styles=["localized_labels"], + language=language, + ) + + file_root = f"labels/no_default_locale/no_default_value/language_{language}" + write_actual_image(response, file_root) + compare_images(RESOURCE_DIR, file_root) + + +@pytest.mark.parametrize("language", ["de", "fr", "it", None, ""]) +def test_i18n_labels_default_locale(geoserver_i18n_label_default_locale_fr, language): + + response = geoserver_i18n_label_default_locale_fr.get_map( + layers=["i18n_labels"], + bbox=(2599999.5, 1199999.5, 2600000.5, 1200000.5), + size=(300, 100), + format="image/png", + transparent=False, + styles=["localized_labels"], + language=language, + ) + + file_root = f"labels/default_locale/default_value/language_{language}" + write_actual_image(response, file_root) + compare_images(RESOURCE_DIR, file_root) + + +@pytest.mark.parametrize("language", ["it", "", None]) +def test_i18n_labels_no_default_value_default_locale( + geoserver_i18n_label_default_locale_fr, language +): + + response = geoserver_i18n_label_default_locale_fr.get_map( + layers=["i18n_labels"], + bbox=(2699999.5, 1299999.5, 2700000.5, 1300000.5), + size=(300, 100), + format="image/png", + transparent=False, + styles=["localized_labels"], + language=language, + ) + + file_root = f"labels/default_locale/no_default_value/language_{language}" + write_actual_image(response, file_root) + compare_images(RESOURCE_DIR, file_root) diff --git a/acceptance_tests/tests/test_wfs.py b/acceptance_tests/tests/test_wfs.py new file mode 100644 index 000000000..fddc26e93 --- /dev/null +++ b/acceptance_tests/tests/test_wfs.py @@ -0,0 +1,76 @@ +from conftest import ( + PGDATABASE, + PGHOST, + PGPASSWORD, + PGPORT, + PGSCHEMA, + PGUSER, + RESOURCE_DIR, +) + + +def test_wfs(geoserver): + workspace = datastore = feature_type = "test_wfs" + attributes = { + "geom": { + "type": "Point", + "required": True, + }, + "id": { + "type": "integer", + "required": True, + }, + "title": { + "type": "string", + "required": False, + }, + "timestamp": { + "type": "datetime", + "required": False, + }, + } + response = geoserver.create_workspace(workspace, set_default_workspace=True) + assert response.status_code == 201 + response = geoserver.create_pg_datastore( + workspace=workspace, + datastore=datastore, + pg_host=PGHOST, + pg_port=PGPORT, + pg_db=PGDATABASE, + pg_user=PGUSER, + pg_password=PGPASSWORD, + pg_schema=PGSCHEMA, + set_default_datastore=True, + ) + assert response.status_code == 201 + response = geoserver.create_feature_type( + feature_type, attributes=attributes, epsg=2056 + ) + assert response.status_code == 201 + + # Post a feature through a WFS request + with open(f"{RESOURCE_DIR}/wfs_payload.xml") as file: + data = file.read() + response = geoserver.post_request(f"/{workspace}/wfs/", data=data) + assert response.status_code == 200 + + # GetFeature request + feature_collection = geoserver.get_feature(workspace, feature_type) + assert type(feature_collection) is dict + assert type(feature_collection.get("features")) is list + feature = feature_collection["features"][0] + properties = feature.get("properties") + assert properties.get("id") == 10 + assert properties.get("title") == "Title" + assert properties.get("timestamp") == "2024-05-13T08:14:48.763Z" + assert feature.get("geometry", {}) == { + "type": "Point", + "coordinates": [2600000, 1200000], + } + assert feature_collection.get("crs") == { + "type": "name", + "properties": {"name": "urn:ogc:def:crs:EPSG::2056"}, + } + + response = geoserver.delete_workspace(workspace) + assert response.status_code == 200 diff --git a/acceptance_tests/tests/test_wms.py b/acceptance_tests/tests/test_wms.py new file mode 100644 index 000000000..73af7b43f --- /dev/null +++ b/acceptance_tests/tests/test_wms.py @@ -0,0 +1,128 @@ +import json + +from conftest import ( + PGDATABASE, + PGHOST, + PGPASSWORD, + PGPORT, + PGSCHEMA, + PGUSER, + RESOURCE_DIR, +) +from lib.utils import compare_images, write_actual_image +from sqlalchemy.sql import text + + +def test_create_and_feature_type_and_get_map(db_session, geoserver): + workspace = datastore = feature_type = "test_create_feature_type" + geoserver.create_workspace(workspace, set_default_workspace=True) + geoserver.create_pg_datastore( + workspace=workspace, + datastore=datastore, + pg_host=PGHOST, + pg_port=PGPORT, + pg_db=PGDATABASE, + pg_user=PGUSER, + pg_password=PGPASSWORD, + pg_schema=PGSCHEMA, + set_default_datastore=True, + ) + response = geoserver.create_feature_type( + feature_type, + epsg=2056, + ) + assert response.status_code == 201 + + # Create feature + db_session.execute( + text( + f"INSERT INTO {feature_type} (geom) VALUES (public.ST_SetSRID(public.ST_MakePoint(2600000, 1200000), 2056))" + ) + ) + db_session.commit() + + # GetMap request + response = geoserver.get_map( + layers=[feature_type], + bbox=(2599999.5, 1199999.5, 2600000.5, 1200000.5), + size=(40, 40), + format="image/png", + transparent=False, + ) + + file_root = f"getmap" + write_actual_image(response, file_root) + compare_images(RESOURCE_DIR, file_root) + + geoserver.delete_workspace(workspace) + + +def test_get_feature_info(db_session, geoserver): + workspace = datastore = feature_type = "test_get_feature_info" + attributes = { + "geom": { + "type": "Point", + "required": True, + }, + "label": { + "type": "string", + "required": False, + }, + } + response = geoserver.create_workspace(workspace, set_default_workspace=True) + assert response.status_code == 201 + response = geoserver.create_pg_datastore( + workspace=workspace, + datastore=datastore, + pg_host=PGHOST, + pg_port=PGPORT, + pg_db=PGDATABASE, + pg_user=PGUSER, + pg_password=PGPASSWORD, + pg_schema=PGSCHEMA, + set_default_datastore=True, + ) + assert response.status_code == 201 + response = geoserver.create_feature_type( + feature_type, attributes=attributes, epsg=2056 + ) + assert response.status_code == 201 + + # Create feature + db_session.execute( + text( + f"INSERT INTO {feature_type} (geom, label) VALUES " + "(public.ST_SetSRID(public.ST_MakePoint(2600000, 1200000), 2056), 'Label')" + ) + ) + db_session.commit() + + # Test that layer is published + response = geoserver.get_request(f"/rest/layers/{workspace}:{feature_type}.json") + assert response.status_code == 200 + + # GetFeatureInfo request + response = geoserver.get_feature_info( + layers=[feature_type], + bbox=(2599999.5, 1199999.5, 2600000.5, 1200000.5), + size=(40, 40), + info_format="application/json", + xy=(20, 20), + ) + + data = json.loads(response.read().decode("utf-8")) + + feature = data.get("features", [])[0] + assert feature + assert feature.get("properties").get("label") == "Label" + assert feature.get("geometry") == { + "type": "Point", + "coordinates": [2600000, 1200000], + } + assert data.get("crs") == { + "type": "name", + "properties": {"name": "urn:ogc:def:crs:EPSG::2056"}, + } + + response = geoserver.delete_workspace(workspace) + assert response.status_code == 200 diff --git a/compose/acceptance_pg_entrypoint/001_create_schemas.sql b/compose/acceptance_pg_entrypoint/001_create_schemas.sql index 230f04cfc..4658ff476 100755 --- a/compose/acceptance_pg_entrypoint/001_create_schemas.sql +++ b/compose/acceptance_pg_entrypoint/001_create_schemas.sql @@ -1,3 +1,4 @@ \c geodata CREATE SCHEMA IF NOT EXISTS test1; -CREATE SCHEMA IF NOT EXISTS test2; \ No newline at end of file +CREATE SCHEMA IF NOT EXISTS test2; +CREATE EXTENSION IF NOT EXISTS postgis;