Repository navigation
test(#1004): re-pin the --fresh swap contract as ten Rust tests behind a CorpusNode seam - #1006
Merged
xerj-org merged 3 commits intoSep 21, 2026
Conversation
…d a CorpusNode seam (#1004) Motivation ---------- PR #1005 ported the reference-coding scripts into the binary and deleted tools/xerj-code/tests/test_xc_index_fresh.py — 81 offline checks that pinned the DESTRUCTIVE side of `xerj corpus index --fresh`: which indexes may be deleted, in what order, and when the state file may switch. Issue #1004 was filed the same day because the port's logic carried those contracts but no test executed them; the answer page and CHANGELOG said so plainly ("carried by review, not by a test suite"). This commit closes that gap. Seams ----- The build/verify/swap flow moves verbatim into `corpus_index_flow(node, run_autoindex, now_secs, root, url, name, fresh)`. Three injections, each exactly the surface the old suite faked externally: - `CorpusNode` — the four node operations the flow performs: list indices under a glob, the tri-state `_count`, DELETE one index by exact name, and the scoped catalog delete. `impl CorpusNode for Es` is thin; the fake in the tests is the fifth implementation and cannot drift from what a real node is asked to do. - `run_autoindex(prefix, state_dir)` — the old suite PATH-shimmed a fake binary; the tests inject a fake runner that lands shards on rc 0/3, or on any rc in the `failing_but_writing` shape (#367: abort in finalisation after every document was written). - `now_secs` — the one-second build-id stamp; the old suite slept a real second to collide two rebuilds, the tests advance a fake clock. `run_corpus_index` keeps argv parsing, pathgates, `build_es`, and binds the seams to production values. Tests ----- Ten contract tests in xc.rs's test module, asserting on a FakeNode audit trail (every count read, delete, catalog scope) and the REAL state file — never on stdout prose: 1. a verified replacement switches state THEN retires, by exact name, count-before-delete ordering proven from the ops log 2. a build that never verifies (rc 0, zero records) removes only what it created; old index and old state untouched 3. a count the node does not answer (Unknown) authorises no delete and no switch; the unverified build is kept for the next verified run to retire 4. an unknown count for the OLD index presumes a working index: a failed build is not salvaged over it 5. a failed FIRST build that wrote records is kept and marked salvaged 6. a plain re-run resumes the recorded build: same prefix, same state dir, no deletes 7. sibling corpus indices (battle vs battle-terse) are never touched 8. a delete the node refuses leaves a duplicate, never a gap — the state file already names the new build 9. two rebuilds inside one second get different build ids (fake clock; the old suite's real 1s sleep) 10. a legacy corpus whose indices predate builds indexes under the bare namespace, no state dir Fail-before proof (both mutations caught, then reverted): - M1 `has_working_index = false` on unknown old count -> test 4 fails - M2 sibling-exclusion filter removed from corpus_indices -> test 7 fails The count-retry env knobs are pinned under a shared mutex: the new tests set XC_COUNT_TRIES=1, the existing defaults test asserts their absence, and run concurrently they would flake. Gates ----- - cargo test -p xerj-autoindex --lib: 1066 passed, 0 failed (was 1054; +12 incl. the two pre-existing tests now under the lock) - cargo clippy -p xerj-autoindex --all-targets: 0 warnings - cargo fmt -p xerj-autoindex -- --check: clean - cargo check -p xerj-server: ok - scripts/seo/build_articles.py --check + gen_sitemap.py --check + test_article_schema.py: 5/5 OK (answer page + CHANGELOG flipped from "not yet re-pinned" to naming the ten tests; landing regenerated) Closes #1004
One unreproduced lib-suite failure was observed once on the merged #977 branch (three other runs green). Process env is global, so a test that 8000 remove_var's XERJ_URL can race a concurrent reader; the #1004 suite already serialises its env knobs under ENV_LOCK — this test joins the same lock. No production change.
xerj-org
merged commit Sep 21, 2026
ede47e8
into
feat/issue-977-xccode-in-binary
2 of 3 checks passed
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 #1004.
What this closes
#1004 was filed the day PR #1005 landed the script port: the 81-check
--freshswap-contract suite (tools/xerj-code/tests/test_xc_index_fresh.py) was deleted with the scripts, and nothing executed the destructive-side contracts ofxerj corpus index --freshanymore — which indexes may be deleted, in what order, and when the state file may switch. The answer page and CHANGELOG said so plainly ("carried by review, not by a test suite") until this PR.The seams
The build/verify/swap flow moves verbatim into
corpus_index_flow(node, run_autoindex, now_secs, …)— three injections, each exactly the surface the old suite faked externally:CorpusNode— the four node operations the flow performs (list under a glob, tri-state_count, DELETE by exact name, scoped catalog delete).impl CorpusNode for Esis thin wrappers; the test fake is the same four operations and cannot drift from what a real node is asked to do.run_autoindex(prefix, state_dir)— the old suite PATH-shimmed a fake binary; the tests inject a runner that lands shards on rc 0/3, or on any rc in thefailing_but_writingshape (xc-index.sh cannot index apache/lucene: autoindex exits 1 on both embed modes, wrapper discards a fully-populated corpus, xc.py then refuses it #367: abort in finalisation after every document was written).now_secs— the one-second build-id stamp; the old suite slept a real second to collide two rebuilds, the tests advance a fake clock.run_corpus_indexkeeps argv parsing, pathgates andbuild_es, and binds the seams to production values.The ten contracts
Asserting on a FakeNode audit trail (every count read, delete, catalog scope) and the REAL state file — never stdout prose:
battlevsbattle-terse) are 8000 never touchedFail-before proof (mutations applied, caught, reverted):
has_working_index = falseon unknown old count → test 4 failscorpus_indices→ test 7 failsDrive-by
url_resolution_prefers_flag_then_env_then_defaultjoins the same ENV_LOCK the env-knob tests use: one unreproduced lib-suite failure was observed once on the merged #977 branch (three other runs green); process env is global, so env-mutating tests serialise.Gates
cargo test -p xerj-autoindex --lib: 1066 passed, 0 failed (base branch: 1056)cargo clippy -p xerj-autoindex --all-targets: 0 warningscargo fmt -p xerj-autoindex -- --check: cleancargo check -p xerj-server: okscripts/seo/build_articles.py --check+gen_sitemap.py --check(176 URLs) +test_article_schema.py: 5/5 OK — answer page + CHANGELOG flipped from "not yet re-pinned" to naming the ten tests; landing regenerated (2 files + sitemap)Stacked
Base is
feat/issue-977-xccode-in-binary(PR #1005) while it is open; retarget tomainwhen it merges. The merge commit in this branch carries #1005's conflict resolution against main (PR #999's windows fix) — already pushed to the feat branch.