Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d2b383a
Add minimal VuMark API interaction test
adamtheturtle Feb 16, 2026
a133585
Share VuMark credentials across secrets files
adamtheturtle Feb 16, 2026
00a5951
Set VuMark test Accept header
adamtheturtle Feb 16, 2026
f8c083c
Make VuMark test assert successful generation
adamtheturtle Feb 16, 2026
9efcf5e
Use VuMark fixture fields in generation test
adamtheturtle Feb 16, 2026
18cedb6
Update CI workflow and remove VuMark test skip logic
adamtheturtle Feb 16, 2026
6d87404
Generate VuMark instance IDs in test instead of env settings
adamtheturtle Feb 16, 2026
dfd0bde
Refactor secrets generation to initialize VuMark data once
adamtheturtle Feb 17, 2026
d65bccf
Fix typing in VuMark setup refactor
adamtheturtle Feb 17, 2026
a762e69
Remove redundant VuMark None check for pyright
adamtheturtle Feb 17, 2026
ee59318
Skip VuMark provisioning when no secrets files are missing
adamtheturtle Feb 17, 2026
2ea8843
Merge origin/main into adamtheturtle/vumark-api-test
adamtheturtle Feb 17, 2026
9409d2e
Merge origin/main into adamtheturtle/vumark-api-test
adamtheturtle Feb 17, 2026
381878f
Merge remote-tracking branch 'origin/main' into adamtheturtle/vumark-…
adamtheturtle Feb 17, 2026
79bad8d
Merge origin/main into adamtheturtle/vumark-api-test
adamtheturtle Feb 17, 2026
f706b1a
Reset VuMark secrets files to origin/main
adamtheturtle Feb 17, 2026
7dcfeef
Add mock VuMark instance generation support and class-based test
adamtheturtle Feb 17, 2026
9cd8765
Fix generator teardown pattern in backend fixture
adamtheturtle Feb 17, 2026
bf6ed8c
Merge remote-tracking branch 'origin/main' into adamtheturtle/vumark-…
adamtheturtle Feb 17, 2026
43e2ac6
Add VuMark target setup to mock backends
adamtheturtle Feb 17, 2026
bf8224a
Fix BugBot issues for VuMark support
adamtheturtle Feb 17, 2026
fbae231
Fix mypy typing in target validator test
adamtheturtle Feb 17, 2026
6dea443
Resolve pyright typing in validator test
adamtheturtle Feb 17, 2026
941348f
Remove uncovered branches from generate_vumark_instance
adamtheturtle Feb 17, 2026
5442a76
Use valid default target ID for VuMark database settings
adamtheturtle Feb 18, 2026
270f9d9
Use valid target ID placeholder in vuforia_secrets.env.example
adamtheturtle Feb 18, 2026
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
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ jobs:
- tests/mock_vws/test_update_target.py::TestInactiveProject
- tests/mock_vws/test_requests_mock_usage.py
- tests/mock_vws/test_flask_app_usage.py
- tests/mock_vws/test_vumark_generation_api.py
- tests/mock_vws/test_docker.py
- README.rst
- docs/source/basic-example.rst
Expand Down
47 changes: 31 additions & 16 deletions admin/create_secrets_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,37 @@ def main() -> None:
for i in range(num_databases)
]
files_to_create = [file for file in required_files if not file.exists()]
driver: WebDriver | None = None
if not files_to_create:
sys.stdout.write("No secrets files need to be created.\n")
return

shared_vumark_details: VuMarkDatabaseDict | None = None

while shared_vumark_details is None:
vumark_driver = vws_web_tools.create_chrome_driver()
time = datetime.datetime.now(tz=datetime.UTC).strftime(
format="%Y-%m-%d-%H-%M-%S",
)
vumark_database_name = f"my-vumark-database-{time}"
try:
vws_web_tools.log_in(
driver=vumark_driver,
email_address=email_address,
password=password,
)
vws_web_tools.wait_for_logged_in(driver=vumark_driver)
shared_vumark_details = _create_and_get_vumark_details(
driver=vumark_driver,
vumark_database_name=vumark_database_name,
)
except TimeoutException:
sys.stderr.write(
"Timed out waiting for shared VuMark setup/details after "
"retries\n"
)
vumark_driver.quit()
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated

driver: WebDriver | None = None
while files_to_create:
if driver is None:
driver = vws_web_tools.create_chrome_driver()
Expand All @@ -151,7 +180,6 @@ def main() -> None:
)
license_name = f"my-license-{time}"
database_name = f"my-database-{time}"
vumark_database_name = f"my-vumark-database-{time}"

