How to
Testing contracts
A contract is only as good as its refusals. Test both sides of every rule.
In the Workbench
For each action, write at least:
- One accepted step — the normal case.
- One refused step per validation line — break exactly one rule each time, and check the reason names it.
- One step from the wrong signer —
"actor": "someone_else". - One query — that it returns the rows you expect, and none you don't.
A good trial reads like a story:
{
"steps": [
{"action": "Lend", "subject": "drill", "args": {"ref": "drill", "owner": "$julien", "item": "Cordless drill", "borrower": "Ana"}},
{"action": "Lend", "subject": "drill", "args": {"ref": "drill", "owner": "$julien", "item": "Cordless drill", "borrower": "Ben"}},
{"action": "Return", "subject": "drill", "args": {"owner": "$julien"}, "actor": "mallory"},
{"action": "Return", "subject": "drill", "args": {"owner": "$julien"}},
{"action": "Return", "subject": "drill", "args": {"owner": "$julien"}}
],
"queries": [{"name": "Out", "params": {"owner": "$julien", "after": ""}}]
}
Expected: ✓ lent to Ana; ✗ lent twice; ✗ returned by a stranger; ✓ returned; ✗ returned twice; the query returns nothing.
From the command line
dhl-engine compile path/to/contract.dhl # compiles, prints the artifact id
dhl-engine describe path/to/contract.dhl out # interfaces and the description (checks a UI file too)
In a repository
Contracts under contracts/ are compiled by the engine's tests, which also pin every contract's artifact id (a change you did not intend shows up as a changed id) and check that UI files leave artifacts unchanged. Add a test against a real node for contracts with composition (requires) or delegation.
Checklist
- Every action checks
context.signer. - Creating twice is refused (
state.revision == 0). - Each status transition checks the current status.
- Money is in minor units with a currency.
- Merge rules are declared for actions used from several devices.
- Queries filter by owner and page by a stable key.