diff --git a/tests/mock_vws/content_length/test_incorrect.py b/tests/mock_vws/content_length/test_incorrect.py index af8fc26ab..cef234693 100644 --- a/tests/mock_vws/content_length/test_incorrect.py +++ b/tests/mock_vws/content_length/test_incorrect.py @@ -11,13 +11,13 @@ import pytest from mock_vws._constants import ResultCodes -from tests.mock_vws.fixtures.vuforia_backends import VuforiaBackend from tests.mock_vws.utils import Endpoint from tests.mock_vws.utils.assertions import ( assert_valid_date_header, assert_vwq_failure, assert_vws_failure, ) +from tests.mock_vws.utils.backend_capabilities import BackendCapabilities from tests.mock_vws.utils.too_many_requests import handle_server_errors @@ -85,12 +85,14 @@ def test_not_integer(endpoint: Endpoint, base_vwq_url: str) -> None: def test_too_large( endpoint: Endpoint, - verify_mock_vuforia: VuforiaBackend, + backend_capabilities: BackendCapabilities, base_vwq_url: str, ) -> None: """An error is given if the given content length is too large.""" - if verify_mock_vuforia is VuforiaBackend.REAL: - pytest.skip(reason="Real Vuforia takes too long to respond") + if not backend_capabilities.fast_oversized_content_length_response: + pytest.skip( + reason="This backend responds slowly to oversized Content-Length." + ) if not bool(endpoint.headers.get("Content-Type")): pytest.skip(reason="No Content-Type header for this request") diff --git a/tests/mock_vws/fixtures/vuforia_backends.py b/tests/mock_vws/fixtures/vuforia_backends.py index e1c09cf9a..6c81935f6 100644 --- a/tests/mock_vws/fixtures/vuforia_backends.py +++ b/tests/mock_vws/fixtures/vuforia_backends.py @@ -33,6 +33,7 @@ VuMarkCloudDatabase, get_model_target_credentials, ) +from tests.mock_vws.utils.backend_capabilities import BackendCapabilities from tests.mock_vws.utils.cloud_databases import AddCloudDatabase from tests.mock_vws.utils.model_target_retries import ( retrying_transient_real_backend_failures, @@ -883,3 +884,18 @@ def fixture_signed_model_target_credentials( return _model_target_credentials_for_backend( backend=verify_model_target_mock_vuforia, ) + + +@pytest.fixture(name="backend_capabilities") +def fixture_backend_capabilities( + *, + verify_mock_vuforia: VuforiaBackend, +) -> BackendCapabilities: + """The behavior supported by the running VWS and query backend.""" + is_mock = verify_mock_vuforia != VuforiaBackend.REAL + return BackendCapabilities( + deterministic_target_order=is_mock, + deterministic_query_order=is_mock, + fast_oversized_content_length_response=is_mock, + rolling_rate_limit_window=is_mock, + ) diff --git a/tests/mock_vws/get_duplicates/test_duplicates.py b/tests/mock_vws/get_duplicates/test_duplicates.py index f3f45e84c..c0dbfd18f 100644 --- a/tests/mock_vws/get_duplicates/test_duplicates.py +++ b/tests/mock_vws/get_duplicates/test_duplicates.py @@ -9,7 +9,7 @@ from vws import VWS from vws.reports import TargetStatuses -from tests.mock_vws.fixtures.vuforia_backends import VuforiaBackend +from tests.mock_vws.utils.backend_capabilities import BackendCapabilities def test_duplicates( @@ -136,7 +136,7 @@ def test_status( def test_order_is_upload_date_then_target_id( *, - verify_mock_vuforia: VuforiaBackend, + backend_capabilities: BackendCapabilities, high_quality_image: io.BytesIO, vws_client: VWS, ) -> None: @@ -145,8 +145,10 @@ def test_order_is_upload_date_then_target_id( The real Vuforia Web Services do not document an order, so we do not verify this against them. """ - if verify_mock_vuforia == VuforiaBackend.REAL: - pytest.skip(reason="The real Vuforia does not document an order.") + if not backend_capabilities.deterministic_target_order: + pytest.skip( + reason="This backend does not guarantee target upload-date order." + ) target_ids = [ vws_client.add_target( diff --git a/tests/mock_vws/query/test_result_order.py b/tests/mock_vws/query/test_result_order.py index be4824e8f..4b9a98d4f 100644 --- a/tests/mock_vws/query/test_result_order.py +++ b/tests/mock_vws/query/test_result_order.py @@ -8,18 +8,18 @@ from vws import VWS from mock_vws.database import CloudDatabase -from tests.mock_vws.fixtures.vuforia_backends import VuforiaBackend from tests.mock_vws.query.helpers import ( query, ) from tests.mock_vws.utils.assertions import ( assert_query_success, ) +from tests.mock_vws.utils.backend_capabilities import BackendCapabilities def test_order_is_upload_date_then_target_id( *, - verify_mock_vuforia: VuforiaBackend, + backend_capabilities: BackendCapabilities, high_quality_image: io.BytesIO, vws_client: VWS, vuforia_database: CloudDatabase, @@ -33,9 +33,9 @@ def test_order_is_upload_date_then_target_id( matcher, so we do not verify this against the real Vuforia Web Services. """ - if verify_mock_vuforia == VuforiaBackend.REAL: + if not backend_capabilities.deterministic_query_order: pytest.skip( - reason="The real Query API uses Vuforia's match scores.", + reason="This backend does not guarantee query upload-date order.", ) target_ids = [ @@ -72,7 +72,7 @@ def test_order_is_upload_date_then_target_id( def test_max_num_results_keeps_the_first_results( *, - verify_mock_vuforia: VuforiaBackend, + backend_capabilities: BackendCapabilities, high_quality_image: io.BytesIO, vws_client: VWS, vuforia_database: CloudDatabase, @@ -84,9 +84,9 @@ def test_max_num_results_keeps_the_first_results( Which matches survive the truncation therefore does not vary between runs. """ - if verify_mock_vuforia == VuforiaBackend.REAL: + if not backend_capabilities.deterministic_query_order: pytest.skip( - reason="The real Query API uses Vuforia's match scores.", + reason="This backend does not guarantee query upload-date order.", ) target_ids = [ diff --git a/tests/mock_vws/target_list/test_rate_limit.py b/tests/mock_vws/target_list/test_rate_limit.py index d70a08eb5..3d5aa2446 100644 --- a/tests/mock_vws/target_list/test_rate_limit.py +++ b/tests/mock_vws/target_list/test_rate_limit.py @@ -8,17 +8,15 @@ from mock_vws._constants import ResultCodes from mock_vws.database import CloudDatabase from mock_vws.request_rate_limits import DOCUMENTED_REQUEST_RATE_LIMITS -from tests.mock_vws.fixtures.vuforia_backends import ( - VuforiaBackend, -) from tests.mock_vws.utils import Endpoint from tests.mock_vws.utils.assertions import assert_vws_too_many_requests +from tests.mock_vws.utils.backend_capabilities import BackendCapabilities def test_two_requests_per_minute( *, base_vws_url: str, - verify_mock_vuforia: VuforiaBackend, + backend_capabilities: BackendCapabilities, rate_limited_database: CloudDatabase, ) -> None: """A third ``GET /targets`` request within a minute is rate @@ -66,7 +64,7 @@ def test_two_requests_per_minute( assert len(responses) <= limit.max_requests + 2 status_codes = [response.status_code for response in responses] - if verify_mock_vuforia != VuforiaBackend.REAL: + if backend_capabilities.rolling_rate_limit_window: assert status_codes == [ *[HTTPStatus.OK] * limit.max_requests, HTTPStatus.TOO_MANY_REQUESTS, diff --git a/tests/mock_vws/target_list/test_target_list.py b/tests/mock_vws/target_list/test_target_list.py index 8346144aa..3e1843152 100644 --- a/tests/mock_vws/target_list/test_target_list.py +++ b/tests/mock_vws/target_list/test_target_list.py @@ -6,9 +6,7 @@ import pytest from vws import VWS -from tests.mock_vws.fixtures.vuforia_backends import ( - VuforiaBackend, -) +from tests.mock_vws.utils.backend_capabilities import BackendCapabilities def test_includes_targets( @@ -32,7 +30,7 @@ def test_deleted( def test_order_is_upload_date_then_target_id( *, - verify_mock_vuforia: VuforiaBackend, + backend_capabilities: BackendCapabilities, high_quality_image: io.BytesIO, vws_client: VWS, ) -> None: @@ -41,8 +39,10 @@ def test_order_is_upload_date_then_target_id( The real Vuforia Web Services do not document an order, so we do not verify this against them. """ - if verify_mock_vuforia == VuforiaBackend.REAL: - pytest.skip(reason="The real Vuforia does not document an order.") + if not backend_capabilities.deterministic_target_order: + pytest.skip( + reason="This backend does not guarantee target upload-date order." + ) target_ids = [ vws_client.add_target( diff --git a/tests/mock_vws/utils/backend_capabilities.py b/tests/mock_vws/utils/backend_capabilities.py new file mode 100644 index 000000000..efb3c2e4c --- /dev/null +++ b/tests/mock_vws/utils/backend_capabilities.py @@ -0,0 +1,29 @@ +"""Behavior declared by an adapter for the shared API tests.""" + +from dataclasses import dataclass + +from beartype import beartype + + +@beartype +@dataclass(frozen=True, kw_only=True) +class BackendCapabilities: + """The behavior and checks supported by the running service. + + ``deterministic_target_order`` covers upload-date ordering for target + lists and duplicate lists. ``deterministic_query_order`` covers that + ordering for matches to identical images. + + ``fast_oversized_content_length_response`` enables requests whose + declared body length exceeds their actual length. Services which wait + too long for the missing bytes cannot run that check. + + ``rolling_rate_limit_window`` enables the exact request-sequence + assertion for a fresh database. Services with a fixed clock window + still run the common rate-limit assertions. + """ + + deterministic_target_order: bool + deterministic_query_order: bool + fast_oversized_content_length_response: bool + rolling_rate_limit_window: bool