Add interleaved push queues tests (unit/BDD) and docs#473
Conversation
Add a dedicated test suite exercising the connection actor's biased select! loop with interleaved high- and low-priority push queues. These tests verify fairness, rate-limit symmetry across both priority levels, and frame delivery completeness under concurrent traffic. - Introduce 8 unit tests in `tests/interleaved_push_queues.rs` covering rate-limit symmetry, counter-based and time-slice fairness, total throughput caps, and strict priority behaviour. - Add BDD scenarios with supporting fixtures, steps, and scenario definitions under `tests/features/`, `tests/fixtures/`, `tests/steps/`, and `tests/scenarios/` to express the same properties behaviourally. - Update module declarations to include the new test files. - Document the interleaved push queue testing strategy and design decisions in `docs/multi-packet-and-streaming-responses-design.md`. - Annotate the users guide to mention interleaved push queue validation. - Mark roadmap entry 10.3.2 as complete in `docs/roadmap.md`. This commit ensures robust validation that the shared rate limiter enforces identical throughput caps regardless of queue priority and that the fairness mechanisms prevent starvation of low-priority frames during sustained high-priority bursts, with no loss of frames during concurrent usage. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Reviewer's GuideAdds a focused test and documentation scaffold to validate interleaved high- and low-priority push queues: new unit tests around shared rate limiting and fairness in ConnectionActor/PushQueues, new rstest-bdd feature, fixture, steps, and scenarios, plus design/roadmap/users-guide updates and an exec plan documenting constraints, risks, and validation steps. Sequence diagram for interleaved push queue rate limiting in unit testssequenceDiagram
participant UnitTest as unit_test_interleaved_push_queues
participant Runtime as TokioRuntime
participant HighHandle as PushHandle_high
participant LowHandle as PushHandle_low
participant Limiter as RateLimiter
participant Queues as PushQueues
participant ConnActor as ConnectionActor
UnitTest->>Runtime: spawn async test task
Runtime->>HighHandle: push_high_priority(frame_h1)
HighHandle->>Limiter: wait_for_permit()
Limiter-->>HighHandle: grant_token
HighHandle->>Queues: send_high(frame_h1)
Runtime->>LowHandle: push_low_priority(frame_l1)
LowHandle->>Limiter: wait_for_permit()
alt token_bucket_empty
Limiter-->>LowHandle: block_until_refill
Runtime->>Limiter: advance_virtual_time()
Limiter-->>LowHandle: grant_token_after_refill
else tokens_available
Limiter-->>LowHandle: grant_token
end
LowHandle->>Queues: send_low(frame_l1)
UnitTest->>ConnActor: run(out_frames)
loop biased_select
ConnActor->>Queues: recv()
Queues-->>ConnActor: next_frame(high_or_low)
ConnActor->>ConnActor: after_high_or_low(fairness)
end
ConnActor-->>UnitTest: out_frames_contains[h1,l1]
UnitTest->>UnitTest: assert_rate_limit_symmetry()
UnitTest->>UnitTest: assert_no_frame_loss()
Class diagram for interleaved push queue testing and core typesclassDiagram
class PushQueues~F~ {
+high_priority_rx
+low_priority_rx
+build_with_config()
+recv()
}
class PushHandle~F~ {
+push_high_priority(frame)
+push_low_priority(frame)
+try_push(frame)
+wait_for_permit(limiter)
}
class FairnessConfig {
+usize max_high_before_low
+Option_Duration_ time_slice
}
class FairnessTracker {
+usize high_counter
+Instant high_start
+record_high_priority()
+should_yield_to_low_priority(clock)
+reset()
}
class RateLimiter {
+wait_for_permit()
+refill_tokens()
}
class ConnectionActor~F,E~ {
+set_fairness(FairnessConfig)
+run(out_frames)
+next_event()
}
class InterleavedPushWorld {
+PushQueues~u8~ queues
+PushHandle~u8~ high_handle
+PushHandle~u8~ low_handle
+ConnectionActor~u8,unit~ actor
+configure_fairness(FairnessConfig)
+push_high_frames(frames)
+push_low_frames(frames)
+drain_actor()
}
PushHandle~F~ --> PushQueues~F~ : sends_frames_to
PushQueues~F~ --> RateLimiter : uses_shared
ConnectionActor~F,E~ --> PushQueues~F~ : drains_from
ConnectionActor~F,E~ --> FairnessTracker : uses
FairnessTracker --> FairnessConfig : configured_by
InterleavedPushWorld --> PushQueues~u8~ : owns
InterleavedPushWorld --> PushHandle~u8~ : owns
InterleavedPushWorld --> ConnectionActor~u8,unit~ : owns
InterleavedPushWorld --> RateLimiter : shares_with_handles
Flow diagram for BDD interleaved push queue scenariosflowchart LR
Feature["interleaved_push_queues.feature\n(Gherkin scenarios)"] --> Runner["rstest_bdd_runner"]
Runner --> FixtureMod["tests/fixtures/mod.rs\nexports interleaved_push_queues"]
FixtureMod --> FixtureFile["interleaved_push_queues.rs\nInterleavedPushWorld fixture"]
FixtureFile --> World["InterleavedPushWorld\n(connection, queues, handles)"]
Runner --> StepsMod["tests/steps/mod.rs\nexports interleaved_push_queues_steps"]
StepsMod --> StepsFile["interleaved_push_queues_steps.rs\nGiven/When/Then"]
StepsFile --> World
Runner --> ScenariosMod["tests/scenarios/mod.rs\nexports interleaved_push_queues_scenarios"]
ScenariosMod --> ScenariosFile["interleaved_push_queues_scenarios.rs\n#[scenario] wiring"]
ScenariosFile --> World
World --> ConnectionActor["ConnectionActor<u8, ()>"]
World --> PushQueues["PushQueues<u8>"]
World --> RateLimiter["shared RateLimiter"]
ConnectionActor --> PushQueues
PushQueues --> RateLimiter
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
WalkthroughAdd comprehensive testing and documentation for interleaved high- and low-priority push queues, including an execution plan, design updates, test helpers, fixtures, unit tests, BDD feature and scenario wiring, and step definitions to validate fairness, rate-limiting and delivery behaviours. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix: tests/interleaved_push_queues.rs Comment on file async fn fairness_disabled_strict_priority(shutdown_token: CancellationToken) -> TestResult {
let (queues, handle) = PushQueues::<u8>::builder()
.high_capacity(8)
.low_capacity(8)
.unlimited()
.build()?;
let fairness = FairnessConfig {
max_high_before_low: 0,
time_slice: None,
};
push_expect!(handle.push_low_priority(101));
push_expect!(handle.push_low_priority(102));
push_expect!(handle.push_high_priority(1));
push_expect!(handle.push_high_priority(2));
push_expect!(handle.push_high_priority(3));
let mut actor: ConnectionActor<_, ()> =
ConnectionActor::new(queues, handle, None, shutdown_token);
actor.set_fairness(fairness);
let mut out = Vec::new();
actor
.run(&mut out)
.await
.map_err(|e| std::io::Error::other(format!("actor run failed: {e:?}")))?;
assert_eq!(
out,
vec![1, 2, 3, 101, 102],
"all high frames should precede all low frames"
);
Ok(())
}❌ New issue: Code Duplication |
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix: tests/fixtures/interleaved_push_queues.rs Comment on file pub async fn run_strict_priority(&mut self) -> TestResult {
let (queues, handle) = PushQueues::<u8>::builder()
.high_capacity(8)
.low_capacity(8)
.unlimited()
.build()?;
let fairness = FairnessConfig {
max_high_before_low: 0,
time_slice: None,
};
push_expect!(handle.push_low_priority(101));
push_expect!(handle.push_low_priority(102));
push_expect!(handle.push_high_priority(1));
push_expect!(handle.push_high_priority(2));
push_expect!(handle.push_high_priority(3));
let shutdown = CancellationToken::new();
let mut actor: ConnectionActor<_, ()> =
ConnectionActor::new(queues, handle, None, shutdown);
actor.set_fairness(fairness);
self.frames = Vec::new();
actor
.run(&mut self.frames)
.await
.map_err(|e| format!("actor run failed: {e:?}"))?;
Ok(())
}❌ New issue: Code Duplication |
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
This comment was marked as resolved.
…ness helper Extracted common setup and execution code in interleaved_push_queues tests into a single async method `run_actor_with_fairness`. Updated individual test runners like `run_strict_priority`, `run_fairness_threshold`, and `run_interleaved_delivery` to invoke this helper with appropriate fairness configs and setup closures, reducing duplication and improving maintainability. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix: tests/fixtures/interleaved_push_queues.rs Comment on file pub async fn run_strict_priority(&mut self) -> TestResult {
self.run_actor_with_fairness(
FairnessConfig {
max_high_before_low: 0,
time_slice: None,
},
|handle| async move {
push_expect!(handle.push_low_priority(101));
push_expect!(handle.push_low_priority(102));
push_expect!(handle.push_high_priority(1));
push_expect!(handle.push_high_priority(2));
push_expect!(handle.push_high_priority(3));
},
)
.await
}❌ New issue: Code Duplication |
This comment was marked as resolved.
This comment was marked as resolved.
…elper Refactored various interleaved_push_queues tests to use a new async helper function `run_actor_test` which abstracts setup, running the actor with fairness configs, and assertions. This reduces code duplication and improves test readability and maintainability. No behavior changes were made. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
|
@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix: tests/interleaved_push_queues.rs Comment on file async fn rate_limit_symmetric_high_only() -> TestResult {
time::pause();
let (mut queues, handle) = PushQueues::<u8>::builder()
.high_capacity(4)
.low_capacity(4)
.rate(Some(2))
.build()?;
// Burst of 2 should succeed immediately.
push_expect!(handle.push_high_priority(1));
push_expect!(handle.push_high_priority(2));
// Third push should be blocked by the rate limiter.
let mut blocked = handle.push_high_priority(3).boxed();
tokio::task::yield_now().await;
assert!(
blocked.as_mut().now_or_never().is_none(),
"third high push should be pending under rate limit"
);
// Advance past the refill window and push again.
time::advance(Duration::from_secs(1)).await;
push_expect!(handle.push_high_priority(4));
// Drain to confirm frame delivery.
let (_, a) = queues.recv().await.ok_or("recv 1 failed")?;
let (_, b) = queues.recv().await.ok_or("recv 2 failed")?;
let (_, c) = queues.recv().await.ok_or("recv 3 failed")?;
assert_eq!((a, b, c), (1, 2, 4), "high frames delivered in order");
Ok(())
}❌ New issue: Code Duplication |
This comment was marked as resolved.
This comment was marked as resolved.
Extract common logic of rate limiting tests into a helper function `test_single_priority_rate_limit` with a generic push function argument and priority name string. Introduced `drain_three` helper to simplify frame draining and verification. Updated both `rate_limit_symmetric_high_only` and `rate_limit_symmetric_low_only` tests to invoke this helper, reducing code repetition and improving clarity in tests for high and low priority rate limit behavior. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Extract common helpers for interleaved push queue tests into `tests/common/interleaved_push_helpers.rs` to avoid code duplication between unit tests and BDD fixtures. This streamlines test setup and execution, improving maintainability. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/execplans/10-3-2-exercise-interleaved-queues.md`:
- Line 94: Replace the non-possessive phrase "users guide" with the correct
possessive "user's guide" in the document (e.g., the line containing "Stage C:
Documentation — update design doc, users guide, roadmap." and the other
occurrences noted), updating all instances referenced (lines ~94, ~135, ~141) so
the phrase reads "user's guide".
- Around line 269-272: The word "Pre-load" should be corrected to "Preload" in
the documentation; update the occurrences in the description of the interleaved
fairness example for the test/section named
interleaved_fairness_yields_at_threshold (where max_high_before_low = 3 is
mentioned) and the similar occurrence later (around the second mention,
previously at line ~291) so both instances read "Preload" as a single word.
In `@docs/multi-packet-and-streaming-responses-design.md`:
- Around line 772-774: The phrase "parameterised" in the sentence referencing
unit tests and FairnessConfig should use Oxford spelling "parameterized"; update
the documentation line that mentions tests/interleaved_push_queues.rs and the
`ConnectionActor`, `PushQueues`, `FairnessConfig`, and `tokio::time::pause` to
replace "parameterised" with "parameterized" so the docs follow en-GB-oxendict
"-ize" convention.
In `@tests/fixtures/interleaved_push_queues.rs`:
- Around line 99-126: The test run_rate_limit_symmetry currently sets
rate_limit_recovered by checking blocked.as_mut().now_or_never().is_some(),
which treats Some(Err(_)) as success; change the check to explicitly match
Some(Ok(())) to mark recovery and treat Some(Err(e)) as a test failure (panic or
return Err) so only a successful push_unblock is considered recovery; update the
assignment to self.rate_limit_recovered and fail fast on any Err from
handle.push_low_priority to ensure correct semantics for the blocked future.
In `@tests/interleaved_push_queues.rs`:
- Around line 104-120: Replace the two nearly identical tests
rate_limit_symmetric_high_only and rate_limit_symmetric_low_only with a single
parameterised #[rstest] that passes cases for the push function and label to
test_single_priority_rate_limit; specifically, create an rstest with two cases
that supply either the closure that calls push_high_priority and label "high" or
the closure that calls push_low_priority and label "low", then call
test_single_priority_rate_limit(...) in the body once. Keep references to
push_high_priority, push_low_priority and test_single_priority_rate_limit so the
existing helper is reused and remove the duplicate wrapper functions.
- Around line 248-277: The spawned task currently discards
ConnectionActor::run's Result (using let _ = actor.run(...).await and a oneshot)
so test can false-green; change the spawn to propagate errors: either await the
JoinHandle and bubble up actor.run()'s Result (replace oneshot::channel with
using tokio::spawn(...) -> JoinHandle<Result<_, _>> and call .await? to
propagate panics/errors) or have the task send the actual Result via the oneshot
(send(Ok(out)) / send(Err(e))). Ensure actor.run(...)'s result is not ignored
and map or ? the error into the test failure; update references to actor.run,
tokio::spawn, oneshot::channel, tx and rx accordingly.
In `@tests/scenarios/interleaved_push_queues_scenarios.rs`:
- Around line 7-35: The four scenario functions strict_priority,
fairness_threshold, rate_limit_symmetry, and all_frames_delivered currently use
the pattern "let _ = interleaved_push_world;" which triggers
clippy::let_underscore_drop; replace those with an explicit
drop(interleaved_push_world) (or borrow the value if you need it) so the binding
is consumed without a lint; update each function body to call
drop(interleaved_push_world) instead of the let_underscore pattern.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (13)
docs/execplans/10-3-2-exercise-interleaved-queues.mddocs/multi-packet-and-streaming-responses-design.mddocs/roadmap.mddocs/users-guide.mdtests/common/interleaved_push_helpers.rstests/features/interleaved_push_queues.featuretests/fixtures/interleaved_push_queues.rstests/fixtures/mod.rstests/interleaved_push_queues.rstests/scenarios/interleaved_push_queues_scenarios.rstests/scenarios/mod.rstests/steps/interleaved_push_queues_steps.rstests/steps/mod.rs
|
@coderabbitai Have the following now been resolved? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain
🏁 Script executed: Length of output: 24700
✅ Overall Comment 1: Shared Helper ExtractionResolved. The code now has
Both the BDD fixture ( ✅ Overall Comment 2: Error Handling ConsistencyResolved. All test helpers now consistently use String-based error formatting: .map_err(|e| format!("actor run failed: {e:?}"))?No mix of ✅ Individual Comment 1: ExecPlan AcronymResolved. Line 3 of This Execution Plan (ExecPlan) is a living document.The full term is expanded before the acronym on first use, with subsequent mentions using "ExecPlan" alone. ✅ Individual Comment 2: BDD AcronymResolved. Line 37 of the execution plan document now includes the full expansion on first use: new Behaviour-Driven Development (BDD) scenariosLine 50 then correctly uses "BDD" alone after it's been defined. All review feedback has been addressed. The code now has proper helper extraction, consistent error handling, and correctly expanded acronyms in documentation. |
…unified rate-limit tests - Unified single-priority rate limit tests into one parameterized test using `PushPriority` enum. - Replaced legacy push function aliases with closures selecting high- or low-priority pushes. - Added helper `drain_three` to simplify frame draining in tests. - Updated tests for clearer async handling using match and pattern guards. - Minor docs and formatting improvements in test and fixture files. - Retained test semantics to verify rate limiter correctness on both high and low priority queues. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
|
@coderabbitai Have the following now been resolved? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 22592
✅ Overall Comment 1: Shared Helper ExtractionResolved. The file
Both the BDD fixture ( ✅ Overall Comment 2: Error Handling ConsistencyResolved. All test helpers now use consistent String-based error formatting: .map_err(|e| format!("actor run failed: {e:?}"))?No mix of ✅ Individual Comment 1: ExecPlan AcronymResolved. Line 3 of This Execution Plan (ExecPlan) is a living document.The full term "Execution Plan" is expanded before the acronym on first use. ✅ Individual Comment 2: BDD AcronymResolved. Line 37 of the execution plan document correctly includes: new Behaviour-Driven Development (BDD) scenariosThe full term "Behaviour-Driven Development" is expanded before the acronym on first use, with subsequent uses of "BDD" alone. All review feedback has been properly addressed in the current codebase. |
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/execplans/10-3-2-exercise-interleaved-queues.md`:
- Line 200: The document contains the heading/text "Users guide" which should
use a possessive apostrophe; update the string to "User's guide" wherever it
appears in this file (e.g., the heading entry shown in the diff) to maintain
consistency with prior fixes.
ℹ️ Review info
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
docs/execplans/10-3-2-exercise-interleaved-queues.mddocs/multi-packet-and-streaming-responses-design.mdtests/fixtures/interleaved_push_queues.rstests/interleaved_push_queues.rs
Corrected "Users guide" to "User's guide" in the interleaved queues exercise document to reflect proper possessive form. Co-authored-by: devboxerhub[bot] <devboxerhub[bot]@users.noreply.github.com>
Summary
Changes
Stage A: Unit tests scaffold
Stage B: BDD tests scaffold
Stage C: Documentation updates
Stage D: Validation guidance
Rationale / Design decisions
Tests planned / Artifacts
How to validate locally
Risk & mitigation
Acceptance criteria
Related files touched
(13 files changed, 1181 insertions(+), 2 deletions(-))
📎 Task: https://www.devboxer.com/task/c920ccb1-e43f-4251-8823-acd4a1e2369e