Reference · reference

testlab module

4 min read · 2026-09-02 · config-weave 0.1.0

use testlab is the driver API a scenario script runs against a live vmlab lab. A scenario runs host-side, not inside an instance. Its script exports fn run(lab: Lab) -> bool or fn run(lab: Lab) -> Result[bool, string] and compiles in stage-5 validation against the full host API plus this module, so validate catches a broken driver. The module exposes no free functions. It registers the handle types Lab, Machine and RunReport and the plain structs StepResult and ExecOut. Every method that touches a machine returns Result[…, string]. See The testlab for how scenarios fit into a package's tests.

Lab

rust
#[opaque]
struct Lab {}

The handle run receives. It owns the vmlab lab declared by the scenario's lab directory and hands out machine handles. The runner tears the lab down after run returns.

MethodSignatureSummary
machine(name: string) -> Result[Machine, string]bring the declared VM up on first reference and return its handle
log(message: string)print a scenario progress line to the terminal

Machine

rust
#[opaque]
struct Machine {}

A provisioned machine. The config-weave binary is copied into a machine and smoke-tested the first time a method needs it, so a machine that is only ever used through exec never pays for that copy. Resource keys are package.resource, and props is a Value::Map of the resource's properties. A string for a symbol-typed parameter is written into the playbook as that symbol, so "ensure": Value::String("absent") becomes ensure = :absent; a string that is not a valid symbol name (letters, digits and _, not starting with a digit) is an Err. Playbook directories are resolved relative to the scenario's package.

MethodSignatureSummary
name() -> stringthe machine's declared name
exec(cmd: string, args: List[string]) -> Result[ExecOut, string]run a program in the guest
powershell(script: string) -> Result[ExecOut, string]run a script with powershell -NoProfile -NonInteractive, kept off the guest's command line
copy_in(host_path: string, dest: string) -> Result[unit, string]copy a host file or directory into the guest
reboot() -> Result[unit, string]reboot the machine
wait_ready(secs: int) -> Result[unit, string]wait up to secs seconds for the guest to answer
apply_resource(key: string, props: Value) -> Result[StepResult, string]apply one resource in the guest
check_resource(key: string, props: Value) -> Result[StepResult, string]check one resource in the guest
gather(key: string, params: Value) -> Result[Value, string]run one gatherer in the guest and return its value; a refusal is an Err
apply(dir: string) -> Result[RunReport, string]apply the only play of a whole playbook directory in the guest
apply_with(dir: string, opts: Value) -> Result[RunReport, string]as apply; opts is null or {play: "name"}
check(dir: string) -> Result[RunReport, string]check the only play of a whole playbook directory in the guest
check_with(dir: string, opts: Value) -> Result[RunReport, string]as check; opts is null or {play: "name"}

apply and check run the playbook's only play, and fail with an error naming the plays when the playbook declares several or none. To run one play of several, use apply_with or check_with with Value::Map(#{ "play": Value::String("name") }). Any other option key is an Err.

powershell keeps the script off the guest's command line, so the guest's process list and command-line auditing do not see a secret interpolated into it. The script is copied into a fresh working directory in the guest as a file, -Command gets a short bootstrap that reads and runs it, and the file is deleted afterwards, even when the run fails. Exit codes and output behave as they would with -Command <script>, with the return exception described under shell::powershell, and execution policy does not apply. If the delete fails, the script's result is still returned and a warning naming the leftover file is printed to standard error (not in JSON output mode).

RunReport

rust
#[opaque]
struct RunReport {}

The report of a whole-playbook apply or check, queried by step name.

MethodSignatureSummary
ok() -> boolthe run exited zero
step(name: string) -> Result[StepResult, string]the result of one step; an unknown name is an Err

StepResult

rust
struct StepResult {
    status: string,
    message: string,
    ok: bool,
}

The outcome of one step. status is one of not_configured, configured, already_configured, reboot_required, error, skipped or not_run. ok is false only when the step errored, or when the step was missing from the report.

ExecOut

rust
struct ExecOut {
    exit_code: int,
    stdout: string,
    stderr: string,
}

The result of exec and powershell.

rust
use testlab
use value

fn run(lab: Lab) -> Result[bool, string] {
    let dc = lab.machine("dc")?
    let first = dc.apply_resource("windows_domain.forest", Value::Map(#{
        "name": Value::String("corp.example")
    }))?
    if first.status != "reboot_required" { return Ok(false) }
    dc.reboot()?
    dc.wait_ready(600)?
    let second = dc.apply_resource("windows_domain.forest", Value::Map(#{
        "name": Value::String("corp.example")
    }))?
    lab.log("forest converged")
    Ok(second.status == "already_configured")
}