Skip to content

Mutation testing: message assembler budget and expiry survivors #595

Description

@leynos

Context

From the first complete full mutation-testing run (ADR-007 shared workflow, run 29074771985, 2026-07-10, main @ 94a5d44). The message-assembler budget checks are asserted only strictly over their limits, and the wall-clock purge path is never exercised.

Surviving mutants

  • src/message_assembler/budget.rs:51replace > with >= in check_aggregate_budgets (connection branch)
  • src/message_assembler/budget.rs:96replace > with >= in check_size_limit
  • src/message_assembler/state.rs:200replace > with >= in MessageAssemblyState::accept_first_frame_at
  • src/message_assembler/state.rs:366replace MessageAssemblyState::purge_expired -> Vec<MessageKey> with vec![]
  • src/app/codec_driver.rs:87replace FramePipeline::purge_expired with ()

Analysis

Boundary-equality cases are untested: no test fills the connection budget exactly to its limit and expects acceptance (the in-flight branch is covered by headroom_reclaimed_after_completion_allows_new_frame), no assembly completes at exactly the size limit, and no first frame declares exactly the limit. The no-argument purge_expired() (wall-clock) is never called by tests — every test and the BDD world use purge_expired_at(now) with a synthetic clock — and FramePipeline's inline tests only build pipelines with fragmentation = None, so its purge_expired delegation is never exercised.

Suggested tests

  1. Connection budget of 20; submit a first frame with a 20-byte body and assert acceptance (buffered_count == 1, total_buffered_bytes == 20).
  2. Size limit of 10; first frame body 5 plus continuation body 5; assert the message completes.
  3. Size limit of 10; first frame declaring total_body_len = Some(10) with empty metadata; assert accept_first_frame returns Ok.
  4. State with Duration::ZERO timeout and one buffered frame; call the no-argument purge_expired() and assert it returns the key and empties the state.
  5. FramePipeline::new(Some(config)) with reassembly_timeout = Duration::ZERO; inject a partial reassembly, call pipeline.purge_expired(), and assert the reassembly state is cleared.

Accepted

  • src/message_assembler/budget.rs:25 (is_enabled -> true) is equivalent: the gate is a hot-path optimization and check_aggregate_budgets returns Ok(()) when both budgets are None.
  • src/app/frame_handling/assembly.rs:86 (delete !) gates a debug! line only; the purge itself is unconditional.
  • src/app/inbound_handler.rs:171 (></==/>=) gates a clamp warning only; the clamp at line 170 is unconditional.

Metadata

Metadata

Assignees

No one assigned

    Labels

    mediumCould be disruptive, but might not happentesting

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions