Testing
Testing your engine
Test engine logic without the platform — unit-test your tool code, run the container against a mock context, and validate your manifest before you publish.
You don't need a running platform to build an engine with confidence. Test at three levels — your tool logic, the packaged container, and the manifest — then do one live run.
1. Unit-test your tool logic
Keep the actual work in plain functions and have your entrypoint call them. Pure functions test without Docker, the platform, or a token:
# transform.py
def summarise(rows: list) -> dict:
return {"rows": len(rows), "first": rows[0] if rows else None}
# tests/test_transform.py
from transform import summarise
def test_summarise():
assert summarise([1, 2, 3]) == {"rows": 3, "first": 1}
When a test needs the run context (but not the network), build one with VerentisEngine.initialize
— no /verentis mount required:
from verentis import VerentisEngine
def test_reads_parameters():
engine = VerentisEngine.initialize(
api_url="https://api.localtest.me:6500",
access_token="test-token",
workspace_id="ws-test",
file_path="/scripts/transform.py",
)
assert engine.context.file.path == "/scripts/transform.py"
Avoid calling engine.files / engine.http in unit tests — those hit the gateway. Test the logic
in isolation; cover file/HTTP interaction with the mock-context container run below, or a live run.
2. Run the container with a mock context
The platform mounts /verentis/context.json, your engine runs, and it writes /verentis/result.json
(and optional progress.json). Reproduce that locally by mounting a /verentis directory:
mkdir -p .local/verentis
cp examples/context.json .local/verentis/context.json # edit to taste
docker run --rm -v "$PWD/.local/verentis:/verentis" my-engine:dev
cat .local/verentis/result.json
A minimal mock context.json (see the Python SDK reference for every
field):
{
"version": 1,
"execution": { "id": "exec-local", "mode": "request-response", "tool": "execute", "trigger": "user", "timeout": 300 },
"workspace": { "id": "ws-local", "name": "Local" },
"file": { "path": "/examples/input.txt", "name": "input.txt", "mimeType": "text/plain", "branch": "main" },
"input": { "arguments": [], "environment": {}, "parameters": { "message": "hello" } },
"platform": { "apiUrl": "http://localhost:5000", "token": { "accessToken": "local-dev-token", "scopes": ["node.file.read"] } }
}
File and HTTP calls in a mock run target platform.apiUrl with platform.token.accessToken. Point them
at a real (or dev) gateway with a valid token to exercise engine.files / engine.http end-to-end;
otherwise keep the run to logic that doesn't touch the platform.
3. Validate your manifest
Before publishing, sanity-check engine.yaml. A quick structural check catches the common mistakes:
# validate_manifest.py — usage: python validate_manifest.py engine.yaml
import sys, yaml
REQUIRED_META = ("name", "display-name", "version")
def validate(path: str) -> list[str]:
doc = yaml.safe_load(open(path))
errors = []
if doc.get("kind") != "ExecutionEngine":
errors.append("kind must be 'ExecutionEngine'")
meta = doc.get("metadata") or {}
errors += [f"metadata.{k} is required" for k in REQUIRED_META if not meta.get(k)]
spec = doc.get("spec") or {}
if not (spec.get("runtimes") or {}):
errors.append("spec.runtimes must declare at least one runtime (docker and/or wasm)")
if not (spec.get("file-types") or []):
errors.append("spec.file-types should declare at least one pattern")
for tool in spec.get("tools") or []:
if not tool.get("name"):
errors.append("every spec.tools[] entry needs a name")
return errors
if __name__ == "__main__":
problems = validate(sys.argv[1])
print("\n".join(problems) if problems else "OK")
sys.exit(1 if problems else 0)
Checklist the platform enforces at registration time:
A resolvable runtimespec.runtimes.docker.image (or a wasm module) that the platform can actually pull/load.
Permissions match what you callReads need node.file.read; writing/deleting files needs the VFS authoring cascade. Request only what
you use — see Scopes.
Tools dispatch cleanlyEach spec.tools[].name must match a branch your entrypoint handles on
engine.context.execution.tool.
4. Debug against a REAL workspace with the CLI
Mock-context runs cover logic, but the fastest way to develop platform-touching code is the CLI's dev
loop: run your script locally — same interpreter, your editor, your breakpoints — while
engine.files / engine.http hit a real workspace with a scoped token identical to a platform run.
cd my-engine-project # contains engine.yaml (spec.permissions drives the token scopes)
# One-shot run with real workspace access:
verentis dev run transform.py --workspace <id> --params '{"message":"hello"}'
# Attach a debugger — the script waits for you before the first line:
verentis dev run transform.py --debug # debugpy on :5678 with --wait-for-client
verentis dev vscode # writes .vscode/launch.json (attach + launch configs)
dev run mints a workspace-bound access token (scopes from your manifest's spec.permissions, or
--scopes / --boundary /some/dir for a node-bounded token like platform runs get), writes a real
.verentis/context.json matching the execution contract, and spawns
your script with the full VERENTIS_* environment — the SDK auto-loads it with zero code changes.
Prefer the building blocks directly?
verentis dev token --workspace <id> # print a scoped token
verentis dev context --file /scripts/transform.py # write .verentis/context.json
export VERENTIS_CONTEXT_PATH=$PWD/.verentis/context.json
python -m debugpy --listen 5678 --wait-for-client transform.py
.verentis/context.json contains a live (short-lived) access token — add .verentis/ to your
.gitignore. Tokens expire; re-run verentis dev run/dev context when you see a 401.
Validate your manifest with the CLI instead of hand-rolled scripts: verentis validate engine.yaml.
Need local files instead of a workspace?
verentis dev run always targets a real workspace. For a fully offline process, call
engine.configure_local(files_root=..., runtime_dir=..., setting_aliases=...) from a separate
local-only runner, then execute that runner with ordinary Python. Do not put this call in the
production entry point you package for Verentis. In offline mode engine.files uses the confined
local filesystem and engine.settings reads explicitly mapped environment variables. See the
Python SDK reference.
5. Test the in-browser (WASM) path
If your engine ships a client runtime (spec.runtimes.wasm.adapter), verify it in the browser too:
Build and upload the worker
Build your adapter and upload it to the VFS path your manifest declares (e.g.
/applications/<name>/runner.js), alongside the manifest.
Run from a directory
Client runs get a scoped token only when the platform can bind one to a launch node — run a directory program (or a file with a parent folder), not a lone root-level file.
Verify the SDK
Confirm engine.files / engine.http work in-browser exactly as they do server-side — same API, backed
by the scoped token.
Next
Push your image, upload the manifest, and make your engine available.