Safe test-mode for ViUR core projects — Playwright e2e ready.
Project description
viur-testing
Safe test-mode for ViUR core projects — primarily for Playwright
end-to-end tests. Swaps the default datastore database out for a
dedicated named database (default: viur-tests), with a bilateral
handshake that refuses to let any test or endpoint run unless both the
server and the runner agree they are talking to the test instance.
Bilateral guarantee in six lines
activate()refuses outsideconf.instance.is_dev_server— read from viur-core's canonical flag.- Synchronous datastore probe in the target database has to succeed before the client swap is applied.
TestModuleandConfigModuleboth refuse to instantiate outside the dev server or without a prioractivate()call — both__init__s raise in either case. Even a host that bypasses theTestModulecontainer and mountsConfigModuledirectly is subject to the same checks, so a forgotten activate or a stray production mount fails loudly at boot.- Per-request token validator (
viur-test-tokencookie) blocks every request that does not carry the session token, except the bootstrap endpoints. protect()installs a production-side cookie guard that 403s any request carrying theviur-test-tokencookie outside dev, regardless of its value. Installed in every environment.- Runner preflight calls
/json/_test/config/statusand refuses to run any test if the server's reply (database, project_id, token hash) does not match.
The session token is the source of truth in the test database itself
(kind viur-tests, entity auth-token). The server side never writes
the token to disk — App Engine file-system writes are not needed, no
state survives a server restart.
The runner side does cache a copy: the Playwright globalSetup persists
the parsed /_test/config/status response under .auth/token.json
(plus a process.env.E2E_TEST_TOKEN for spawned subprocesses) so that
worker processes and per-test fixtures can read the session info
without re-hitting the bootstrap endpoint. .auth/ is gitignored by
the viur-testing-init template; globalTeardown deletes the file on
suite end. If you want to drop both the on-disk and env-var copies in
your own setup, replace createGlobalSetup/createGlobalTeardown
with a custom wrapper that uses your preferred mechanism instead.
Module layout
/_test/ TestModule (container, refuses outside dev mode)
/_test/config/status ConfigModule.status — issues/returns token
/_test/config/enter ConfigModule.enter — sets the token cookie
/_test/config/finish ConfigModule.finish — deletes token entity
Future test flavours (load test, integration helpers, …) go in as
sibling submodules under the same _test container.
Requirements
- Python ≥ 3.12
- viur-core ≥ 3.7, < 4
- A named Datastore database (default name:
viur-tests) created in your GCP project alongside(default).
Install
The PyPI distribution name is spltz-viur-testing (experimental
prefix); the Python import path stays viur.testing:
pip install spltz-viur-testing
import viur.testing
viur.testing.setup()
Server-side wiring
Two one-liners. main.py — as the first lines, before any
viur.core import:
import viur.testing
viur.testing.setup()
# Only now may viur.core be imported by your own code.
from viur.core import setup as core_setup
import modules
import render
core_setup(modules, render)
viur.testing.setup() reads the VIUR_TESTING env var (1/true/on
for the default namespace, or any other value as the namespace verbatim);
when on it calls activate() (datastore client swap + key-factory patch +
closed-system allowlist + state priming + validator install) and always
installs the production cookie guard via protect().
In modules/__init__.py register the test endpoints — idempotent and
safe to leave in place for production deployments (no-op when test
mode isn't armed):
# modules/__init__.py
import viur.testing
viur.testing.register_modules(globals())
That exposes POST /json/_test/config/status and POST /json/_test/config/finish.
Both endpoints re-verify conf.instance.is_dev_server and the active
datastore database before performing any work — defence in depth on
top of the TestModule.__init__ guard.
If you need more control, the two functions wrap underlying primitives
you can call yourself: viur.testing.activate(database=...),
viur.testing.protect(), plus direct mounting via
from viur.testing._test import TestModule.
Running the dev server with test mode
Toggle test mode at boot by setting the env var that setup() reads.
1 (or true/on) means on with the default namespace; any other
value is the namespace verbatim (VIUR_TESTING=alice); unset / 0 /
off means off:
VIUR_TESTING=1 viur run
Without the env var (or with off), setup() skips activate() and
the process boots against the default database as if the package were
not installed.
When test mode is active, the dev-server boot banner gains two extra
lines — database = … and namespace = … — so the running slice is
visible at a glance. The namespace line is rendered unconditionally;
without a namespace in VIUR_TESTING it falls back to (default),
making it obvious that test mode is armed but namespace isolation is
not in effect:
# With VIUR_TESTING=alice
################## LOCAL DEVELOPMENT SERVER IS UP AND RUNNING ##################
# project = my-viur-project #
# python = 3.13.0 #
# viur = 3.8.25 #
# database = viur-tests #
# namespace = alice #
################################################################################
# With VIUR_TESTING=1 (no namespace)
################## LOCAL DEVELOPMENT SERVER IS UP AND RUNNING ##################
# project = my-viur-project #
# python = 3.13.0 #
# viur = 3.8.25 #
# database = viur-tests #
# namespace = (default) #
################################################################################
Concurrency: sharing one test database across multiple testers
The viur-tests database is a shared GCP resource. If two engineers
both boot a dev server with the same database and run tests at the
same time, their entities will collide — Person A's seed wipes
Person B's user, Person B's test queries find leftovers from Person A.
The fix is the optional namespace argument. ViUR-testing passes it
to google.cloud.datastore.Client(database=…, namespace=…) and rewires
viur.core.db.Key so every read and write in the process is scoped
to that namespace. Different namespaces in the same database are
fully isolated — no separate DB provisioning needed.
Boot each dev server with its own namespace:
# Alice's machine
VIUR_TESTING=alice viur run
# Bob's machine
VIUR_TESTING=bob viur run
# CI for PR #42
VIUR_TESTING=ci-pr-42 viur run
The runner-side require_test_mode can assert the expected namespace
to fail fast when somebody points at the wrong slice::
from viur.testing import require_test_mode
status = require_test_mode(
"http://localhost:8080",
expected_namespace="alice", # omit to skip; pass None for default
)
VIUR_TESTING=1 (or true/on) means "on, no namespace — use the
Datastore default", the right choice for a single-developer setup. Any
other value is the namespace verbatim (VIUR_TESTING=ak → namespace
ak); there is no separator and no reserved names.
Guarded Mode (Playwright only, since @spltz/viur-testing 0.3.0)
For occasional smoke tests on a deployed instance — landing pages,
public catalog browsing, public marketing pages — spinning up a
dedicated test backend is overkill. The Playwright companion package
auto-detects this case: when the backend does not expose
/_test/config/status (HTTP 404), it falls into Guarded Mode —
no test database, no token cookie, no _test/ endpoints. Tests run
as a normal browser would.
Because this bypasses the bilateral guarantee, Guarded Mode demands
a fresh 6-digit PIN confirmation on the terminal every single
run. No persisted ACK, no env-var bypass; no TTY → no run. Specs
that depend on _test/ infrastructure (serverStatus, backendApi,
callTestModule) are auto-skipped in Guarded Mode — only specs
that act as a regular browser actually execute.
See playwright/README.md for the auto-detect table and the full
run-mode behaviour.
Runner-side wiring (Playwright + npm)
The e2e runner ships as the npm package
@spltz/viur-testing
in this same repo (playwright/). Scaffold a working suite next to
your project with:
npx viur-testing-init
The CLI asks interactively for the scaffold mode (Test Mode for a
local dev server armed with VIUR_TESTING=1, Guarded Mode
for tests against an already-deployed instance) and drops a working
package.json, tsconfig.json, playwright.config.ts,
vite.e2e.config.ts, .env.e2e, .gitignore and an example spec.
To skip the prompt, pass --mode test|guarded or --guarded.
In the scaffolded directory:
cd testing/e2e
npm install
npx playwright install --with-deps chromium
E2E_BACKEND_URL=http://localhost:8080 npm test
The generated playwright.config.ts wires createGlobalSetup() /
createGlobalTeardown() which probe /_test/config/status and pick
the mode automatically. In Test Mode the fixtures set the
viur-test-token cookie on the browser context and APIRequestContext; in
Guarded Mode a 6-digit PIN gate appears on the terminal and specs
that depend on /_test/ infrastructure auto-skip.
The Python-side primitives — require_test_mode, finish,
ServerStatus, TestModePreflightError — are still available for
hosts that drive their own runners (Python smoke harness, custom CI
helpers); see the Runner API docs.
Naming
The Python package keeps its repository name viur-testing for stability,
but everything inside it speaks the generic test vocabulary so future
test flavours can be added under the same TestModule umbrella
without churn. The _test URL prefix's leading underscore signals
"system-internal, not for production callers".
Development
git clone git@github.com:sprengplatz/viur-testing.git
cd viur-testing
pip install -e ".[dev]"
pytest # 100% coverage required
mkdocs serve # docs at http://localhost:8000
Documentation
See sprengplatz.github.io/viur-testing.
Changelog
See CHANGELOG.md.
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spltz_viur_testing-0.5.0.tar.gz.
File metadata
- Download URL: spltz_viur_testing-0.5.0.tar.gz
- Upload date:
- Size: 66.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f9bc3baf7a2520db6bc8f37d800e9e5e95a1a94f40bb0fa01b2e84a27a84128
|
|
| MD5 |
417349faff0a20afe87806aa9df39ae7
|
|
| BLAKE2b-256 |
164e111971d847364e5856f605ff170794a1e64be885730960965c1af3b53e23
|
Provenance
The following attestation bundles were made for spltz_viur_testing-0.5.0.tar.gz:
Publisher:
release-python.yml on sprengplatz/viur-testing
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spltz_viur_testing-0.5.0.tar.gz -
Subject digest:
3f9bc3baf7a2520db6bc8f37d800e9e5e95a1a94f40bb0fa01b2e84a27a84128 - Sigstore transparency entry: 1840801038
- Sigstore integration time:
-
Permalink:
sprengplatz/viur-testing@936252684ad617113623faf3bd616eca3cf21bdf -
Branch / Tag:
refs/tags/v0.5.0-py - Owner: https://github.com/sprengplatz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@936252684ad617113623faf3bd616eca3cf21bdf -
Trigger Event:
push
-
Statement type:
File details
Details for the file spltz_viur_testing-0.5.0-py3-none-any.whl.
File metadata
- Download URL: spltz_viur_testing-0.5.0-py3-none-any.whl
- Upload date:
- Size: 47.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b44d90a67c7eca14c52e909da925cf4dde0de9ef3d60c7d778db07d4972a7afe
|
|
| MD5 |
852ae8e18f7a133c70b0166c57680659
|
|
| BLAKE2b-256 |
3d85de8c799dcfa50772e3c10f91175bb806afb00dc44b5c0470fe597a067fdd
|
Provenance
The following attestation bundles were made for spltz_viur_testing-0.5.0-py3-none-any.whl:
Publisher:
release-python.yml on sprengplatz/viur-testing
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
spltz_viur_testing-0.5.0-py3-none-any.whl -
Subject digest:
b44d90a67c7eca14c52e909da925cf4dde0de9ef3d60c7d778db07d4972a7afe - Sigstore transparency entry: 1840801116
- Sigstore integration time:
-
Permalink:
sprengplatz/viur-testing@936252684ad617113623faf3bd616eca3cf21bdf -
Branch / Tag:
refs/tags/v0.5.0-py - Owner: https://github.com/sprengplatz
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release-python.yml@936252684ad617113623faf3bd616eca3cf21bdf -
Trigger Event:
push
-
Statement type: