Verentis

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 runtime

spec.runtimes.docker.image (or a wasm module) that the platform can actually pull/load.

Permissions match what you call

Reads need node.file.read; writing/deleting files needs the VFS authoring cascade. Request only what you use — see Scopes.

Tools dispatch cleanly

Each 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.

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

Publishing & distribution

Push your image, upload the manifest, and make your engine available.