Skip to content

Testing modules

Test in three layers: fast native unit tests against a mock host, real executions of the built .hbc with ndsr, and finally calls on the testnet validators.

In native builds (not compiled to Hive Bytecode), the same API runs against an in-process mock host with NDSR semantics (per-module storage, rollback of failed calls, hive.call to registered modules, event validation). Add rlib to crate-type.

#[cfg(test)]
mod tests {
use super::*;
use hivekit::testing;
#[test]
fn ranks_and_rejects_replays() {
testing::reset();
let m = &HIVE_MODULE;
m.invoke("submitScore", json!({"player": "ada", "score": 900, "match": "m1"})).unwrap();
m.invoke("submitScore", json!({"player": "bob", "score": 1200, "match": "m2"})).unwrap();
assert!(m.invoke("submitScore", json!({"player": "bob", "score": 5, "match": "m2"})).is_err());
let t = m.invoke("top", json!(null)).unwrap();
assert_eq!(t["top"][0]["player"], "bob");
assert_eq!(testing::events()[0].name, "score.submitted");
// testing::register_module("0x…", &other::HIVE_MODULE) makes hive.call targets available.
}
}
Terminal window
cargo test

In a normal go test build the host imports are served by an in-memory mock (hivekit.Mock: storage map, recorded events and logs, an OnCall hook for hive.call). InvokeLocal runs a function the way NDSR would, rolling back storage and events when it fails:

func TestSubmit(t *testing.T) {
hivekit.ResetMock()
out, err := hivekit.InvokeLocalJSON("submit", `{"pair":"ETH/USD","source":"a","price":5}`)
// inspect out, err, hivekit.Mock.Storage, hivekit.Mock.Events
}

hive.invoke(name, input) runs a handler in Node against an in-memory store, with the same input/output conventions. hivec run --local does the same from the command line.

hive.invoke_local(name, input) runs a handler in-process; hivec run --local file.py fn '{…}' from the command line.

Mocks do not meter gas or check ABI limits. Always run the built .hbc:

Terminal window
ndsr inspect dist/my_module.hbc # ABI, imports, manifest, address
ndsr run dist/my_module.hbc fn --input '{"…":…}' --gas 5000000
ndsr run dist/my_module.hbc fn --input-file case.json --data-dir ./state # stateful sequence

Things to check:

  • Gas for your largest realistic input, with headroom below the limit you will use (/v1/execute caps at 50 M; committee tasks at the project’s max_gas_limit).
  • Imports: a committee worker must list no storage.* and no hive.call in hivec inspect.
  • Failure paths: invalid input should fail with a clear message ("error": "guest abort: …") and success: false, writing nothing.
  • Determinism: the same input on another machine (or NDSR_ENGINE=pulley ndsr run …, which forces the portable engine) must give the same receipt_hash.

hive.call targets: pass --module other.hbc to hivec run (Rust, -module for Go) or copy the artifact to <data-dir>/modules/<address>.hbc.

  • Upload the module and call it through validators with /v1/execute: a quorum of 3 of 4 must agree, which catches any accidental non-determinism immediately.
  • For a project, POST /v1/developers/projects/{id}/simulations runs up to 10 sample tasks through validator rounds before your project is listed, and returns their receipts. Monitoring