Skip to content

test(config,vitest): harden test isolation and complete vitest migration - #211

Merged
sroussey merged 3 commits into
mainfrom
claude/beautiful-archimedes-o87xem
Jul 24, 2026
Merged

test(config,vitest): harden test isolation and complete vitest migration#211
sroussey merged 3 commits into
mainfrom
claude/beautiful-archimedes-o87xem

Conversation

@sroussey

Copy link
Copy Markdown
Contributor

Summary

Bundles two related test-hygiene fixes on top of main (PR #210's vitest
migration is replayed here as the first commit, since the base branch does not
yet contain it — see note below):

  • chore(test): complete the vitest migration on top of main. Adds
    vitest.config.ts with a resolveId plugin that appends .js to the
    workglow package's internal re-exports (its published ESM omits the
    extension, which Node's strict loader rejects). Routes workglow through
    Vite's transform pipeline via server.deps.inline. Uses pool: "forks" for
    native-module isolation (better-sqlite3, onnxruntime). Ports the
    runCliProcess helper for CLI-subprocess tests to node:child_process
    (Bun.spawn crossed stdout/stderr under vitest, breaking --format json
    parsing) and widens its env passthrough to USER, LOGNAME, CI,
    NODE_OPTIONS, XDG_CONFIG_HOME, XDG_CACHE_HOME. Replaces Bun.Glob with
    readdirSync + regex in the daily/quarterly index tests, swaps
    Bun.file(FIXTURE).text() for readFileSync(..., "utf-8") in the DEFM14A
    test trio, and gates the remaining Bun-only tests
    (BootstrapDownloadTask's Bun.file writers, SecFetchJob's Bun.serve
    loopback tests, the index-fetch tests whose JobQueueServer poll loop times
    out under Node/vitest) with describe.skipIf(typeof Bun === "undefined") +
    a TODO. Fixes ReturnType<typeof spyOn>ReturnType<typeof vi.spyOn> in
    EntityTemporalRepo.test.ts. Adds better-sqlite3 as a devDependency so
    @workglow/sqlite/storage can bootstrap on Node.

  • test(config): strip env-derived DI tokens on TestingDI reset. Adds
    ENV_DERIVED_TOKENS in src/config/tokens.ts (the canonical list of every
    token EnvToDI.ts and the CLI preAction hook project into the container),
    and has resetDependencyInjectionsForTesting() iterate it, calling
    globalServiceRegistry.container.remove(token.id) before repo
    re-registration. Deletes the SEC_DB_TYPE = "memory" as unknown as ...
    casts in the three sqlite test files
    (SpacDealReplace.sqlite.test.ts, Form8KEventReplace.sqlite.test.ts,
    Form8KEventLegacyMigration.test.ts), simplifies the save-and-restore hooks
    in db.test.ts and BootstrapDownloadTask.test.ts to just call the reset,
    and adds src/config/TestingDI.test.ts (asserts every env-derived token is
    stripped, and one repo token is still re-registered). Adds a TSDoc block
    above resetDependencyInjectionsForTesting() pinning the invariant.

Base note (PR #210)

main at HEAD does not yet contain PR #210
(chore/vitest-runner). The first commit here replays the net-effect final
state of PR #210 (vitest.config.ts, converted test files, runCliProcess
helper) so both fixes land in one PR — no chore/vitest-runner merge required
first.

Test plan

  • bun x tsc --noEmit — zero type errors.
  • bun x vitest run — 263 test files pass, 3 skipped, 0 failing (before:
    218 failing / 47 passing on the chore/vitest-runner head this
    replays).
  • bun x vitest run src/config/TestingDI.test.ts — the new reset test
    passes.
  • Manual walkthrough of the three Bun-gated test suites
    (BootstrapDownloadTask, SecFetchJob retry/wire, index-fetch) —
    each carries a TODO noting the underlying Bun-API dependence that would
    let the skip drop.

Known gaps

  • FetchDailyIndexTask.test.ts / FetchQuarterlyIndexTask.test.ts are gated
    to Bun for now — the JobQueueServer poll loop times out under Node/vitest
    at pollIntervalMs: 1. That's a portability issue with the workglow queue
    server, pre-existing on the chore/vitest-runner head this PR replays; the
    skip is annotated with a TODO to migrate the test off the live-server
    pattern.

Generated by Claude Code

