Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@
"tests.mock_vws.fixtures.credentials",
"tests.mock_vws.fixtures.prepared_requests",
"tests.mock_vws.fixtures.vuforia_backends",
# ``model_target_prepared_requests`` imports from
# ``vuforia_backends``, so it must be listed after it.
"tests.mock_vws.fixtures.model_target_prepared_requests",
]

Expand Down
22 changes: 0 additions & 22 deletions tests/mock_vws/fixtures/model_target_prepared_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@
from mock_vws.model_target import JSONValue
from tests.mock_vws.fixtures.credentials import (
ModelTargetCredentials,
get_model_target_credentials,
)
from tests.mock_vws.fixtures.vuforia_backends import VuforiaBackend
from tests.mock_vws.utils import ModelTargetEndpoint
from tests.mock_vws.utils.assertions import assert_model_target_status

Expand All @@ -40,33 +38,13 @@
}


@beartype
def credentials_for_backend(
*,
backend: VuforiaBackend,
) -> ModelTargetCredentials:
"""Return Model Target credentials for the chosen backend."""
if backend == VuforiaBackend.REAL:
return get_model_target_credentials()

return ModelTargetCredentials(
client_id="client-id",
client_secret="client-secret",
cad_data_url="https://example.com/model.glb",
username="user@example.com",
password="password",
)


