Skip to main content

Module contract_tests

Module contract_tests 

Source
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_message and the step() path that pops a single input and delegates to process_message.
  • Batched behaviour: step_batch() semantics for fixed-size, sliding and fixed+max_delta_t windowing, including span validation based on MessageHeader::creation_tick.
  • Error mapping: QueueErrorStepResult / NodeError rules and how enqueue results map to progress/backpressure semantics.
  • Telemetry and occupancy hooks exercised via EdgeLink + GraphTelemetry.

§Usage

Implementors should provide:

  • a NodeLink factory: || -> NodeLink<N, IN, OUT, InP, OutP> that returns a fresh NodeLink owning the concrete node under test, and
  • ensure payload types implement Default + Clone so tests can construct Message::<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: initializestarton_watchdog_timeoutstop.
run_model_batching_tests
Model-node batching smoke-test.
run_process_message_enqueues_and_made_progress
Single-message process_message path 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_t span 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 return StepResult::NoInput.
run_step_pops_and_calls_process_message
step() pops one message and delegates to process_message.