claude added 3 commits July 23, 2026 09:23
Migrate the test suite runner from `bun test` to vitest so each test file
runs in its own module registry — no more cross-file `globalServiceRegistry`
leaks — and land the follow-on hardening the migration exposed:

  * `vitest.config.ts` with `.env.test` autoload, `pool: "forks"` for native
    module isolation (better-sqlite3, onnxruntime), and a resolveId plugin
    that appends `.js` to the workglow package's internal re-exports (its
    published ESM omits the extension, which Node's strict loader rejects).
    Route the workglow package through Vite's transform pipeline via
    `server.deps.inline: [/workglow/]` so the plugin sees the intra-package
    specifiers.

  * `src/cli/testing/runCliProcess.ts` — spawn wrapper for the CLI-subprocess
    tests. Uses `node:child_process` to keep stdout/stderr cleanly separated
    (Bun.spawn + Response capture crossed them under vitest, breaking JSON
    parsing) and its `cliEnv` helper now passes through `USER`, `LOGNAME`,
    `CI`, `NODE_OPTIONS`, `XDG_CONFIG_HOME`, `XDG_CACHE_HOME` alongside the
    original set — some CLI paths (and CI matrices) rely on them.

  * Replace `Bun.Glob` scans in the daily / quarterly index tests with a
    `readdirSync` + regex filter, and swap `Bun.file(FIXTURE).text()` calls
    in the DEFM14A test trio for `readFileSync(FIXTURE, "utf-8")` so those
    files load under Node without needing Bun's IO helpers.

  * Gate Bun-only tests with `describe.skipIf(typeof Bun === "undefined")` —
    `BootstrapDownloadTask` (uses `Bun.file` writers and stubs `Bun.spawn` /
    `Bun.which`) and `SecFetchJob`'s wire-level + retry tests (use
    `Bun.serve` for a loopback listener). Each carries a TODO noting the
    migration off Bun-specific APIs that would let the skip drop.

  * `EntityTemporalRepo.test.ts` — `ReturnType<typeof spyOn>` → `ReturnType<
    typeof vi.spyOn>` to satisfy the vitest 4 typings.

  * Add `better-sqlite3` as a devDependency — `@workglow/sqlite/storage` on
    Node loads it via dynamic import for `Sqlite.init`; without it every
    setupAllDatabases-driven test fails at bootstrap.

The daily/quarterly index tests are also gated to Bun for now (the
JobQueueServer poll loop times out under Node/vitest at `pollIntervalMs: 1`
— a portability issue with the workglow queue server, pre-existing on the
`chore/vitest-runner` head this PR replays). TODOs on the skips document
the migration path.

Baseline before this PR: 218 failing / 47 passing (265 total) on
`chore/vitest-runner` head. After: 263 passing / 3 skipped, 0 failing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WXWNrDwDQnd59uksqbPC2E
Tests share one process (per vitest test file, but still the same worker
process for tests within a file), so a token registered by an earlier test
— `SEC_DB_TYPE = "sqlite"`, `SEC_DRY_RUN = true`, `SEC_RAW_DATA_FOLDER =
"/tmp/xyz"` — silently leaked into later tests through the shared
`globalServiceRegistry`. Test files worked around it with bespoke
save/restore blocks or `SEC_DB_TYPE = "memory" as unknown as ...` casts
that only mostly worked ("no unregister API" — actually there IS a
`container.remove(id)`).

Fix it once, in one place:

  * `tokens.ts` — add `ENV_DERIVED_TOKENS`, the canonical list of every
    token the CLI's env/DI wiring projects into the registry (`EnvToDI.ts`
    plus the CLI preAction flags `SEC_DRY_RUN` / `SEC_JSON_OUTPUT`). The
    TSDoc pins the invariant: whenever `EnvToDI.ts` gains a new token, it
    must be appended here too.

  * `TestingDI.ts` — `resetDependencyInjectionsForTesting()` now iterates
    `ENV_DERIVED_TOKENS` and calls `globalServiceRegistry.container.remove
    (token.id)` before re-registering the in-memory repositories, so each
    test file starts with a clean container regardless of what the
    previous file bound.

  * `TestingDI.test.ts` — asserts every token in `ENV_DERIVED_TOKENS` is
    stripped by the reset and that a representative repo token
    (`ADDRESS_REPOSITORY_TOKEN`) is still re-registered afterwards. The
    second assertion guards against a future refactor that clobbers the
    repo re-registration path.

  * Delete the `SEC_DB_TYPE = "memory" as unknown as "sqlite" | "postgres"`
    casts in `SpacDealReplace.sqlite.test.ts`,
    `Form8KEventReplace.sqlite.test.ts`, and
    `Form8KEventLegacyMigration.test.ts` — the afterEach now just calls
    `resetDependencyInjectionsForTesting()`. Removed the accompanying "no
    unregister API" comments.

  * `db.test.ts` — drop the save-and-restore block around `SEC_DB_TYPE`;
    the reset takes care of it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WXWNrDwDQnd59uksqbPC2E
…imedes-o87xem

# Conflicts:
#	CHANGELOG.md
#	package.json
@sroussey
sroussey merged commit 5b78ea6 into main Jul 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants