8000 test(#1004): re-pin the --fresh swap contract as ten Rust tests behind a CorpusNode seam by xerj-org · Pull Request #1006 · xerj-org/xerj · GitHub
[go: up one dir, main page]

Skip to content
8000

test(#1004): re-pin the --fresh swap contract as ten Rust tests behind a CorpusNode seam - #1006

Merged
xerj-org merged 3 commits into
feat/issue-977-xccode-in-binaryfrom
test/issue-1004-fresh-contract
Sep 21, 2026
Merged

xerj-org merged 3 commits into
feat/issue-977-xccode-in-binaryfrom
test/issue-1004-fresh-contract

Conversation

@xerj-org
Copy link
Copy Markdown
Owner

Closes #1004.

What this closes

#1004 was filed the day PR #1005 landed the script port: the 81-check --fresh swap-contract suite (tools/xerj-code/tests/test_xc_index_fresh.py) was deleted with the scripts, and nothing executed the destructive-side contracts of xerj corpus index --fresh anymore — 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 Es is 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 the failing_but_writing shape (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_index keeps argv parsing, pathgates and build_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:

  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 8000 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 1 s sleep)
  10. a legacy corpus whose indices predate builds indexes under the bare namespace

Fail-before proof (mutations applied, caught, reverted):

  • M1 has_working_index = false on unknown old count → test 4 fails
  • M2 sibling-exclusion filter removed from corpus_indices → test 7 fails

Drive-by

url_resolution_prefers_flag_then_env_then_default joins 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 warnings
  • cargo fmt -p xerj-autoindex -- --check: clean
  • cargo check -p xerj-server: ok
  • scripts/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 to main when 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.

…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.
@cla-bot cla-bot Bot added the cla-signed label Sep 21, 2026
@xerj-org
xerj-org merged commit ede47e8 into feat/issue-977-xccode-in-binary Sep 21, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

0