Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,45 @@ exclude_re = [
# not pay the timeout floor weekly for a verdict already in hand.
"otel\\.rs:931:21:.*\\+= with \\*=",
"otel\\.rs:634:22:.*== with !=",
# The quicksearch byte scanners' cursor hangs, hand-reproduced test by
# test under a 10s timeout: `i *= 1` leaves the index where it is, so
# the loop re-runs the same byte in the same state forever; `i -= 1`
# (`-= 2` past an escape) steps back onto a byte the preceding branch
# advances over again, so the pair cycles (on other inputs it
# underflows, and those tests fail). Each one hangs at least one test
# in the same binary, which then never exits, so no added test can
# turn it into a kill.
"server\\.rs:2044:15:.*\\+= with -=",
"server\\.rs:2052:15:.*\\+= with (-=|\\*=)",
"server\\.rs:2063:15:.*\\+= with \\*=",
"server\\.rs:2078:15:.*\\+= with (-=|\\*=)",
"server\\.rs:2082:11:.*\\+= with (-=|\\*=)",
"server\\.rs:2099:11:.*\\+= with \\*=",
"server\\.rs:2116:15:.*\\+= with (-=|\\*=)",
"server\\.rs:2132:11:.*\\+= with (-=|\\*=)",
"server\\.rs:2144:11:.*\\+= with \\*=",
# The foreign-host skip `at = pos + 1`: `pos - 1` and `pos * 1` both
# re-find the occurrence just rejected, so the scan never passes it
# (embedded_scan_needs_a_domain_boundary_on_the_left hangs).
"guard\\.rs:942:26:.*\\+ with (-|\\*)",
# The stdio delimiter advance `newline + 1` (DESIGN.md): `- 1` and
# `* 1` leave the newline inside `rest`, so the frame loop finds it
# again forever (the multi-frame drain hangs; `- 1` on a bare newline
# underflows instead, in the same binary).
"stdio\\.rs:196:34:.*\\+ with (-|\\*)",
# The glob backtrack advance is unobservable: `p = star_pos` re-enters
# the `'*'` arm and lands on the identical (p, resume, s) state one
# iteration later (or skips the same `'*'` in the trailing-star loop),
# proven by 265k exhaustive + 200k random differential cases with zero
# divergences.
"policy\\.rs:171:26:.*\\+ with \\*",
# Its three advances frozen by `*= 1` — the `'*'` skip (164), the
# backtrack resume (172) and the trailing-star drain (180) — never
# move p or resume, so every glob test with a `*` hangs; reproduced
# by hand like the otel hangs above.
"policy\\.rs:164:15:.*\\+= with \\*=",
"policy\\.rs:172:20:.*\\+= with \\*=",
"policy\\.rs:180:11:.*\\+= with \\*=",
# Typo-shaped ports are refused by exact refusal text the anchor tests
# pin; `>=` for `>` is identical (at equal lengths the byte lookup is
# None), so that one is documented in DESIGN.md, not tested.
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/mutants.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,9 @@ jobs:
# the tier/variant/tag exclusions, policy.rs the glob-advance one,
# client.rs none; the eight small files carry two
# documented-not-tested exclusions (config port >=, custom comma
# collapse) while the stdio delimiter hangs stay in scope and pay
# the floor weekly.
# collapse). Hand-reproduced cursor hangs (server, policy, guard,
# stdio) are excluded like otel's, so a verdict already in hand does
# not pay the floor weekly.
MUTANTS_SCOPE: crates/bugwarden-core/src/guard.rs crates/bugwarden/src/main.rs crates/bugwarden/src/server.rs crates/bugwarden/src/audit.rs crates/bugwarden/src/otel.rs crates/bugwarden-core/src/client.rs crates/bugwarden-core/src/policy.rs crates/bugwarden-core/src/custom_fields.rs crates/bugwarden/src/config.rs crates/bugwarden/src/http_auth.rs crates/bugwarden/src/stdio.rs crates/bugwarden/src/tracing_fields.rs crates/bugwarden/src/http_session.rs crates/bugwarden/src/panic_hook.rs crates/bugwarden-core/src/quoted.rs
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down
20 changes: 20 additions & 0 deletions crates/bugwarden-core/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1047,4 +1047,24 @@ mod tests {
"Debug keeps the status and the code: {own}"
);
}

/// Microseconds are summed across requests and reported as whole
/// milliseconds, so two sub-millisecond waits make one together; a `%`
/// or `*` in the scale reports 300 or 1_300_000 for the same pair.
#[test]
fn upstream_stats_sum_microseconds_into_whole_milliseconds() {
let stats = UpstreamStats::default();
stats.record(Duration::from_micros(600), Some(200));
stats.record(Duration::from_micros(700), None);
assert_eq!(stats.requests(), 2);
assert_eq!(stats.latency_ms(), 1);
assert_eq!(
stats.status(),
None,
"the last request produced no response"
);
stats.record(Duration::from_micros(2_345_678), Some(404));
assert_eq!(stats.latency_ms(), 2_346);
assert_eq!(stats.status(), Some(404));
}
}
7 changes: 5 additions & 2 deletions docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -3957,8 +3957,11 @@ wired, `server.rs` and `main.rs` are the reference.
Display is the unchanged text; the code is read only inside an
`"error": true` envelope (a proxy's non-2xx JSON, a string code and a float
code give none); a gateway's HTML under 502 and a 200 non-JSON body are
typed with no code; and neither the type's Debug nor anyhow's alternate
Debug carries the message.
typed with no code; neither the type's Debug nor anyhow's alternate
Debug carries the message; and `UpstreamStats` sums microseconds across
requests into whole milliseconds (two sub-millisecond waits make one, so
neither a `%` nor a `*` scale passes), the last status winning and a
response-less request reading `None`.
- Unit tests (#[cfg(test)] in crates/bugwarden/src/server.rs): assemble_bug_info
re-classification — a body embargoed after the verdict is refused, a body
that now earns only summary is downgraded, a body granting neither read nor
Expand Down
Loading