metamon/command
Stateful / model-based testing primitives.
A Command(model, real) represents one transition that can be
applied to two parallel worlds:
- model: a pure description of the expected state (typically
a record or a
Dict). Thenext_modelstep says how the command updates this expected state. - real: the system under test (a database connection, a
stateful module, an in-memory cache). The
runstep performs the command on the real world and returnsOk(Nil)on success orError(reason)when an invariant is violated.
The precondition decides whether the command can fire in the
current model state; the runner skips commands whose preconditions
are false.
See metamon/stateful for the runner that drives a list of
commands against an initial (model, real) pair.
Types
A single transition in a model-based test.
pub type Command(model, real) {
Command(
name: String,
precondition: fn(model) -> Bool,
next_model: fn(model) -> model,
run: fn(real) -> Result(Nil, String),
)
}
Constructors
-
Command( name: String, precondition: fn(model) -> Bool, next_model: fn(model) -> model, run: fn(real) -> Result(Nil, String), )
Values
pub fn always(
name name: String,
next_model next: fn(model) -> model,
run run: fn(real) -> Result(Nil, String),
) -> Command(model, real)
Synonym for no_precondition. The name reads as “always runs”,
which overstates the contract — the command’s run can still
return Error, halting the sequence. Prefer no_precondition in
new code; this constructor is retained as an alias so existing
call sites continue to compile.
pub fn new(
name name: String,
precondition pre: fn(model) -> Bool,
next_model next: fn(model) -> model,
run run: fn(real) -> Result(Nil, String),
) -> Command(model, real)
Construct a command. name is shown in failure reports.
pub fn no_precondition(
name name: String,
next_model next: fn(model) -> model,
run run: fn(real) -> Result(Nil, String),
) -> Command(model, real)
Construct a command with no precondition — the precondition arm is
fixed to fn(_m) { True }, so the command’s gating step is a
no-op.
“No precondition” is not the same as “always runs”: if the
command’s run returns Error(reason), the runner halts the
sequence with Failed, and any later commands in the list are
skipped. Use command.new when the command should be gated on the
current model state.
command.always is a synonym for no_precondition kept for
backward compatibility; new code should prefer this name because
it reads correctly at the call site.