Expand description
Node contract test fixtures and helpers.
This module contains a reusable, single contract test-suite that validates
the Node trait behaviour expected by Limen runtimes. The tests exercise:
- Node lifecycle:
initialize,start,on_watchdog_timeout,stop. - Per-message behaviour:
process_messageand thestep()path that pops a single input and delegates toprocess_message. - Batched behaviour:
step_batch()semantics for fixed-size, sliding and fixed+max_delta_t windowing, including span validation based onMessageHeader::creation_tick. - Error mapping:
QueueError→StepResult/NodeErrorrules and how enqueue results map to progress/backpressure semantics. - Telemetry and occupancy hooks exercised via
EdgeLink+GraphTelemetry.
§Usage
Implementors should provide:
- a
NodeLinkfactory:|| -> NodeLink<N, IN, OUT, InP, OutP>that returns a freshNodeLinkowning the concrete node under test, and - ensure payload types implement
Default + Cloneso tests can constructMessage::<P>::new(Header, P::default())values.
Then call the test macro from your crate tests:
run_node_contract_tests!(my_node_contracts, { make_nodelink: || create_mynode_link() });The suite is adaptive: it skips or changes assertions for IN == 0
(sources), OUT == 0 (sinks) or when node.node_kind() indicates a
wrapper-specific implementation (Source, Sink, Model). The tests only
assert node-level semantics (counts/StepResult/telemetry) and do not
inspect or require node-specific payload contents.
Planned node/context tests — NOT YET IMPLEMENTED
C2 (N→M node arity — planned/C2.md): When C2 lands (multiple input ports, multiple output ports, optional inputs):
- run_fan_in_two_inputs_both_available: step() with two filled input ports triggers the node and both tokens are consumed.
- run_fan_in_one_input_absent_blocks: with an optional-absent policy, step() can still fire on one input.
- run_fan_out_two_outputs: a single step produces messages on two distinct output ports; both queues gain one item.
- run_fan_out_one_backpressured_maps_to_backpressure: if output port 1 is at capacity and output port 0 succeeds, the step returns Backpressured.
R1 (urgency ordering — planned/R1.md):
- run_step_pop_respects_urgency_order: fill input queue with messages of mixed QoSClass; step() must process the highest-urgency item first.
R2 (freshness / expiry — planned/R2.md):
- run_step_skips_expired_inputs: push a message whose deadline is already elapsed; step() must not pass it to process_message (or must decrement a stale counter and skip).
R4 (mailbox semantics — planned/R4.md):
- run_step_mailbox_overwrites_previous: rapid pushes to a mailbox-mode input leave only the most recent; step() sees only the latest value.
R5/R6 (liveness policies — planned/R5.md, R6.md):
- run_step_liveness_check_fires_on_timeout: if no input arrives within the configured liveness interval, the node must emit a watchdog event or return a liveness-timeout StepResult.
P1 (PlatformBackend / controllable clock — planned/P1.md):
- run_batch_delta_t_with_controllable_clock: inject a fake clock that advances in discrete steps; verify delta_t batching respects injected timestamps rather than wall time.
RS1 (runtime lifecycle — planned/RS1.md):
- run_stop_during_inflight_batch: call stop() while a batch is partially consumed; verify all tokens are freed and no manager slots leak.
Functions§
- run_
initialize_ start_ stop_ roundtrip - Lifecycle:
initialize→start→on_watchdog_timeout→stop. - run_
model_ batching_ tests - Model-node batching smoke-test.
- run_
process_ message_ enqueues_ and_ made_ progress - Single-message
process_messagepath and basic egress semantics. - run_
push_ output_ drop_ oldest_ evicts_ oldest_ once - Regression: push_output with DropOldest must evict exactly once when the output queue is between soft and hard cap (Evict(1) decision), not twice.
- run_
push_ output_ evict_ until_ below_ hard_ no_ double_ eviction - Regression: push_output with DropOldest at hard cap (EvictUntilBelowHard) must not double-evict when the caller’s pre-eviction leaves the queue above soft.
- run_
push_ output_ no_ token_ leak_ on_ backpressure - push_output must free the manager slot when output is backpressured (DropNewest). A leaked token would exhaust manager capacity and eventually panic on store. Uses MemoryManager::available() before and after to verify the invariant.
- run_
sink_ specific_ tests - Sink-node specific checks.
- run_
source_ specific_ tests - Source-node specific checks.
- run_
step_ batch_ fixed_ n_ disjoint step_batch()with fixed-N disjoint semantics.- run_
step_ batch_ fixed_ n_ max_ delta_ t_ tests - Tests for
fixed_n + max_delta_tspan validation. - run_
step_ batch_ sliding_ window step_batch()with sliding-window semantics (smoke test).- run_
step_ maps_ backpressure_ and_ errors - Mapping of output backpressure and queue errors to node-level results.
- run_
step_ on_ empty_ returns_ noinput step()on empty inputs must returnStepResult::NoInput.- run_
step_ pops_ and_ calls_ process_ message step()pops one message and delegates toprocess_message.