Repository navigation
test(harness): destructive suites refuse to FLUSHALL a server they do not own (#292) - #300
Merged
Merged
Conversation
…292) Dragonfly df-v1.40.1 reports no run_id in INFO server (checked live), so the recorded claim could never match on a re-run and a non-empty Dragonfly always refused. INFO replication's master_replid is per-instance random and serves the same purpose.
…atabase (#292) Review of 556914d found three ways the guard could be bypassed from INSIDE. All three shared one cause: the emptiness probe was narrower than the destruction, and every way the probe could fail was coerced to "empty". 1. A server that was still starting made `claim_resp_instance` return early ("an unreachable server cannot be damaged"). It can: the suite's own `wait_for_*()` then waits up to 10-30s, the server comes up, and the `OnceLock` means the claim is never retried. Reproduced live: seeded container, `docker start` fired 3s into the run -> `test ... ok`, corpus destroyed, no claim file written. The probe now retries for 30s (>= the longest `wait_for_*` deadline) and REFUSES if it never becomes reachable. 2. `DBSIZE`/`FT._LIST` errors were `.unwrap_or(0)`, i.e. `Fresh`. Reproduced live with `ACL SETUSER default -dbsize` (FLUSHALL still permitted): guard read 0 keys, 3 keys destroyed. Probe failures are now `Probe::Inconclusive`, which `claim_verdict` refuses on, and a server replaying its RDB (`INFO persistence` -> `loading:1`) is refused too. 3. `DBSIZE` counts one database while `FLUSHALL` destroys all of them. Reproduced live: 3 keys in db 1, empty db 0 -> `test ... ok`, db 1 wiped. The probe now sums `INFO keyspace` across every `dbN:` line. Also: INV-P3's scan matched the literal `cmd("FLUSHALL")`, which a synthetic wiping suite evaded three ways (`FLUSHDB`, a rustfmt-wrapped call, and `Cmd::new().arg(..)`) with no claim call at all; it now matches the quoted command name and covers FLUSHDB. INV-P2 now checks the call's ARGUMENTS. INV-P1 also reads `.unwrap_or_else(|| N)`. The waiver prints a warning straight to the process stderr, because libtest only replays `eprintln!` for FAILING tests. The refusal message no longer asserts "this harness did not create" (false when your own container is restarted) and now names the claim path and distinguishes a missing claim from a stale one.
…eplayed refusal (#292) Third review round. Three should-fixes, each reproduced against 7ad5e51 first. 1. The wipe scan was case-sensitive. Redis command names are case-insensitive on the wire (`redis-cli flushall` on redis:8.8.0 takes a server 2 keys -> 0), so a synthetic wiping suite with NO claim call passed all 23 invariants spelled `cmd("flushall")`, `cmd("FlushAll")` or `cmd("flushdb")`. Round two widened the matcher from one call spelling to "the quoted command name" and never mutated case — the same guards-one-notch-weak pattern, inside the fix for the previous instance of it. The matcher now uppercases the source and accepts either quote style, which also catches a Lua `EVAL "redis.call('flushall')"`. 2. The scan never read tests/common/mod.rs. Moving the FT._LIST drop loop + FLUSHALL into a shared `pub fn wipe_server()` — the LIKELY refactor, since this guard's premise is that four suites share one flush_db() shape — left all 23 invariants passing with no claim required. New INV-P5 forbids a wipe command in tests/common/, because every suite includes `mod common;` and so a wipe there is invisible to the per-suite scan. 3. `OnceLock::get_or_init` does not memoize a panicking initializer, so after a refusal every later test re-ran the whole 30 s reachability loop. Measured against a dead port: 2 tests took 60.21 s; the 44-test suite would have taken ~22 min to report "server not available". The memoized value is now the VERDICT and the panic happens outside the initializer: the same 2 tests take 30.07 s and the full 44-test suite 30.17 s. Also: `sum_keyspace_keys` returns Option<i64> so it can say "cannot tell" rather than 0 (missing `# Keyspace` header, a dbN: line without a usable `keys=`, negative, or overflow) — 0 meant Fresh, this guard's own bug class one notch in. INV-P1 now sees through Rust digit separators (`6_399`). Documented that the loading check is best-effort and inert on kividb v1.0.2-full, which reports no `loading` field (measured). Fixed a doc typo in integration_dragonfly.rs.
…e, print the right container port (#292) Claims audit at b5c8b32 found one shipped sentence that describes a mechanism that does not exist, plus four smaller overstatements. BLOCKER. Every refusal said "Editing the port literal in tests/<suite>.rs is rejected by tests/harness_invariants.rs". It was not. INV-P1 rejects a SECOND literal; `default_port()` re-reads whatever is on the `.unwrap_or(..)` line, so editing the single one from 6399 to 7911 left all 27 invariants green — the exact bypass incident 1 in #292 used. And the test policing the message only asserted the string "harness_invariants" was PRESENT, never that it was true. Fixed by making the sentence true rather than by weakening it: new INV-P6 pins each wiping suite's default to the host port tests/docker-compose.test.yml publishes for it, parsed from the compose file so the two cannot drift apart in either direction. The message now cites that mechanism, and the message test asserts the citation, not just the filename. SF4. The refusal hardcoded `docker run -p <your-port>:6379`. KiviDB listens on 6380 (compose maps 6386:6380), so an operator following its refusal verbatim got a container the suite cannot reach and then a second refusal from this same guard. The container port is now per-suite data on `claim_resp_instance`, checked against compose by INV-P2 and asserted for kividb by the message test. SF2. `is_standalone_number` treated `_` as a boundary, so INV-P1 flagged `let foo_6399 = 1;` — a line with no port literal — and the shipped comment claiming otherwise was false. Identifier characters now disqualify a match, with a negative control that also proves the scanner still catches a real literal on the same shape of line. SF3. `CLAIM_OUTCOME`'s doc cited "60.14 s instead of 20.13 s". 20.13 s is not reproducible and is structurally impossible — CLAIM_REACHABLE_WAIT is 30 s either way. Replaced with the measured pair, 60.21 s -> 30.07 s, and the full suite's 30.17 s. SF5. The file header said "Four invariants" while shipping five; it now lists all six, and the CI step comment no longer says "INV-P1/P2/P3". NIT 6. integration_valkey.rs and integration_kividb.rs still said "state this harness did not create" — the phrasing argued false in the previous round and which the message test forbids in the message itself.
…d one directory level (#292) Mutation campaign at b5c8b32. Sixteen mutants were caught; these two were not. BLOCKER 1. The strongest evasion was not a spelling of the flush but a spelling of the CLAIM: comment it out. INV-P2 matched `squeeze(body).contains(..)` against raw source, while INV-P1's `bare_port_hits` had skipped `//` lines since round 1 — same file, one dimension short. Live A/B on a seeded redis:8.8.0 (1 key db0, 1 key db1, 1 FT index): pristine b5c8b32 refuses and leaves the server intact; with one `/` prepended to the claim line, harness_invariants still reports 27 passed, the suite reports `test result: ok`, and db0, db1 and the index are all gone. Fixed by comparing the WHOLE `test_port()` body — comments stripped — against the single shape it is allowed to have, instead of asking whether it contains a call. Equality also closes `if false { .. }` and `stringify!(..)`, pins the arguments and the container port, and makes `squeeze`'s rustfmt tolerance load-bearing (an identity `squeeze` no longer matches). The comment stripper leaves string literals alone so `redis://` survives, with a control for it. BLOCKER 2. INV-P5 — round 3's headline fix — used `read_dir`, which reads one level, so a wipe helper at tests/common/wipe/mod.rs evaded it entirely: the invariant re-evaded in its own idiom. The walk now recurses and the scanned file set is pinned rather than asserted `> 0`. Also: * INV-P4 only ever exercised claim_verdict, the CONSUMER. A `probe_server` returning `Reachable { keys: 0, index_count: 0 }` for an unreachable server — the exact regression INV-P4's docstring cites — passed everything, as did swallowing the panic, forcing the waiver on, and shortening the wait. No integration suite can catch these: a permissive mutation makes them pass harder. `probe_server` is now `pub` and asserted `Inconclusive` against a closed port at zero wait, plus source pins that every early return is `Inconclusive`, that `Probe::Reachable` is constructed exactly once and last, that `claim_resp_instance` panics, that the waiver defaults to off, and that CLAIM_REACHABLE_WAIT >= the longest `wait_for_*()` deadline. * `Fresh` whenever `keys == 0` was unpinned, though `flush_db()` drops indexes and the refusal counts them: 0 keys + 1 index must now refuse. * `\"FLUSHALL\"` — the natural Rust spelling of a Lua payload — was not matched, making the shipped "either quote style" claim false. `\` is now a quote boundary, with a positive control. * New INV-P7 pins the set of test targets, so a wiping suite named outside `integration_*.rs` cannot slip past the INV-P3 scan unclassified.
fcostaoliveira
added a commit
that referenced
this pull request
Aug 9, 2026
Merged, not rebased: the branch is published and the standing rule here is to add a commit rather than force-push.
fcostaoliveira
added a commit
that referenced
this pull request
Aug 9, 2026
Merged, not rebased: the branch is published and the standing rule here is to add a commit rather than force-push. Genuinely quiet this time, and built rather than assumed after the last merge was textually clean and did not compile. `build_search_result_json` is untouched by #300, and all 23 test targets compile. Checked rather than assumed: * #300's production diff is empty, confirmed directly (`git diff e99d2b5..7899014 -- src Cargo.toml Cargo.lock build.rs`); * INV-P7 pins the set of `tests/*.rs` targets — this branch adds none, and `harness_invariants` passes 38/38 on the merged tree; * the two source scanners are disjoint by construction and cannot disagree about a file: mine walks `src/`, #300's walks `tests/`. Now stated in guard 1's docstring, because a later "unification" is the obvious wrong move. `tests/` is deliberately outside my scan: test code legitimately reads the environment (`harness_invariants.rs` has nine such reads, `common/mod.rs` two), none of it is compiled into a binary, and nothing there can put a value in an artifact. Baselines derived in an isolated worktree, all three matching: master 7899014 = 937 unit (129 lib + 2 generate_dataset + 806 main), `harness_invariants` 38, `overhead_invariants` 9. Branch = 1006 unit, +69 / -0, with `harness_invariants` and `overhead_invariants` unchanged since this branch adds nothing under `tests/`.
11 tasks done
This was referenced Sep 23, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #292.
The defect
Four integration suites share one
flush_db()shape — drop every indexFT._LISTreports, then
FLUSHALL— and each defaults to a port fromtests/docker-compose.test.ymlthat several sessions share. TheREDIS_TEST_PORTstyle override existed, but the default was the shared instance, so the
protection only helped whoever remembered to set it. Two incidents followed:
sedagainstconst TEST_PORT: u16 = 6399;matched nothing (the constant had become
fn test_port()), exited 0, and therun proceeded against the shared server. No error, no output difference.
docker run -p 6399:6379failed because the shared container already heldthe port, so the writes landed in the shared server.
The common shape is an override that looks applied but isn't, on a shared
instance whose teardown path is destructive.
What this PR does
1 — refuse to run against an instance the harness has no claim for
(
tests/common/mod.rs:claim_verdict/claim_resp_instance). On first use adestructive suite probes the server. Unless it can positively establish the
server is safe, every test in the suite fails with an explanatory panic.
The claim lives inside
fn test_port(), not insideflush_db(), becausetest_port()is the one place every path to the server resolves through: thedirect
redis::Clienthelpers and theREDIS_PORT-style value handed tospawned benchmark binaries.
The probe reads
INFO persistence(loading),INFO keyspace(keys across alldatabases) and
FT._LIST. Every way it can fail — unreachable, stillloading, a denied or unsupported command, an unparseable reply — produces
Probe::Inconclusive, whichclaim_verdictrefuses on.Probe::Reachableisconstructed at exactly one place: the last statement of
probe_server, afterboth
INFO keyspaceandFT._LISTreturnedOk.Identity is
run_idfromINFO server, falling back tomaster_replidfromINFO replication(dragonfly df-v1.40.1 reports norun_id). The claim isrecorded in a file under the test binary's target directory
(
<target>/vdbb-test-claims/<suite>-<host>-<port>.claim), not as a key on theserver —
integration_valkeyasserts exactDBSIZEvalues, so a server-sidemarker key would have broken it. Escape hatch:
VDBB_TEST_ALLOW_DIRTY=1, whichwarns on every run.
2 — single-source the port, enforced by a test (
tests/harness_invariants.rs,a new pure test target wired into the container-free CI job next to
overhead_invariants). Seven invariants: one source of truth per port (P1); every server-wiping suite'stest_port()matches the one allowed shape, claim call and arguments included(P2); the wipe scan's suite set is pinned (P3); the decision fails closed, and so
does the probe that feeds it (P4); no shared helper under
tests/common/, at anydepth, may wipe a server (P5); each suite's default port equals the port
docker-compose.test.ymlpublishes for it (P6); and the set of test targets ispinned so a new one must be classified (P7).
Not chosen: a randomised default port (CI and
docker-compose.test.ymlcould nolonger name the port, and it does nothing about a stale override spelling), and a
marker key on the server (breaks valkey's
DBSIZEassertions).The single clearest demonstration
A Redis holding 20 000 000 keys, restarted so it replays its RDB, with the
suite fired during the replay. At
556914d(the first version of this guard):Run 1 fails — but at
wait_for_redis(), not at the guard.DBSIZEhaderrored with
-LOADING,.unwrap_or(0)turned that into "0 keys", the verdict wasFresh, and a claim was minted for that server. Once the load finished:Twenty million foreign keys destroyed on the second run, reported as
ok. On thisbranch, same container, same sequence — refused twice, nothing minted, nothing
lost:
Review round 2 — three bypasses INSIDE the guard
All shared one cause: the probe was narrower than the destruction, and every way
it could fail was coerced to "empty". Each reproduced live, before and after.
B1 — skipped while the server was starting. The guard returned early when it
could not connect ("an unreachable server cannot be damaged"). False: the suite's
own
wait_for_*()then waits 10-30 s, the server comes up, and theOnceLockmeans the claim is never retried. RED at
556914d— seeded container,docker startfired 3 s in:GREEN, same race:
REFUSING …,keyspace=db0:keys=3,FT._LIST=[prod_idx ]. Theprobe now retries for 30 s (>= the longest
wait_for_*()deadline in the guardedsuites) and refuses if the server never becomes reachable.
B2 — probe errors swallowed. RED at
556914dwithACL SETUSER default -dbsize(FLUSHALLstill permitted):test … ok, 3 keys destroyed. GREEN twice:with
-dbsizethe probe no longer uses it and refuses onINFO keyspace; with-infodenied it refuses with`INFO keyspace` failed … (NoPerm: … 'info' command), so the number of keys at risk is unknown.B3 —
DBSIZEmeasures one database,FLUSHALLdestroys all. RED at556914d, 3 keys in db 1 and an empty db 0:test … ok,after: db0=0 db1=0.GREEN: refuses,
db0=0 db1=3. The probe sumsINFO keyspaceover everydbN:line, parsing
keys=by name because the layouts differ — all captured liveand pinned, including both empty-server shapes (redis/valkey/kividb omit the db
line; dragonfly still emits
db0:keys=0).Also in that round: INV-P3's scan was widened from one call spelling to the quoted
command name; INV-P2 gained an argument check; INV-P1 learned
.unwrap_or_else(|| N); the waiver gained a warning written straight tostd::io::stderr()(libtest only replayseprintln!for failing tests); and therefusal message stopped asserting "this harness did not create" — false when your
own container is restarted — in favour of "has no claim for", plus the claim path
and a missing-vs-stale distinction.
Review round 3 — three more, each reproduced against
7ad5e51SF1 — the wipe scan was case-sensitive. Round 2 widened the matcher from one
call spelling to "the quoted command name" and never mutated case: the same
guards-one-notch-weak pattern, inside the fix for the previous instance of it.
Redis command names are case-insensitive on the wire, so
cmd("flushall")wipesjust as hard.
SF2 — the scan never read
tests/common/mod.rs. Moving theFT._LISTdroploop +
FLUSHALLinto a sharedpub fn wipe_server()is the likely refactor,since this guard's premise is that four suites share one
flush_db()shape.RED at
7ad5e51, with a synthetictests/integration_synthwipe.rswhoseflush_dbiscmd("flushall")and which has no claim call at all, plus thatwipe_server()appended totests/common/mod.rs:GREEN on this branch, identical mutations:
Both synthetic mutations were reverted afterwards and the pushed tree
grep-verified clean (
wipe_server→ 0 hits, nosynthwipefile). The matcher nowuppercases the source and accepts either quote style, so
"flushall","FlushAll","flushdb"and a LuaEVAL "redis.call('flushall')"are allpositive controls, with a prose-mention negative control that keeps
integration_vectorsetsout of the pinned set.SF3 — the 30 s probe re-ran for every test.
OnceLock::get_or_initdoes notmemoize a panicking initializer, so after a refusal each later test paid the full
reachability loop again — and the PR body claimed "runs once per process", which
was false on exactly the refusal path. The memoized value is now the verdict
(
None= allowed,Some(msg)= refuse) and the panic happens outside theinitializer. Measured against a dead port (7999), same tests, same command:
7ad5e51b5c8b32test_redis_knn)finished in 60.21sfinished in 30.07srunning 44 tests…finished in 30.17sAt
7ad5e51the 44-test suite would have taken ~22 minutes to report "server notavailable"; it now takes 30.17 s in total.
NITs also closed.
sum_keyspace_keysreturnsOption<i64>so it can say "Icannot tell" instead of
0— an empty reply, a missing# Keyspaceheader, adbN:line with no usablekeys=, a negative count and an overflow are each aNone(which becomesInconclusive) rather thanFresh; that was this guard'sown bug class one notch in. INV-P1 now sees through Rust digit separators
(
6_399) while leavingfoo_6399alone. TheINFO persistenceloading check isdocumented as best-effort and inert on kividb v1.0.2-full, which reports no
loadingfield (measured). Doc typo inintegration_dragonfly.rsfixed.Review rounds 4-5 — the audits found the guard's own claims, then the claim itself
Round 4 (claims audit at
b5c8b32). Every refusal said "Editing the portliteral in tests/.rs is rejected by tests/harness_invariants.rs". It was
not: INV-P1 rejects a second literal, and
default_port()re-reads whateveris on the
.unwrap_or(..)line, so editing the single one straight through isunpoliced — the exact bypass incident 1 used. Worse, the test policing the
message asserted only that the string
"harness_invariants"was present, neverthat it was true.
RED at
b5c8b32,.unwrap_or(6399)→.unwrap_or(7911), nothing else touched:GREEN here, same one-line edit:
Fixed by making the sentence true, not by weakening it: INV-P6 pins each
wiping suite's default to the host port compose publishes, parsed from the
compose file so the two cannot drift apart in either direction. Also in round 4:
docker run -p <your-port>:6379, but kividb listens on 6380 (compose maps6386:6380), so an operator following its refusal got a container the suitecannot reach — and then a second refusal from this same guard. The container
port is now per-suite data, checked against compose. Live:
docker run -d --rm -p <your-port>:6380 … / KIVIDB_PORT=<your-port> …forkividb,
:6379for redis.is_standalone_numbertreated_as a boundary, so INV-P1 flaggedlet foo_6399 = 1;— a line with no literal at all — and the comment claimingotherwise was false. Identifier characters now disqualify, with a negative
control that also proves a real literal on the same shape of line is still
caught.
CLAIM_OUTCOME's doc cited "60.14 s instead of 20.13 s". 20.13 s is notreproducible and is structurally impossible (the wait is 30 s either way).
Replaced with the measured pair, 60.21 s → 30.07 s.
"INV-P1/P2/P3". Both corrected.
integration_valkey.rsandintegration_kividb.rsstill said "state thisharness did not create" — the phrasing the message test forbids in the message.
Round 5 (mutation campaign at
b5c8b32). Sixteen invariant mutants werecaught; two were not.
The claim could simply be commented out. INV-P2 matched
squeeze(body).contains(..)against raw source, while INV-P1'sbare_port_hitshad skipped
//lines since round 1 — same file, one dimension short. Live A/Bon a redis:8.8.0 seeded with 1 key in db0, 1 key in db1 and one FT index. RED at
b5c8b32with a single/prepended to the claim line:INV-P2 now compares the whole
test_port()body, comments stripped, againstthe single shape it may have. Equality also closes
if false { .. }andstringify!(..), pins the arguments and container port, and makessqueeze'srustfmt tolerance load-bearing. The comment stripper leaves string literals
alone so
redis://survives, with a control.INV-P5 read one directory level. Round 3's headline fix, re-evaded in its own
idiom: a wipe helper at
tests/common/wipe/mod.rswas invisible. The walk nowrecurses and the file set is pinned rather than asserted
> 0.GREEN here with both mutations applied at once:
Both mutations were reverted and the pushed tree grep-verified (
common/wipe→ 0files,
// common::claim→ 0 hits).Round 5 also closed four gaps where the guard could be made inert with every
suite still passing — a permissive mutation makes integration tests pass harder,
so only a source pin can catch them:
claim_verdict, the consumer, only. Aprobe_serverreturning
Reachable { keys: 0, index_count: 0 }for an unreachable server —the exact regression INV-P4's own docstring cites — passed everything.
probe_serveris nowpuband assertedInconclusiveagainst a closedephemeral port at zero wait, plus source pins that every early return is
Inconclusive, thatProbe::Reachableis constructed exactly once and last,that
claim_resp_instancepanics on a refusal, that the waiver defaults tooff, and that
CLAIM_REACHABLE_WAIT>= the longestwait_for_*()deadline.Freshwheneverkeys == 0was unpinned even thoughflush_db()dropsindexes and the refusal counts them: 0 keys + 1 index must now refuse.
\"FLUSHALL\"— the natural Rust spelling of a Lua payload — was not matched,which made the shipped "either quote style" claim false.
\is now a quoteboundary, with a positive control.
integration_*.rscannot slip past the INV-P3 scan unclassified.Engine survey
Server-wide destruction — guarded here, all four live-validated:
integration_redisflush_db()→FT.DROPINDEX <each> DD+FLUSHALLintegration_valkeyflush_db()→FT.DROPINDEX <each>+FLUSHALLintegration_dragonflyflush_db()→FT.DROPINDEX <each>+FLUSHALLintegration_kividbflush_db()→FT.DROPINDEX <each>+FLUSHALLNarrower destruction, NOT guarded here. No other suite has an
enumerate-and-delete loop, a
DROP DATABASE/SCHEMA, adropDatabase, aDELETE /_allor a wildcard index delete. Two are worth naming rather than filingunder "named targets only":
integration_pgvector(5433) —DROP TABLE IF EXISTS items CASCADEat:54,:60,:485. The in-process connection is built fromconst PG_PORT: u16 = 5433/PG_DB = "postgres"with no env read at all(
PGVECTOR_PORTonly reaches the spawned child), so the target is unmovable,the table name is maximally generic, and
CASCADEtakes dependents with it.This is the natural integration_redis FLUSHALLs whatever is on REDIS_TEST_PORT's default 6399, and the old port-override spelling silently no-ops #292 follow-up.
integration_qdrant(6334/6335) —:2030and:2127take the collectionfrom
std::env::var("QDRANT_COLLECTION_NAME").unwrap_or("benchmark")and thosetwo tests' child spawns do not pin the var, so an inherited value steers the
drop/recreate.
integration_mongodb'sdrop_test_collection(),ES/OS's
DELETE /<index>, and milvus/weaviate/chroma's named collection/classdeletes are all fixed-name targets.
integration_vectorsets(6398) issues no wipe; since VectorSets uses a hardcodedidxkey — it never got the #151-4 per-config namespacing, so concurrent runs clobber each other #236 each config ownsidx:<config-name>. Covered by INV-P1, not by the claim.Gates
Baselines derived on
e99d2b5in a dedicated target dir, aftertouch src/lib.rs src/bin/vector_db_benchmark/main.rs:e99d2b5cargo fmt --checkcargo clippy --all-targets -- -D warningswarning/errorlinescargo test --lib --binscargo test --test overhead_invariantsrunning 9 tests, 9 passedrunning 9 tests, 9 passedcargo test --test harness_invariantsrunning 38 tests, 38 passedintegration_redis -- --test-threads=1running 44 testsrunning 44 tests, 44 passedintegration_valkey -- --test-threads=1running 29 tests, 29 passedintegration_dragonfly -- --test-threads=1running 15 tests, 15 passedintegration_kividb -- --test-threads=1running 21 tests, 21 passed (622.48s)integration_vectorsets(untouched)running 15 tests, 15 passedRefusal checks with foreign data present, all on my own containers: redis 7621,
valkey 7624, dragonfly 7622 — each
REFUSING …,holds 2 key(s) across all databases,keys intact: 2. Ports 7601-7999 only; 6399/6398/6380/27018 werenever touched, and
tests-redis-1/tests-valkey-1confirmed still running.One filtered run of mine printed
running 0 tests/okbecause the filtermatched no test name. I caught it, found the real name, and re-ran — every count
above is from a line I read.
Stated limits
docker restartof your own container. Bothrun_idandmaster_replidare regenerated on restart while an RDB-persisted datasetsurvives, so the claim goes stale and the suite refuses data it created itself.
Safe direction; the refusal message names this case and the one-line fix.
cargo clean, a freshCARGO_TARGET_DIR, a new worktree, a CI cache miss, or--target <triple>alldrop the claim. This only bites after a run killed mid-suite: a clean full run
leaves the server empty, so ordinary re-runs take the
Freshpath.FT._LISTfailing is treated as inconclusive. Correct for these four suites — they all
call
FT.DROPINDEX— but it does mean the guard is stricter than "has data".INFO persistenceerrorsor omits
loadinggets no loading check; kividb v1.0.2-full is such an engine(measured). A server so locked down that INFO fails entirely is still caught by
INFO keyspace.once it has written anything, but in the window right after its
flush_db()theserver is empty and this guard will claim it.
master_replididentifies a replication GROUP, not an instance. A Dragonflyreplica reports the same value as its primary, so a claim recorded against a
primary would also authorise a replica of it that later took the same
host:port.run_id(redis/valkey/kividb) does not have this property.in either quote style, and escaped (
\"FLUSHALL\"), but not aKEYS *+DELloop, a
SCAN+UNLINKsweep, or a command assembled from fragments. All threewere confirmed missed, exactly as stated.
test_port(), not its runtime effect. Anyrefactor of that function fails the invariant until the expected body is
updated — deliberate, because it is the choke point, but it is friction.
r#"..."#). No suiteuses one inside
test_port(), and INV-P2's equality would fail loudly ratherthan silently if one appeared.
fn test_port() -> u16fallback — the fiveRESP suites.
integration_opensearch,_milvus,_elasticsearchand_pgvectorstill pass bare port literals to spawned binaries; not fixed here,and none of them can wipe a server.
src/redisearch/configure.rs:53FLUSHALLs as a Memorystore fallback (the scanreads only
tests/), andsrc/bin/vector_db_benchmark/engine/dragonfly.rs:101has
.unwrap_or(6385)in production code.tests/, plus one CI step.🤖 Generated with Claude Code