From fa9f72176763c9d028fe857beec7eb87790b4419 Mon Sep 17 00:00:00 2001 From: Adam Dangoor Date: Wed, 14 Oct 2026 11:40:18 +0100 Subject: [PATCH] Supply mock credentials from the backend adapter --- tests/conftest.py | 4 - tests/mock_vws/fixtures/credentials.py | 164 +----------------- tests/mock_vws/fixtures/real_credentials.py | 164 ++++++++++++++++++ tests/mock_vws/fixtures/vuforia_backends.py | 73 ++++++++ .../requests_mock/test_backend_credentials.py | 133 ++++++++++++++ 5 files changed, 371 insertions(+), 167 deletions(-) create mode 100644 tests/mock_vws/fixtures/real_credentials.py create mode 100644 tests/mock_vws/requests_mock/test_backend_credentials.py diff --git a/tests/conftest.py b/tests/conftest.py index b6328d3a2..060b85dea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,11 +2,7 @@ repository. """ -# `credentials` must be listed before modules that import from it. -# If listed later, those imports happen before pytest can register it for -# assertion rewriting, causing a PytestAssertRewriteWarning. pytest_plugins = [ - "tests.mock_vws.fixtures.credentials", "tests.mock_vws.fixtures.prepared_requests", "tests.mock_vws.fixtures.vuforia_backends", "tests.mock_vws.fixtures.model_target_prepared_requests", diff --git a/tests/mock_vws/fixtures/credentials.py b/tests/mock_vws/fixtures/credentials.py index 81d4d59f4..83faa2193 100644 --- a/tests/mock_vws/fixtures/credentials.py +++ b/tests/mock_vws/fixtures/credentials.py @@ -1,97 +1,6 @@ -"""Fixtures for credentials for Vuforia databases.""" +"""Credential data shared by API tests and backend adapters.""" from dataclasses import dataclass, field -from pathlib import Path -from uuid import uuid4 - -import pytest -from pydantic import Field -from pydantic_settings import BaseSettings, SettingsConfigDict - -from mock_vws.database import CloudDatabase -from mock_vws.states import States - - -class _CloudDatabaseSettings(BaseSettings): - """Settings for a Vuforia database.""" - - target_manager_database_name: str - server_access_key: str - server_secret_key: str - client_access_key: str - client_secret_key: str - - model_config = SettingsConfigDict( - env_prefix="VUFORIA_", - env_file=Path("vuforia_secrets.env"), - extra="allow", - ) - - -class _WorkingCloudDatabaseSettings(_CloudDatabaseSettings): - """Settings for the working Vuforia database. - - Only the working database has an ID, because only endpoints which name - a database in their path need one. - """ - - database_id: str - - -class _InactiveCloudDatabaseSettings(_CloudDatabaseSettings): - """Settings for an inactive Vuforia database.""" - - model_config = SettingsConfigDict( - env_prefix="INACTIVE_VUFORIA_", - env_file=Path("vuforia_secrets.env"), - extra="allow", - ) - - -class _InactiveVuMarkDatabaseSettings(BaseSettings): - """Settings for an inactive VuMark database.""" - - target_manager_database_name: str - server_access_key: str - server_secret_key: str - - model_config = SettingsConfigDict( - env_prefix="INACTIVE_VUMARK_VUFORIA_", - env_file=Path("vuforia_secrets.env"), - extra="allow", - ) - - -class _VuMarkCloudDatabaseSettings(BaseSettings): - """Settings for a VuMark Vuforia database.""" - - target_manager_database_name: str - server_access_key: str - server_secret_key: str - target_id: str - processing_target_id: str = Field(default_factory=lambda: uuid4().hex) - - model_config = SettingsConfigDict( - env_prefix="VUMARK_VUFORIA_", - env_file=Path("vuforia_secrets.env"), - extra="allow", - ) - - -class _ModelTargetSettings(BaseSettings): - """Settings for the Model Target Web API.""" - - client_id: str - client_secret: str - cad_data_url: str - username: str - password: str - - model_config = SettingsConfigDict( - env_prefix="MODEL_TARGET_VUFORIA_", - env_file=Path("vuforia_secrets.env"), - extra="allow", - ) @dataclass(frozen=True, kw_only=True) @@ -123,74 +32,3 @@ class ModelTargetCredentials: cad_data_url: str = field(repr=False) username: str = field(repr=False) password: str = field(repr=False) - - -def get_model_target_credentials() -> ModelTargetCredentials: - """Return Model Target Web API credentials from environment - variables. - """ - settings = _ModelTargetSettings.model_validate(obj={}) - return ModelTargetCredentials( - client_id=settings.client_id, - client_secret=settings.client_secret, - cad_data_url=settings.cad_data_url, - username=settings.username, - password=settings.password, - ) - - -@pytest.fixture -def vuforia_database() -> CloudDatabase: - """Return VWS credentials from environment variables.""" - settings = _WorkingCloudDatabaseSettings.model_validate(obj={}) - return CloudDatabase( - database_id=settings.database_id, - database_name=settings.target_manager_database_name, - server_access_key=settings.server_access_key, - server_secret_key=settings.server_secret_key, - client_access_key=settings.client_access_key, - client_secret_key=settings.client_secret_key, - state=States.WORKING, - ) - - -@pytest.fixture -def inactive_cloud_database() -> CloudDatabase: - """ - Return VWS credentials for an inactive project from environment - variables. - """ - settings = _InactiveCloudDatabaseSettings.model_validate(obj={}) - return CloudDatabase( - database_name=settings.target_manager_database_name, - server_access_key=settings.server_access_key, - server_secret_key=settings.server_secret_key, - client_access_key=settings.client_access_key, - client_secret_key=settings.client_secret_key, - state=States.PROJECT_INACTIVE, - ) - - -@pytest.fixture -def inactive_vumark_database() -> InactiveVuMarkCloudDatabase: - """Return inactive VuMark credentials from environment variables.""" - settings = _InactiveVuMarkDatabaseSettings.model_validate(obj={}) - return InactiveVuMarkCloudDatabase( - target_manager_database_name=settings.target_manager_database_name, - server_access_key=settings.server_access_key, - server_secret_key=settings.server_secret_key, - ) - - -@pytest.fixture -def vumark_vuforia_database() -> VuMarkCloudDatabase: - """Return VuMark VWS credentials from environment variables.""" - settings = _VuMarkCloudDatabaseSettings.model_validate(obj={}) - - return VuMarkCloudDatabase( - target_manager_database_name=settings.target_manager_database_name, - server_access_key=settings.server_access_key, - server_secret_key=settings.server_secret_key, - target_id=settings.target_id, - processing_target_id=settings.processing_target_id, - ) diff --git a/tests/mock_vws/fixtures/real_credentials.py b/tests/mock_vws/fixtures/real_credentials.py new file mode 100644 index 000000000..cff23ed2f --- /dev/null +++ b/tests/mock_vws/fixtures/real_credentials.py @@ -0,0 +1,164 @@ +"""Load credentials for the explicit real-Vuforia backend.""" + +from pathlib import Path +from uuid import uuid4 + +from pydantic import Field +from pydantic_settings import BaseSettings, SettingsConfigDict + +from mock_vws.database import CloudDatabase +from mock_vws.states import States +from tests.mock_vws.fixtures.credentials import ( + InactiveVuMarkCloudDatabase, + ModelTargetCredentials, + VuMarkCloudDatabase, +) + + +class _CloudDatabaseSettings(BaseSettings): + """Settings for a Vuforia database.""" + + target_manager_database_name: str + server_access_key: str + server_secret_key: str + client_access_key: str + client_secret_key: str + + model_config = SettingsConfigDict( + env_prefix="VUFORIA_", + env_file=Path("vuforia_secrets.env"), + extra="allow", + ) + + +class _WorkingCloudDatabaseSettings(_CloudDatabaseSettings): + """Settings for the working Vuforia database. + + Only the working database has an ID, because only endpoints which name + a database in their path need one. + """ + + database_id: str + + +class _InactiveCloudDatabaseSettings(_CloudDatabaseSettings): + """Settings for an inactive Vuforia database.""" + + model_config = SettingsConfigDict( + env_prefix="INACTIVE_VUFORIA_", + env_file=Path("vuforia_secrets.env"), + extra="allow", + ) + + +class _InactiveVuMarkDatabaseSettings(BaseSettings): + """Settings for an inactive VuMark database.""" + + target_manager_database_name: str + server_access_key: str + server_secret_key: str + + model_config = SettingsConfigDict( + env_prefix="INACTIVE_VUMARK_VUFORIA_", + env_file=Path("vuforia_secrets.env"), + extra="allow", + ) + + +class _VuMarkCloudDatabaseSettings(BaseSettings): + """Settings for a VuMark Vuforia database.""" + + target_manager_database_name: str + server_access_key: str + server_secret_key: str + target_id: str + processing_target_id: str = Field(default_factory=lambda: uuid4().hex) + + model_config = SettingsConfigDict( + env_prefix="VUMARK_VUFORIA_", + env_file=Path("vuforia_secrets.env"), + extra="allow", + ) + + +class _ModelTargetSettings(BaseSettings): + """Settings for the Model Target Web API.""" + + client_id: str + client_secret: str + cad_data_url: str + username: str + password: str + + model_config = SettingsConfigDict( + env_prefix="MODEL_TARGET_VUFORIA_", + env_file=Path("vuforia_secrets.env"), + extra="allow", + ) + + +def get_model_target_credentials() -> ModelTargetCredentials: + """Return Model Target Web API credentials from environment + variables. + """ + settings = _ModelTargetSettings.model_validate(obj={}) + return ModelTargetCredentials( + client_id=settings.client_id, + client_secret=settings.client_secret, + cad_data_url=settings.cad_data_url, + username=settings.username, + password=settings.password, + ) + + +def get_real_cloud_database() -> CloudDatabase: + """Return VWS credentials from environment variables.""" + settings = _WorkingCloudDatabaseSettings.model_validate(obj={}) + return CloudDatabase( + database_id=settings.database_id, + database_name=settings.target_manager_database_name, + server_access_key=settings.server_access_key, + server_secret_key=settings.server_secret_key, + client_access_key=settings.client_access_key, + client_secret_key=settings.client_secret_key, + state=States.WORKING, + ) + + +def get_real_inactive_cloud_database() -> CloudDatabase: + """ + Return VWS credentials for an inactive project from environment + variables. + """ + settings = _InactiveCloudDatabaseSettings.model_validate(obj={}) + return CloudDatabase( + database_name=settings.target_manager_database_name, + server_access_key=settings.server_access_key, + server_secret_key=settings.server_secret_key, + client_access_key=settings.client_access_key, + client_secret_key=settings.client_secret_key, + state=States.PROJECT_INACTIVE, + ) + + +def get_real_inactive_vumark_database() -> InactiveVuMarkCloudDatabase: + """Return inactive VuMark credentials from environment variables.""" + settings = _InactiveVuMarkDatabaseSettings.model_validate(obj={}) + return InactiveVuMarkCloudDatabase( + target_manager_database_name=settings.target_manager_database_name, + server_access_key=settings.server_access_key, + server_secret_key=settings.server_secret_key, + ) + + +def get_real_vumark_database() -> VuMarkCloudDatabase: + """Return VuMark VWS credentials from environment variables.""" + settings = _VuMarkCloudDatabaseSettings.model_validate(obj={}) + + return VuMarkCloudDatabase( + target_manager_database_name=settings.target_manager_database_name, + server_access_key=settings.server_access_key, + server_secret_key=settings.server_secret_key, + target_id=settings.target_id, + processing_target_id=settings.processing_target_id, + ) diff --git a/tests/mock_vws/fixtures/vuforia_backends.py b/tests/mock_vws/fixtures/vuforia_backends.py index 188834ed3..d6c77aaaf 100644 --- a/tests/mock_vws/fixtures/vuforia_backends.py +++ b/tests/mock_vws/fixtures/vuforia_backends.py @@ -7,6 +7,7 @@ from collections.abc import Generator from enum import Enum from http import HTTPMethod +from uuid import uuid4 import pytest import requests @@ -31,7 +32,13 @@ InactiveVuMarkCloudDatabase, ModelTargetCredentials, VuMarkCloudDatabase, +) +from tests.mock_vws.fixtures.real_credentials import ( get_model_target_credentials, + get_real_cloud_database, + get_real_inactive_cloud_database, + get_real_inactive_vumark_database, + get_real_vumark_database, ) from tests.mock_vws.utils.backend_capabilities import BackendCapabilities from tests.mock_vws.utils.cloud_databases import AddCloudDatabase @@ -436,6 +443,72 @@ class VuforiaBackend(Enum): backend for backend in _ALL_BACKENDS if backend != VuforiaBackend.REAL ] + +@beartype +def _credential_backend(*, request: pytest.FixtureRequest) -> VuforiaBackend: + """Read the active VWS selector without adding another backend matrix. + + Implementation tests which manage their own mock have no selector and + receive mock credentials. + """ + for fixture_name in ("_vws_backend", "_mock_vws_backend"): + if fixture_name in request.fixturenames: + backend = request.getfixturevalue(argname=fixture_name) + assert isinstance(backend, VuforiaBackend) + return backend + return VuforiaBackend.MOCK + + +@pytest.fixture(name="vuforia_database") +def fixture_vuforia_database( + *, request: pytest.FixtureRequest +) -> CloudDatabase: + """An active database with credentials supplied by the backend.""" + if _credential_backend(request=request) == VuforiaBackend.REAL: + return get_real_cloud_database() + return CloudDatabase() + + +@pytest.fixture(name="inactive_cloud_database") +def fixture_inactive_cloud_database( + *, request: pytest.FixtureRequest +) -> CloudDatabase: + """An inactive database with credentials supplied by the backend.""" + if _credential_backend(request=request) == VuforiaBackend.REAL: + return get_real_inactive_cloud_database() + return CloudDatabase(state=States.PROJECT_INACTIVE) + + +@pytest.fixture(name="inactive_vumark_database") +def fixture_inactive_vumark_database( + *, request: pytest.FixtureRequest +) -> InactiveVuMarkCloudDatabase: + """An inactive VuMark database supplied by the backend.""" + if _credential_backend(request=request) == VuforiaBackend.REAL: + return get_real_inactive_vumark_database() + return InactiveVuMarkCloudDatabase( + target_manager_database_name=uuid4().hex, + server_access_key=uuid4().hex, + server_secret_key=uuid4().hex, + ) + + +@pytest.fixture(name="vumark_vuforia_database") +def fixture_vumark_vuforia_database( + *, request: pytest.FixtureRequest +) -> VuMarkCloudDatabase: + """A VuMark database with credentials supplied by the backend.""" + if _credential_backend(request=request) == VuforiaBackend.REAL: + return get_real_vumark_database() + return VuMarkCloudDatabase( + target_manager_database_name=uuid4().hex, + server_access_key=uuid4().hex, + server_secret_key=uuid4().hex, + target_id=uuid4().hex, + processing_target_id=uuid4().hex, + ) + + # These deliberately have no type annotation, so that the keyword # arguments of the setup functions are still checked where they are # bound. diff --git a/tests/mock_vws/requests_mock/test_backend_credentials.py b/tests/mock_vws/requests_mock/test_backend_credentials.py new file mode 100644 index 000000000..5952c465c --- /dev/null +++ b/tests/mock_vws/requests_mock/test_backend_credentials.py @@ -0,0 +1,133 @@ +"""Tests for credential selection inside the backend adapter.""" + +from pathlib import Path + +import pytest + +from mock_vws.database import CloudDatabase +from mock_vws.states import States +from tests.mock_vws.fixtures.credentials import ( + InactiveVuMarkCloudDatabase, + VuMarkCloudDatabase, +) +from tests.mock_vws.fixtures.real_credentials import ( + get_model_target_credentials, +) +from tests.mock_vws.fixtures.vuforia_backends import VuforiaBackend + + +@pytest.fixture(name="credentials_file", autouse=True, params=[False]) +def fixture_credentials_file( + *, + request: pytest.FixtureRequest, + monkeypatch: pytest.MonkeyPatch, + tmp_path: Path, +) -> None: + """Isolate credential sources from real secrets and inherited settings.""" + valid = request.param + assert isinstance(valid, bool) + example = ( + Path(__file__).resolve().parents[3] / "vuforia_secrets.env.example" + ).read_text() + for line in example.splitlines(): + if "=" in line: + variable = line.split(sep="=", maxsplit=1)[0] + monkeypatch.delenv(name=variable, raising=False) + monkeypatch.chdir(path=tmp_path) + contents = example if valid else "UNRELATED_SETTING=invalid\n" + _ = (tmp_path / "vuforia_secrets.env").write_text(data=contents) + + +@pytest.fixture( + name="_vws_backend", + params=[VuforiaBackend.MOCK, VuforiaBackend.FLASK_IN_PROCESS], + ids=["in-memory", "flask"], +) +def fixture_backend_selection( + *, request: pytest.FixtureRequest +) -> VuforiaBackend: + """Select credentials without setting up a service or making + requests. + """ + backend = request.param + assert isinstance(backend, VuforiaBackend) + return backend + + +@pytest.mark.usefixtures("_vws_backend") +def test_mock_credentials_ignore_settings( + *, + vuforia_database: CloudDatabase, + inactive_cloud_database: CloudDatabase, + vumark_vuforia_database: VuMarkCloudDatabase, + inactive_vumark_database: InactiveVuMarkCloudDatabase, +) -> None: + """Both mock adapters supply usable credentials with invalid + settings. + """ + assert vuforia_database.state == States.WORKING + assert inactive_cloud_database.state == States.PROJECT_INACTIVE + assert ( + vuforia_database.server_access_key + != inactive_cloud_database.server_access_key + ) + assert ( + vumark_vuforia_database.target_id + != vumark_vuforia_database.processing_target_id + ) + assert ( + inactive_vumark_database.server_access_key + != vumark_vuforia_database.server_access_key + ) + + +def test_standalone_mock_credentials( + *, vuforia_database: CloudDatabase +) -> None: + """Implementation tests with no selected backend also need no settings.""" + assert vuforia_database.state == States.WORKING + assert vuforia_database.server_access_key != "" + + +@pytest.mark.parametrize( + argnames="credentials_file", argvalues=[True], indirect=True +) +@pytest.mark.usefixtures("_vws_backend") +@pytest.mark.parametrize( + argnames="_vws_backend", argvalues=[VuforiaBackend.REAL], indirect=True +) +def test_real_credentials_use_settings( + *, + vuforia_database: CloudDatabase, + inactive_cloud_database: CloudDatabase, + vumark_vuforia_database: VuMarkCloudDatabase, + inactive_vumark_database: InactiveVuMarkCloudDatabase, +) -> None: + """Only the real selection loads each account from its settings + file. + """ + assert vuforia_database.database_id == "example_database_id" + assert vuforia_database.server_access_key == "example_server_access_key" + assert inactive_cloud_database.state == States.PROJECT_INACTIVE + assert ( + inactive_cloud_database.client_access_key + == "example_inactive_client_access_key" + ) + assert vumark_vuforia_database.target_id == "examplevumarktargetid" + assert ( + inactive_vumark_database.server_access_key + == "example_inactive_vumark_server_access_key" + ) + + +@pytest.mark.parametrize( + argnames="credentials_file", argvalues=[True], indirect=True +) +def test_real_model_target_settings() -> None: + """The real Model Target loader keeps its settings and input data.""" + credentials = get_model_target_credentials() + assert credentials.client_id == "example_model_target_client_id" + assert credentials.client_secret == "example_model_target_client_secret" + assert credentials.cad_data_url == "https://example.com/model.glb" + assert credentials.username == "user@example.com" + assert credentials.password == "example_model_target_password"