@beartype
def get_access_token(
*,
base_vws_url: str,
credentials: ModelTargetCredentials,
backend: VuforiaBackend,
) -> str:
"""Return an OAuth2 access token."""
del backend
response = requests.post(
url=f"{base_vws_url}/oauth2/token",
auth=(credentials.client_id, credentials.client_secret),
Expand Down
96 changes: 96 additions & 0 deletions tests/mock_vws/fixtures/vuforia_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
from mock_vws.target import VuMarkTarget
from tests.mock_vws.fixtures.credentials import (
InactiveVuMarkCloudDatabase,
ModelTargetCredentials,
VuMarkCloudDatabase,
get_model_target_credentials,
)
from tests.mock_vws.utils.cloud_databases import AddCloudDatabase
from tests.mock_vws.utils.model_target_retries import (
Expand Down Expand Up @@ -787,3 +789,97 @@ def _send_flask_unrouted_request(
key="x-envoy-upstream-service-time",
),
)


@beartype
def _model_target_credentials_for_backend(
*,
backend: VuforiaBackend,
) -> ModelTargetCredentials:
"""Return Model Target credentials for the chosen backend."""
if backend == VuforiaBackend.REAL:
return get_model_target_credentials()

return ModelTargetCredentials(
client_id="client-id",
client_secret="client-secret",
cad_data_url="https://example.com/model.glb",
username="user@example.com",
password="password",
)


@pytest.fixture(name="model_target_credentials")
def fixture_model_target_credentials(
*,
verify_model_target_mock_vuforia: VuforiaBackend,
) -> ModelTargetCredentials:
"""Model Target credentials supplied by the running backend."""
return _model_target_credentials_for_backend(
backend=verify_model_target_mock_vuforia,
)


@pytest.fixture(name="mock_model_target_credentials")
def fixture_mock_model_target_credentials(
*,
model_target_mock_only_vuforia: VuforiaBackend,
) -> ModelTargetCredentials:
"""Model Target credentials for tests which require a mock."""
return _model_target_credentials_for_backend(
backend=model_target_mock_only_vuforia,
)


# Creating an advanced dataset with a state-based configuration or a standard
# dataset with inline CAD data is a "signed" request: the real Vuforia signs
# the trained dataset, and each signing consumes the account's allowance. The
# allowance is tiny (roughly 20 signings, under ten CI runs' worth), it
# is shared by every CI job and every concurrent run, and it cannot be
# raised or reset by us. Verifying this behavior on every run therefore
# burns the whole allowance within hours and then turns every CI run red
# with ``TRAINING_ALLOWANCE_EXCEEDED`` - which is exactly what happened
# when it ran unconditionally. The equivalent unsigned requests (a
# standard dataset, or an advanced dataset without a state-based
# configuration) are far cheaper and stay enabled, though with enough
# traffic they can also hit the allowance; an unexpected allowance
# rejection is reported as an expected failure by
# ``assert_model_target_status`` rather than failing the run.
_SIGNED_REQUEST_SKIP_REASON = (
"Signed Model Target requests consume the real Vuforia account's "
"small, shared, non-resettable training allowance, so they are not "
"verified against the real Vuforia by default. Pass "
f"{VERIFY_MODEL_TARGET_SIGNING_OPTION} to verify them, for example "
"after the allowance has recovered. The mock backends always run "
"this test."
)


def _skip_unrequested_real_signing(
*,
request: pytest.FixtureRequest,
backend: VuforiaBackend,
) -> None:
"""Skip a signing request against real Vuforia unless opted in."""
if backend is VuforiaBackend.REAL and not request.config.getoption(
name=VERIFY_MODEL_TARGET_SIGNING_OPTION,
):
pytest.skip(reason=_SIGNED_REQUEST_SKIP_REASON)


@pytest.fixture(name="signed_model_target_credentials")
def fixture_signed_model_target_credentials(
*,
verify_model_target_mock_vuforia: VuforiaBackend,
request: pytest.FixtureRequest,
) -> ModelTargetCredentials:
"""Credentials for signing tests, after applying the real-service
opt-in policy.
"""
_skip_unrequested_real_signing(
request=request,
backend=verify_model_target_mock_vuforia,
)
return _model_target_credentials_for_backend(
backend=verify_model_target_mock_vuforia,
)
21 changes: 1 addition & 20 deletions tests/mock_vws/model_target_web_api/authentication.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,7 @@
"""Authentication inputs and token creation for Model Target API tests."""

from beartype import beartype

from tests.mock_vws.fixtures.model_target_prepared_requests import (
credentials_for_backend,
get_access_token,
)
from tests.mock_vws.fixtures.vuforia_backends import VuforiaBackend
"""Authentication inputs for Model Target API tests."""

MOCK_BEARER_TOKEN = (
"eyJhbGciOiJtb2NrIn0."
"eyJzY29wZSI6Im1vZGVsdGFyZ2V0cy5zdGFuZGFyZG1vZGVsdGFyZ2V0LmFsbCJ9."
"c2lnbmF0dXJl"
)


@beartype
def access_token_for_backend(
*, base_vws_url: str, backend: VuforiaBackend
) -> str:
"""Return a valid access token for the chosen backend."""
credentials = credentials_for_backend(backend=backend)
return get_access_token(
base_vws_url=base_vws_url, credentials=credentials, backend=backend
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,7 @@
import pytest
import requests

from tests.mock_vws.fixtures.model_target_prepared_requests import (
credentials_for_backend,
)
from tests.mock_vws.fixtures.vuforia_backends import (
VuforiaBackend,
)
from tests.mock_vws.fixtures.credentials import ModelTargetCredentials
from tests.mock_vws.model_target_web_api.assertions import (
assert_oauth2_error,
)
Expand Down Expand Up @@ -105,19 +100,16 @@ def test_invalid_oauth2_token_request(

def test_password_grant(
*,
model_target_credentials: ModelTargetCredentials,
base_vws_url: str,
verify_model_target_mock_vuforia: VuforiaBackend,
) -> None:
"""A username and password can be exchanged for a scoped token."""
credentials = credentials_for_backend(
backend=verify_model_target_mock_vuforia,
)
response = requests.post(
url=f"{base_vws_url}/oauth2/token",
data={
"grant_type": "password",
"username": credentials.username,
"password": credentials.password,
"username": model_target_credentials.username,
"password": model_target_credentials.password,
"scope": "modeltargets.standardmodeltarget.all",
},
timeout=30,
Expand All @@ -133,16 +125,16 @@ def test_password_grant(

def test_scoped_client_credentials_grant(
*,
model_target_credentials: ModelTargetCredentials,
base_vws_url: str,
verify_model_target_mock_vuforia: VuforiaBackend,
) -> None:
"""A client credentials request accepts an explicit scope."""
credentials = credentials_for_backend(
backend=verify_model_target_mock_vuforia,
)
response = requests.post(
url=f"{base_vws_url}/oauth2/token",
auth=(credentials.client_id, credentials.client_secret),
auth=(
model_target_credentials.client_id,
model_target_credentials.client_secret,
),
data={
"grant_type": "client_credentials",
"scope": "modeltargets.standardmodeltarget.all",
Expand All @@ -160,16 +152,16 @@ def test_scoped_client_credentials_grant(

def test_insufficient_scope(
*,
model_target_credentials: ModelTargetCredentials,
base_vws_url: str,
verify_model_target_mock_vuforia: VuforiaBackend,
) -> None:
"""A route rejects a token carrying only another route's scope."""
credentials = credentials_for_backend(
backend=verify_model_target_mock_vuforia,
)
token_response = requests.post(
url=f"{base_vws_url}/oauth2/token",
auth=(credentials.client_id, credentials.client_secret),
auth=(
model_target_credentials.client_id,
model_target_credentials.client_secret,
),
data={
"grant_type": "client_credentials",
"scope": "modeltargets.standardmodeltarget.all",
Expand Down Expand Up @@ -203,21 +195,18 @@ def test_insufficient_scope(

def test_client_credentials_management(
*,
model_target_credentials: ModelTargetCredentials,
base_vws_url: str,
verify_model_target_mock_vuforia: VuforiaBackend,
) -> None:
"""Client credentials can be created, listed, updated and
deleted.
"""
credentials = credentials_for_backend(
backend=verify_model_target_mock_vuforia,
)
password_token_response = requests.post(
url=f"{base_vws_url}/oauth2/token",
data={
"grant_type": "password",
"username": credentials.username,
"password": credentials.password,
"username": model_target_credentials.username,
"password": model_target_credentials.password,
"scope": "oauth2.clientcredentials.all",
},
timeout=30,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,11 @@
import pytest
import requests

from tests.mock_vws.fixtures.credentials import ModelTargetCredentials
from tests.mock_vws.fixtures.model_target_prepared_requests import (
MODEL_TARGET_DATASET_UUID,
credentials_for_backend,
get_access_token,
)
from tests.mock_vws.fixtures.vuforia_backends import (
VuforiaBackend,
)
from tests.mock_vws.model_target_web_api.assertions import assert_oauth2_error
from tests.mock_vws.model_target_web_api.errors.helpers import (
EMPTY_GUIDE_VIEW_POSITION,
Expand Down Expand Up @@ -535,19 +532,15 @@ def test_invalid_basic_auth_header(
)
def test_invalid_dataset_request(
*,
model_target_credentials: ModelTargetCredentials,
base_vws_url: str,
verify_model_target_mock_vuforia: VuforiaBackend,
body: dict[str, object],
expected_messages: set[str],
) -> None:
"""Invalid standard dataset creation requests are rejected."""
credentials = credentials_for_backend(
backend=verify_model_target_mock_vuforia,
)
access_token = get_access_token(
base_vws_url=base_vws_url,
credentials=credentials,
backend=verify_model_target_mock_vuforia,
credentials=model_target_credentials,
)
response = requests.post(
url=f"{base_vws_url}/modeltargets/datasets",
Expand All @@ -573,22 +566,18 @@ def test_invalid_dataset_request(

def test_advanced_model_count_exceeds_limit(
*,
model_target_credentials: ModelTargetCredentials,
base_vws_url: str,
verify_model_target_mock_vuforia: VuforiaBackend,
) -> None:
"""Advanced datasets reject more than 20 uniquely named models."""
models = [{**MODEL, "name": f"model-{index}"} for index in range(21)]
# Include a duplicate to verify the two validation details which real
# Vuforia returns together for this request.
models[-1]["name"] = models[0]["name"]
body = {**UNAUTHENTICATED_DATASET_REQUEST, "models": models}
credentials = credentials_for_backend(
backend=verify_model_target_mock_vuforia,
)
access_token = get_access_token(
base_vws_url=base_vws_url,
credentials=credentials,
backend=verify_model_target_mock_vuforia,
credentials=model_target_credentials,
)
response = requests.post(
url=f"{base_vws_url}/modeltargets/advancedDatasets",
Expand Down Expand Up @@ -634,19 +623,15 @@ def test_advanced_model_count_exceeds_limit(
)
def test_unknown_dataset(
*,
model_target_credentials: ModelTargetCredentials,
base_vws_url: str,
verify_model_target_mock_vuforia: VuforiaBackend,
method: HTTPMethod,
path: str,
) -> None:
"""Unknown datasets are rejected with a NOT_FOUND error."""
credentials = credentials_for_backend(
backend=verify_model_target_mock_vuforia,
)
access_token = get_access_token(
base_vws_url=base_vws_url,
credentials=credentials,
backend=verify_model_target_mock_vuforia,
credentials=model_target_credentials,
)
response = requests.request(
method=method,
Expand Down
Loading
Loading