3.9 KiB
Scholarr rewrite - testing strategy [live 2026-07-21]
This is an agreed direction from the 2026-07-21 discussion with Justin, to be formalized during spec freeze. Part of the scholarr-rewrite runbook; read the doctrine there first.
This document records how the rewrite proves itself correct. It is a decision record, not the final spec: it feeds the spec set and defines the release gates below.
Core principle
Deterministic tests against recorded reality. The service's hard logic (matching, dedup, scheduling, backoff) must be testable with zero network. Nothing that matters to correctness depends on a live provider being reachable at test time.
Golden corpus
Two recorded artifacts become permanent fixtures in the repo (anonymized where needed):
- The recovered v1 database: 25,698 real publications, 492 profiles with real edge cases.
- The calibration run's cached OpenAlex responses.
These are the ground for deterministic replay: the matcher and dedup logic are exercised against real data and real provider payloads without touching the network.
The five pillars
-
Pure logic (normalization, fingerprinting, identifier parsing, confidence scoring):
- table tests;
- property-based tests for the invariants that must always hold: normalization is idempotent, dedup is order-independent, merge is commutative;
- Go native fuzzing on every parser that touches external text or paste input.
-
Provider clients:
- contract tests replaying recorded HTTP fixtures via
httptest, including the ugly cases:429with and withoutRetry-After, truncated JSON, empty pages; - the rate limiter and cooldown state machine tested with a fake clock;
- the 2026-07-21 OpenAlex IP block becomes a regression case so the system's handling of an IP-level block is proven, not hoped.
- contract tests replaying recorded HTTP fixtures via
-
Sync engine - executable invariants:
- one active run per user;
- idempotent re-runs;
- additive-only baseline import;
- crash-mid-run leaves resumable state;
- race detector in CI, plus a concurrent-write hammer test, because single-writer discipline is the main risk of the SQLite-only design.
-
Migrations:
- every migration tested up and down against a real-database copy;
- plus a seeded prior-version DB in CI, so upgrades are proven pre-release.
-
End-to-end smoke:
- the binary boots in CI with a seeded SQLite and a stub provider;
- a scripted curl run walks the full API flow: onboard, import, sync, review-resolve, read, export.
- No browser automation. UI QA is manual by Justin against the frozen design contract
(
/opt/stacks/scholarr-rewrite/design/DESIGN.md). See the working-conventions block in README.md.
Dry runs are a product feature
Not test-only scaffolding, actual product behavior that also serves as the QA instrument:
- bulk import previews before creating anything;
--dry-runon sync and on destructive admin actions.
Acceptance
Shadow-run with the real ~253-author list for 2 to 3 weeks, alongside the dad's existing JS-script habit, and diff what each caught. This is the release flip criterion and follows the conductarr-soak pattern.
Release gates, in order
- unit + property + fuzz green;
- race detector green;
- migration up/down green;
- e2e smoke green;
- golden-corpus ratchet - matcher changes may not regress the recorded auto-match baseline;
- tagged release with a tested backup/restore path documented.
Related
- The recovered v1 DB and calibration cache that become fixtures: README.md (Assets), identity-calibration.md.
- Manual-QA rule (no browser automation): a working convention of this project, stated in README.md.