try:
database_details = _create_and_get_database_details(
Expand All @@ -169,25 +197,12 @@ def main() -> None:
driver = None
continue

try:
vumark_details = _create_and_get_vumark_details(
driver=driver,
vumark_database_name=vumark_database_name,
)
except TimeoutException:
sys.stderr.write(
"Timed out waiting for VuMark setup/details after retries\n"
)
driver.quit()
driver = None
continue

driver.quit()
driver = None

file_contents = _generate_secrets_file_content(
database_details=database_details,
vumark_details=vumark_details,
vumark_details=shared_vumark_details,
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated
inactive_database_details=inactive_database_details,
)
file.write_text(data=file_contents)
Expand Down
8 changes: 7 additions & 1 deletion docs/source/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,14 @@ Then, add a database from the `Vuforia Target Manager`_.

To find the environment variables to set in the :file:`vuforia_secrets.env` file, visit the Target Database in the `Vuforia Target Manager`_ and view the "Database Access Keys".

Two databases are necessary in order to run all the tests.
Two Cloud databases are necessary in order to run all the Cloud Target tests.
One of those must be an inactive project.
To create an inactive project, delete the license key associated with a database.

VuMark tests require one VuMark database.
When creating multiple credentials files, the same inactive database and the
same VuMark database can be reused across all files.

Targets sometimes get stuck at the "Processing" stage meaning that they cannot be deleted.
When this happens, create a new target database to use for testing.

Expand All @@ -101,6 +105,8 @@ To create databases without using the browser, use :file:`admin/create_secrets_f
$ export EXISTING_SECRETS_FILE=/existing/file/with/inactive/db/creds
# You may have to run this a few times, but it is idempotent.
$ python admin/create_secrets_files.py
# Each generated file gets its own Cloud database credentials and shares
# one VuMark database credential set.
# After creating the secrets, update the encrypted archive:
$ tar cvf secrets.tar "${NEW_SECRETS_DIR}"
$ gpg \
Expand Down
39 changes: 39 additions & 0 deletions tests/mock_vws/fixtures/credentials.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Fixtures for credentials for Vuforia databases."""

from dataclasses import dataclass
from pathlib import Path

import pytest
Expand Down Expand Up @@ -35,6 +36,31 @@ class _InactiveVuforiaDatabaseSettings(_VuforiaDatabaseSettings):
)


class _VuMarkVuforiaDatabaseSettings(BaseSettings):
"""Settings for a VuMark Vuforia database."""

target_manager_database_name: str
server_access_key: str
server_secret_key: str
target_id: str = "<SHARED_VUMARK_TARGET_ID>"

model_config = SettingsConfigDict(
env_prefix="VUMARK_VUFORIA_",
env_file=Path("vuforia_secrets.env"),
extra="allow",
)


@dataclass(frozen=True)
class VuMarkVuforiaDatabase:
"""Credentials for the VuMark generation API."""

target_manager_database_name: str
server_access_key: str
server_secret_key: str
target_id: str
Comment thread
cursor[bot] marked this conversation as resolved.
Outdated


@pytest.fixture
def vuforia_database() -> VuforiaDatabase:
"""Return VWS credentials from environment variables."""
Expand Down Expand Up @@ -64,3 +90,16 @@ def inactive_database() -> VuforiaDatabase:
client_secret_key=settings.client_secret_key,
state=States.PROJECT_INACTIVE,
)


@pytest.fixture
def vumark_vuforia_database() -> VuMarkVuforiaDatabase:
"""Return VuMark VWS credentials from environment variables."""
settings = _VuMarkVuforiaDatabaseSettings.model_validate(obj={})

Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
return VuMarkVuforiaDatabase(
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,
)
53 changes: 53 additions & 0 deletions tests/mock_vws/test_vumark_generation_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"""Tests for the VuMark generation web API."""

import json
from http import HTTPMethod, HTTPStatus
from uuid import uuid4

import requests
from vws_auth_tools import authorization_header, rfc_1123_date

from tests.mock_vws.fixtures.credentials import VuMarkVuforiaDatabase

_VWS_HOST = "https://vws.vuforia.com"
_PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n"


def test_generate_instance_success(
vumark_vuforia_database: VuMarkVuforiaDatabase,
) -> None:
"""A VuMark instance can be generated with valid template settings."""
request_path = f"/targets/{vumark_vuforia_database.target_id}/instances"
content_type = "application/json"
generated_instance_id = uuid4().hex
content = json.dumps(obj={"instance_id": generated_instance_id}).encode(
encoding="utf-8"
)
date = rfc_1123_date()
authorization_string = authorization_header(
access_key=vumark_vuforia_database.server_access_key,
secret_key=vumark_vuforia_database.server_secret_key,
method=HTTPMethod.POST,
content=content,
content_type=content_type,
date=date,
request_path=request_path,
)

response = requests.post(
url=_VWS_HOST + request_path,
headers={
"Accept": "image/png",
"Authorization": authorization_string,
"Content-Length": str(object=len(content)),
"Content-Type": content_type,
"Date": date,
},
data=content,
timeout=30,
)
Comment thread
cursor[bot] marked this conversation as resolved.

assert response.status_code == HTTPStatus.OK
assert response.headers["Content-Type"].split(sep=";")[0] == "image/png"
assert response.content.startswith(_PNG_SIGNATURE)
assert len(response.content) > len(_PNG_SIGNATURE)
8 changes: 5 additions & 3 deletions vuforia_secrets.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ INACTIVE_VUFORIA_SERVER_SECRET_KEY=<INACTIVE_SERVER_SECRET_KEY>
INACTIVE_VUFORIA_CLIENT_ACCESS_KEY=<INACTIVE_CLIENT_ACCESS_KEY>
INACTIVE_VUFORIA_CLIENT_SECRET_KEY=<INACTIVE_CLIENT_SECRET_KEY>

VUMARK_VUFORIA_TARGET_MANAGER_DATABASE_NAME=<VUMARK_DATABASE_NAME>
# Shared across all generated secrets files.
VUMARK_VUFORIA_TARGET_MANAGER_DATABASE_NAME=<SHARED_VUMARK_DATABASE_NAME>

VUMARK_VUFORIA_SERVER_ACCESS_KEY=<VUMARK_SERVER_ACCESS_KEY>
VUMARK_VUFORIA_SERVER_SECRET_KEY=<VUMARK_SERVER_SECRET_KEY>
VUMARK_VUFORIA_SERVER_ACCESS_KEY=<SHARED_VUMARK_SERVER_ACCESS_KEY>
VUMARK_VUFORIA_SERVER_SECRET_KEY=<SHARED_VUMARK_SERVER_SECRET_KEY>
VUMARK_VUFORIA_TARGET_ID=<SHARED_VUMARK_TARGET_ID>