From ac9a2ee12f956ecd451fb39bdc39a132009ffa76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Magyar?= Date: Sun, 24 May 2026 12:10:10 +0100 Subject: [PATCH] chore(ci): consolidate parity shards and narrow cross-platform matrix (#1798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(ci): reduce CI runner-minutes by consolidating parity and narrowing cross-platform Scope-resolution parity previously spawned 9 separate GitHub Actions jobs (one per migrated language), each doing full checkout + npm ci + build for a single test file. Consolidate into one job running scripts/run-parity.ts which loops through all migrated languages sequentially — same coverage, ~45 fewer runner-minutes of redundant setup per PR. Cross-platform (Windows/macOS) previously ran the full 373-file test suite. Narrow to 45 platform-sensitive files (native LadybugDB, process spawning, path separators, worker threads, filesystem behavior). Full suite still runs on Ubuntu with coverage. Also adds 2 missing lbug integration tests (lbug-orphan-sidecar-recovery, lbug-readonly-init) to the sequential lbug-db vitest project where they belong, and rewrites TESTING.md to document all test lanes. * fix: address code review findings on parity and cross-platform scripts - Capture stderr in run-parity.ts (vitest writes diagnostics to stderr) - Lower per-invocation timeout from 5min to 60s to stay within CI job limit - Add --language flag validation (error on missing value) - Add timeout diagnostic to run-cross-platform.ts catch block - Add analyze-wal-checkpoint-failure.test.ts to lbug-db sequential project - Expand cross-platform list: parser-loader, pipeline, pipeline-graph-golden, setup-skills, cli/tool-no-index-stderr (51 files, was 45) * fix: add shell:true for Windows npx resolution and simplify fs import execFileSync('npx', ...) fails with ENOENT on Windows because npx is npx.cmd — shell:true resolves this. Also replaces dynamic await import('fs') with static import, and fixes timeout detection to use err.killed instead of err.code. * fix(ci): raise parity per-invocation timeout to 120s and job timeout to 30min TypeScript and C++ resolver tests take 60-90s on CI runners, exceeding the 60s per-invocation timeout. Raise to 120s. Also bump the job-level timeout from 25 to 30 minutes for margin (realistic total is ~11 min). * fix(ci): raise parity per-invocation timeout to 180s for C++ resolver C++ resolver tests take 130-150s on CI runners due to template metaprogramming, ADL, and SFINAE fixture volume. 120s was still too tight. Realistic total across all 9 languages is ~12 min, well under the 30-min job timeout. * fix(ci): use stdio inherit for parity — no per-invocation timeout Switch from piped stdio with per-invocation timeouts to stdio: 'inherit'. Vitest output streams to CI console in real time, making failures immediately visible. The CI job-level timeout (30 min) is the only guard — no more artificial per-invocation timeouts that cut off slow resolver tests like C++ (which genuinely takes 3+ minutes). --------- Co-authored-by: Test --- .github/workflows/ci-scope-parity.yml | 62 +++------- .github/workflows/ci-tests.yml | 10 +- TESTING.md | 139 +++++++++++++++-------- gitnexus/package.json | 2 + gitnexus/scripts/cross-platform-tests.ts | 131 +++++++++++++++++++++ gitnexus/scripts/run-cross-platform.ts | 44 +++++++ gitnexus/scripts/run-parity.ts | 128 +++++++++++++++++++++ gitnexus/vitest.config.ts | 6 + 8 files changed, 428 insertions(+), 94 deletions(-) create mode 100644 gitnexus/scripts/cross-platform-tests.ts create mode 100644 gitnexus/scripts/run-cross-platform.ts create mode 100644 gitnexus/scripts/run-parity.ts diff --git a/.github/workflows/ci-scope-parity.yml b/.github/workflows/ci-scope-parity.yml index 8e2926ba8..039438a15 100644 --- a/.github/workflows/ci-scope-parity.yml +++ b/.github/workflows/ci-scope-parity.yml @@ -24,6 +24,19 @@ name: Scope Resolution Parity # When the set is empty (e.g. mid-Ring-3 for every language), the parity # matrix is skipped and the workflow reports success — no-op until a # language is explicitly claimed migrated. +# +# ── Consolidation (chore/vitest-speed-strategy) ──────────────────────── +# Previously each language was a separate GitHub Actions matrix job, +# meaning N languages × 1 checkout+install+build per shard. The build +# cost dwarfed the test cost (~5 min setup for ~15 sec test execution). +# +# Now a single job runs `scripts/run-parity.ts` which loops through all +# migrated languages sequentially (2 vitest invocations per language: +# legacy + registry-primary). All failures are collected and reported +# at the end (equivalent to the old fail-fast: false behavior). +# +# Adding a new language to MIGRATED_LANGUAGES still requires no workflow +# edit — the script auto-discovers the set at runtime. on: workflow_call: @@ -37,7 +50,6 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 outputs: - languages: ${{ steps.read.outputs.languages }} has-any: ${{ steps.read.outputs.has-any }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -49,65 +61,27 @@ jobs: working-directory: gitnexus run: | set -euo pipefail - # `tsx` evaluates the TS source directly (no build step), imports - # the exported `Set`, and emits a GH-Actions-friendly JSON matrix. LANGS=$(npx tsx scripts/ci-list-migrated-languages.ts) COUNT=$(printf '%s' "$LANGS" | jq 'length') HAS_ANY="false" if [[ "$COUNT" -gt 0 ]]; then HAS_ANY="true"; fi - echo "languages=$LANGS" >> "$GITHUB_OUTPUT" echo "has-any=$HAS_ANY" >> "$GITHUB_OUTPUT" echo "Discovered $COUNT migrated language(s): $LANGS" - echo "Parity matrix will run: $HAS_ANY" + echo "Parity will run: $HAS_ANY" parity: - name: ${{ matrix.lang.slug }} parity + name: scope-resolution parity needs: discover if: needs.discover.outputs.has-any == 'true' runs-on: ubuntu-latest - timeout-minutes: 20 - strategy: - # One language failing must not abort the others — we want the full - # parity matrix result on a single CI run so a reviewer sees every - # regression at once rather than one-at-a-time. - fail-fast: false - matrix: - lang: ${{ fromJSON(needs.discover.outputs.languages) }} + timeout-minutes: 30 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-gitnexus with: build: 'true' - - name: Verify resolver test file exists + - name: Run parity for all migrated languages shell: bash working-directory: gitnexus - run: | - set -euo pipefail - TEST_FILE="test/integration/resolvers/${{ matrix.lang.slug }}.test.ts" - if [[ ! -f "$TEST_FILE" ]]; then - echo "::error title=Missing resolver test::\ - Expected $TEST_FILE for '${{ matrix.lang.slug }}' (listed in \ - MIGRATED_LANGUAGES). Either fix the slug or add the test file \ - before listing this language as migrated." - exit 1 - fi - - - name: Resolver tests — legacy DAG (REGISTRY_PRIMARY_${{ matrix.lang.envvar }}=0) - shell: bash - working-directory: gitnexus - env: - FLAG_NAME: REGISTRY_PRIMARY_${{ matrix.lang.envvar }} - # Explicitly force the flag to `0` even though it also defaults to - # `MIGRATED_LANGUAGES.has(lang)` — once a language is in the set, - # the default flips to registry-primary, so an unset env var would - # silently re-run the same path as step #2. `env FOO=0 cmd` spawns - # `cmd` with the override scoped to just this invocation. - run: env "$FLAG_NAME=0" npx vitest run "test/integration/resolvers/${{ matrix.lang.slug }}.test.ts" - - - name: Resolver tests — registry-primary (REGISTRY_PRIMARY_${{ matrix.lang.envvar }}=1) - shell: bash - working-directory: gitnexus - env: - FLAG_NAME: REGISTRY_PRIMARY_${{ matrix.lang.envvar }} - run: env "$FLAG_NAME=1" npx vitest run "test/integration/resolvers/${{ matrix.lang.slug }}.test.ts" + run: npx tsx scripts/run-parity.ts diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index f354e626b..c34d0f6ec 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -59,21 +59,25 @@ jobs: gitnexus-web/web-test-results.json retention-days: 5 + # Platform-sensitive subset only — the full suite runs on Ubuntu above. + # See gitnexus/scripts/cross-platform-tests.ts for the file list and + # rationale for each included test. cross-platform: - name: ${{ matrix.os }} + name: ${{ matrix.os }} (platform-sensitive) strategy: fail-fast: false matrix: # Ubuntu already covered by the coverage job above os: [windows-latest, macos-latest] runs-on: ${{ matrix.os }} - timeout-minutes: 25 + timeout-minutes: 20 steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: ./.github/actions/setup-gitnexus with: build: 'true' - - run: npx vitest run + - name: Run platform-sensitive tests + run: npx tsx scripts/run-cross-platform.ts working-directory: gitnexus # End-to-end smoke test for the #1728 packaging fix: pack the published diff --git a/TESTING.md b/TESTING.md index cf481d32b..e69a4b8e9 100644 --- a/TESTING.md +++ b/TESTING.md @@ -10,32 +10,37 @@ How we structure tests and which commands to run locally and in CI. | Web UI | `gitnexus-web/`| Vitest | Unit/component tests | | Web UI E2E | `gitnexus-web/`| Playwright | Run when changing UI flows | -## Commands (local) +## Test lanes -From repository root, unless noted: +### `gitnexus/` commands -**`gitnexus` (CLI / library)** +From `gitnexus/`: + +| Command | What it runs | When to use | +| ------------------------ | ---------------------------------------------------- | ------------------------------- | +| `npm test` | Full suite (all 3 vitest projects) | Before opening a PR | +| `npm run test:unit` | Unit tests only (`test/unit/`) | Tight development loop | +| `npm run test:integration` | Integration tests (`test/integration/`) | After changing pipelines, DB, workers | +| `npm run test:coverage` | Full suite + v8 coverage with thresholds | Checking coverage impact | +| `npm run test:parity` | Scope-resolution parity for all migrated languages | After changing resolver or scope code | +| `npm run test:cross-platform` | Platform-sensitive subset only | Debugging a Windows/macOS issue | +| `npm run test:watch` | Vitest in watch mode | Active development | + +### `gitnexus-web/` commands + +From `gitnexus-web/`: + +| Command | What it runs | When to use | +| ---------------------- | --------------------------------- | ------------------------------ | +| `npm test` | Unit/component tests (vitest) | After changing web code | +| `npm run test:coverage`| Unit tests + coverage | Checking coverage impact | +| `npm run test:e2e` | Playwright browser tests | After changing UI flows (requires `gitnexus serve` + `npm run dev`) | + +### Before opening a PR ```bash -cd gitnexus -npm install -npm run build -npm test # full suite: vitest run -npm run test:unit # unit only: vitest run test/unit -npm run test:integration # integration suite -npm run test:coverage -npx tsc --noEmit # typecheck (matches CI) -``` - -**`gitnexus-web`** - -```bash -cd gitnexus-web -npm install -npm test # unit tests (vitest) -npx tsc -b --noEmit # typecheck (matches CI) -npm run test:coverage -npm run test:e2e # Playwright (requires gitnexus serve + npm run dev) +cd gitnexus && npx tsc --noEmit && npm test +cd ../gitnexus-web && npx tsc -b --noEmit && npm test ``` ## Pre-commit hook @@ -50,22 +55,79 @@ Tests do **not** run in the pre-commit hook — they run in CI (`ci-tests.yml`) Skip with `git commit --no-verify` (use sparingly). +## Vitest projects + +`gitnexus/vitest.config.ts` defines three projects for safety isolation: + +| Project | Files | Parallelism | Purpose | +| ---------- | ----------------------------- | ----------- | ---------------------------------------------- | +| `lbug-db` | Native LadybugDB integration tests (explicit list) | Sequential | Prevents file-lock conflicts from native mmap addon | +| `cli-e2e` | `skills-e2e.test.ts` | Sequential | CLI process spawning requires serial execution | +| `default` | Everything else | Parallel | Fast execution for pure logic and parser tests | + +When adding a new test that uses native LadybugDB (`@ladybugdb/core`), add it to the `lbug-db` project's explicit include list and the `default` project's exclude list. + ## Test categories - **Unit** — Pure logic, parsers, graph/query helpers; fast; no network. - **Integration** — Real combinations (filesystem, MCP wiring, larger pipelines) as already organized under `gitnexus/test/integration`. -- **Eval-style / golden sets** — For agent- or classification-style behavior, keep labeled inputs and expected outputs (JSON or table-driven tests) and run them in CI when relevant. +- **Resolver / parity** — Language-specific call-resolution tests in `test/integration/resolvers/`. - **E2E (web)** — Critical user paths only; prefer `data-testid` attributes for stable selectors. Tests run against real backend (`gitnexus serve`) and Vite dev server. -## Performance metrics (targets) +## Scope-resolution parity -Set targets to match team expectations, then tune to this repo’s CI reality: +Migrated languages (listed in `MIGRATED_LANGUAGES` in `src/core/ingestion/registry-primary-flag.ts`) are tested in both legacy and registry-primary modes on every PR. -| Metric | Target (initial) | Notes | -| ------------------- | ---------------- | ------------------------------------------ | -| Unit coverage | Align with CI | CI runs Vitest with coverage in `gitnexus` | -| Unit wall time | Fast PR feedback | Use `vitest run test/unit` for tight loop | -| Integration duration| < few minutes | Guard heavy tests with env flags if needed | +For each migrated language, CI runs the resolver test file twice: +1. `REGISTRY_PRIMARY_=0` — legacy DAG path +2. `REGISTRY_PRIMARY_=1` — registry-primary path + +Both must pass. Known legacy gaps are listed in `LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES` in `test/integration/resolvers/helpers.ts` and are automatically skipped in legacy mode. + +Adding a language to `MIGRATED_LANGUAGES` automatically enrolls it in parity — no workflow or config edit needed. The test file must exist at `test/integration/resolvers/.test.ts`. + +Run parity locally: `cd gitnexus && npm run test:parity` + +Run for a single language: `cd gitnexus && npx tsx scripts/run-parity.ts --language python` + +## Cross-platform testing + +Windows and macOS CI runs only the platform-sensitive test subset (~50 files out of 373). The full suite runs on Ubuntu. + +The subset is defined in `gitnexus/scripts/cross-platform-tests.ts` and includes: + +- **Platform-specific logic** — tests with `process.platform` guards, path.sep behavior, EPERM/EBUSY error classification +- **Native LadybugDB** — all `lbug-*` integration tests (N-API addon with known platform-varying behavior) +- **Process spawning / CLI** — tests using real `child_process.spawn`, shell quoting, CLI invocations +- **Worker threads** — tests spawning real `worker_threads` +- **Native addon loading** — tree-sitter grammar loading smoke tests +- **Filesystem behavior** — CRLF handling, directory walking, symlinks + +When adding a platform-sensitive test, add it to the appropriate section in `scripts/cross-platform-tests.ts`. + +### Confirming no tests are orphaned + +Every test file matches one of the three vitest projects. To verify: + +```bash +cd gitnexus +npx vitest list 2>/dev/null | wc -l # should match total test count +``` + +To check the cross-platform list is up to date, run `npm run test:cross-platform` — it fails fast if any listed file is missing. + +## CI integration + +GitHub Actions (`.github/workflows/ci.yml`) orchestrate: + +| Workflow | Jobs | Purpose | +| --------------------- | ------------------------------ | ------------------------------------------------ | +| `ci-quality.yml` | format, lint, typecheck, typecheck-web, workflow-convention | Code quality gates | +| `ci-tests.yml` | ubuntu/coverage, cross-platform (Win/Mac), packaged-install-smoke | Full suite + coverage on Ubuntu; platform-sensitive subset on Win/Mac | +| `ci-scope-parity.yml` | discover, parity | Scope-resolution parity for all migrated languages | +| `ci-e2e.yml` | e2e (chromium) | Playwright E2E, gated on `gitnexus-web/**` changes | + +The `CI Gate` job in `ci.yml` is the single required check for branch protection. It requires quality, tests, e2e, and scope-parity to all pass. ## Regression testing @@ -76,23 +138,6 @@ Re-run the full relevant suite when: - Graph schema, query contracts, or MCP tool shapes change - Dependencies with parsing or runtime impact upgrade -## CI integration - -GitHub Actions (`.github/workflows/ci.yml`) orchestrate: - -- **`ci-quality.yml`** — prettier format check, eslint lint, `tsc --noEmit` for `gitnexus/`, `tsc -b --noEmit` for `gitnexus-web/` -- **`ci-tests.yml`** — `vitest run` with coverage (ubuntu) + cross-platform (macOS, Windows) -- **`ci-e2e.yml`** — Playwright E2E tests, gated on `gitnexus-web/**` changes - -Local checks before pushing: - -```bash -cd gitnexus && npx tsc --noEmit && npm test -cd ../gitnexus-web && npx tsc -b --noEmit && npm test -``` - -Or rely on the pre-commit hook which runs these automatically for staged files. - ## User acceptance / beta (optional) For staged releases or UI betas: deploy to a staging environment, collect structured feedback, watch errors and latency, then iterate before a wider release. diff --git a/gitnexus/package.json b/gitnexus/package.json index fb40cba56..0e25ed25a 100644 --- a/gitnexus/package.json +++ b/gitnexus/package.json @@ -48,6 +48,8 @@ "test:integration": "vitest run test/integration", "test:watch": "vitest", "test:coverage": "vitest run --coverage", + "test:parity": "tsx scripts/run-parity.ts", + "test:cross-platform": "tsx scripts/run-cross-platform.ts", "postinstall": "node scripts/materialize-vendor-grammars.cjs && node scripts/build-tree-sitter-dart.cjs && node scripts/build-tree-sitter-proto.cjs && node scripts/build-tree-sitter-swift.cjs", "prepare": "node scripts/build.js", "prepack": "node scripts/build.js" diff --git a/gitnexus/scripts/cross-platform-tests.ts b/gitnexus/scripts/cross-platform-tests.ts new file mode 100644 index 000000000..122ce064b --- /dev/null +++ b/gitnexus/scripts/cross-platform-tests.ts @@ -0,0 +1,131 @@ +/** + * Cross-platform test subset runner. + * + * Runs only the tests that exercise platform-sensitive behavior on + * Windows and macOS. The full suite runs on Ubuntu; this narrows the + * cross-platform matrix to tests that actually vary across OSes. + * + * Categories included: + * - Platform-specific logic (path.sep, process.platform guards) + * - Native addon loading (LadybugDB, tree-sitter) + * - Process spawning and shell behavior + * - Filesystem locking and temp-dir behavior + * - Worker threads (real, not mocked) + * - CLI end-to-end tests + * + * When adding a new test that uses platform-varying APIs (native addons, + * child_process with real spawning, filesystem locking, path.sep), add + * it to the appropriate section below. + * + * Usage: + * npx vitest run $(npx tsx scripts/cross-platform-tests.ts) + * # or via the package script: + * npm run test:cross-platform + */ + +// Platform-specific logic tests — contain explicit process.platform guards +// or test behavior that differs across operating systems +const PLATFORM_LOGIC = [ + 'test/unit/setup.test.ts', + 'test/unit/setup-jsonc.test.ts', + 'test/unit/setup-codex.test.ts', + 'test/unit/platform-capabilities.test.ts', + 'test/unit/worker-pool-windows-quarantine.test.ts', + 'test/unit/lbug-pool-win-fts-probe.test.ts', + 'test/unit/repo-manager.test.ts', + 'test/unit/repo-manager-finalize-invariant.test.ts', + 'test/unit/hooks.test.ts', + 'test/unit/cursor-hook.test.ts', + 'test/unit/sidecar-recovery.test.ts', + 'test/unit/pool-wal-recovery.test.ts', + 'test/unit/detect-changes-worktree.test.ts', + 'test/unit/eval-server-bind-restriction.test.ts', + 'test/unit/ignore-service.test.ts', + 'test/unit/group/bridge-db.test.ts', + 'test/unit/group/bridge-db-edge.test.ts', +]; + +// Native LadybugDB integration tests — exercise the @ladybugdb/core +// N-API addon which has known platform-specific behavior (Windows +// file-lock lag after close, macOS N-API destructor segfaults) +const LBUG_NATIVE = [ + 'test/integration/lbug-core-adapter.test.ts', + 'test/integration/lbug-vector-extension.test.ts', + 'test/integration/lbug-pool.test.ts', + 'test/integration/lbug-pool-stability.test.ts', + 'test/integration/lbug-lock-retry.test.ts', + 'test/integration/lbug-open-retry.test.ts', + 'test/integration/lbug-close-handle-release.test.ts', + 'test/integration/lbug-orphan-sidecar-recovery.test.ts', + 'test/integration/lbug-readonly-init.test.ts', + 'test/integration/local-backend.test.ts', + 'test/integration/local-backend-calltool.test.ts', + 'test/integration/search-core.test.ts', + 'test/integration/search-pool.test.ts', + 'test/integration/staleness-and-stability.test.ts', + 'test/integration/analyze-wal-checkpoint-failure.test.ts', +]; + +// Process spawning and CLI tests — exercise child_process with real +// process spawning, which behaves differently across platforms (shell +// quoting, path resolution, signal handling) +const SPAWN_CLI = [ + 'test/integration/cli-e2e.test.ts', + 'test/integration/hooks-e2e.test.ts', + 'test/integration/skills-e2e.test.ts', + 'test/integration/server-http-startup.test.ts', + 'test/integration/mcp/server-startup.test.ts', + 'test/integration/analyze-heap-oom-e2e.test.ts', + 'test/integration/group/group-cli.test.ts', + 'test/integration/cli/tool-no-index-stderr.test.ts', + 'test/integration/setup-skills.test.ts', +]; + +// Worker threads tests — exercise real worker_threads which have +// platform-specific behavior (thread spawning, IPC, exit handling) +const WORKER_THREADS = [ + 'test/integration/worker-pool.test.ts', + 'test/integration/parse-impl-quarantine-cache-skip.test.ts', +]; + +// Tree-sitter native addon smoke tests — verify that native grammars +// load correctly on each platform (binary compatibility, .node loading) +const NATIVE_ADDON_SMOKE = [ + 'test/integration/tree-sitter-languages.test.ts', + 'test/integration/parsing.test.ts', + 'test/integration/pipeline.test.ts', + 'test/integration/pipeline-graph-golden.test.ts', + 'test/unit/parser-loader.test.ts', +]; + +// Filesystem behavior tests — exercise operations that vary across +// platforms (CRLF, symlinks, permissions, temp dirs) +const FILESYSTEM = [ + 'test/integration/filesystem-walker.test.ts', + 'test/integration/markdown-processor-crlf.test.ts', + 'test/integration/ignore-and-skip-e2e.test.ts', +]; + +const ALL_CROSS_PLATFORM = [ + ...PLATFORM_LOGIC, + ...LBUG_NATIVE, + ...SPAWN_CLI, + ...WORKER_THREADS, + ...NATIVE_ADDON_SMOKE, + ...FILESYSTEM, +]; + +// When invoked directly, print the file list for vitest consumption +if (process.argv[1]?.endsWith('cross-platform-tests.ts')) { + console.log(ALL_CROSS_PLATFORM.join('\n')); +} + +export { + ALL_CROSS_PLATFORM, + PLATFORM_LOGIC, + LBUG_NATIVE, + SPAWN_CLI, + WORKER_THREADS, + NATIVE_ADDON_SMOKE, + FILESYSTEM, +}; diff --git a/gitnexus/scripts/run-cross-platform.ts b/gitnexus/scripts/run-cross-platform.ts new file mode 100644 index 000000000..1f5464caf --- /dev/null +++ b/gitnexus/scripts/run-cross-platform.ts @@ -0,0 +1,44 @@ +/** + * Cross-platform test runner. + * + * Runs the platform-sensitive test subset defined in cross-platform-tests.ts + * via vitest. Used by `npm run test:cross-platform` and by the CI cross- + * platform matrix (ci-tests.yml). + * + * The main vitest.config.ts is used, so lbug-db project files get + * sequential execution and other safety constraints are preserved. + */ + +import { execFileSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { ALL_CROSS_PLATFORM } from './cross-platform-tests.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); + +// Verify all files exist +const missing = ALL_CROSS_PLATFORM.filter((f) => !fs.existsSync(path.resolve(ROOT, f))); +if (missing.length > 0) { + console.error(`Cross-platform test files not found (${missing.length}):`); + for (const f of missing) console.error(` ${f}`); + console.error('\nUpdate scripts/cross-platform-tests.ts if files were moved or removed.'); + process.exit(1); +} + +console.log(`Running ${ALL_CROSS_PLATFORM.length} platform-sensitive tests...\n`); + +try { + execFileSync('npx', ['vitest', 'run', ...ALL_CROSS_PLATFORM], { + cwd: ROOT, + stdio: 'inherit', + timeout: 15 * 60 * 1000, + shell: true, + }); +} catch (err: any) { + if (err.killed || err.signal) { + console.error('vitest timed out after 15 minutes'); + } + process.exit(1); +} diff --git a/gitnexus/scripts/run-parity.ts b/gitnexus/scripts/run-parity.ts new file mode 100644 index 000000000..f56db11bc --- /dev/null +++ b/gitnexus/scripts/run-parity.ts @@ -0,0 +1,128 @@ +/** + * Consolidated scope-resolution parity runner. + * + * Replaces the per-language matrix in ci-scope-parity.yml with a single + * job that runs all migrated languages sequentially in one process. This + * eliminates 8× redundant checkout + npm ci + build cycles (the old + * workflow created a separate GitHub Actions job per language). + * + * For each language in MIGRATED_LANGUAGES: + * 1. Run its resolver test with REGISTRY_PRIMARY_=0 (legacy DAG) + * 2. Run its resolver test with REGISTRY_PRIMARY_=1 (registry-primary) + * + * Both modes must pass. Failures are collected and reported at the end + * so all regressions are visible in a single CI run (equivalent to the + * old workflow's fail-fast: false behavior). + * + * Vitest output streams to the console in real time (stdio: 'inherit') + * so CI logs show the actual test output directly. No per-invocation + * timeout — the CI job-level timeout (30 min) is the outer guard. + * + * Usage: + * npx tsx scripts/run-parity.ts + * npx tsx scripts/run-parity.ts --language python # single language + */ + +import { execFileSync } from 'child_process'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import { MIGRATED_LANGUAGES } from '../src/core/ingestion/registry-primary-flag.js'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..'); + +interface ParityFailure { + lang: string; + mode: 'legacy' | 'registry-primary'; +} + +function envVarName(slug: string): string { + return `REGISTRY_PRIMARY_${slug.toUpperCase().replace(/-/g, '_')}`; +} + +function testFilePath(slug: string): string { + return `test/integration/resolvers/${slug}.test.ts`; +} + +function runVitest(testFile: string, env: Record): boolean { + try { + execFileSync('npx', ['vitest', 'run', testFile], { + cwd: ROOT, + env: { ...process.env, ...env }, + stdio: 'inherit', + shell: true, + }); + return true; + } catch { + return false; + } +} + +// Parse CLI args +const args = process.argv.slice(2); +const langFlag = args.indexOf('--language'); +const singleLang = langFlag >= 0 ? args[langFlag + 1] : undefined; + +if (langFlag >= 0 && singleLang === undefined) { + console.error('--language requires a value'); + process.exit(1); +} + +const languages = singleLang ? [singleLang] : [...MIGRATED_LANGUAGES].map(String); + +// Verify test files exist before running +const missingFiles: string[] = []; +for (const lang of languages) { + const file = path.resolve(ROOT, testFilePath(lang)); + try { + fs.accessSync(file); + } catch { + missingFiles.push(`${testFilePath(lang)} (${lang})`); + } +} + +if (missingFiles.length > 0) { + console.error('Missing resolver test files:'); + for (const f of missingFiles) console.error(` ${f}`); + process.exit(1); +} + +console.log(`Scope-resolution parity: ${languages.length} language(s)`); +console.log(`Languages: ${languages.join(', ')}\n`); + +const failures: ParityFailure[] = []; + +for (const lang of languages) { + const file = testFilePath(lang); + const envVar = envVarName(lang); + + console.log(`\n── ${lang} — legacy DAG (${envVar}=0) ──`); + if (!runVitest(file, { [envVar]: '0' })) { + failures.push({ lang, mode: 'legacy' }); + } + + console.log(`\n── ${lang} — registry-primary (${envVar}=1) ──`); + if (!runVitest(file, { [envVar]: '1' })) { + failures.push({ lang, mode: 'registry-primary' }); + } +} + +// Summary +const total = languages.length * 2; +const passed = total - failures.length; + +console.log('\n═══════════════════════════════════════'); +console.log('PARITY SUMMARY'); +console.log('═══════════════════════════════════════'); +console.log(`Passed: ${passed}/${total}`); + +if (failures.length > 0) { + console.log(`\nFAILURES (${failures.length}):`); + for (const f of failures) { + console.log(` ✗ ${f.lang} [${f.mode}]`); + } + process.exit(1); +} + +console.log('\nAll parity checks passed.'); diff --git a/gitnexus/vitest.config.ts b/gitnexus/vitest.config.ts index 862357668..34ef25467 100644 --- a/gitnexus/vitest.config.ts +++ b/gitnexus/vitest.config.ts @@ -66,6 +66,9 @@ export default defineConfig({ 'test/integration/shape-check-regression.test.ts', 'test/integration/java-class-impact.test.ts', 'test/integration/class-impact-all-languages.test.ts', + 'test/integration/lbug-orphan-sidecar-recovery.test.ts', + 'test/integration/lbug-readonly-init.test.ts', + 'test/integration/analyze-wal-checkpoint-failure.test.ts', ], fileParallelism: false, sequence: { groupOrder: 1 }, @@ -95,6 +98,9 @@ export default defineConfig({ 'test/integration/shape-check-regression.test.ts', 'test/integration/java-class-impact.test.ts', 'test/integration/class-impact-all-languages.test.ts', + 'test/integration/lbug-orphan-sidecar-recovery.test.ts', + 'test/integration/lbug-readonly-init.test.ts', + 'test/integration/analyze-wal-checkpoint-failure.test.ts', 'test/integration/skills-e2e.test.ts', ], },