mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-01 02:01:24 +00:00
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* fix(lbug): self-heal read-only opens refused by an interrupted checkpoint Homelab repro 2026-09-19 (image 1.6.10-20260917, @ladybugdb/core 0.19.x): a wiki pod killed mid-CHECKPOINT left the engine's checkpoint artifacts on disk (lbug.wal.checkpoint / lbug.shadow / checkpoint intent+apply locks), and every later READ-ONLY open refused with 'Cannot open database in read-only mode while checkpoint is in progress' — permanently, until a writable open (any gitnexus analyze) happened to run. Two defects, both fixed: 1. The refusal was unclassified. ensureReadOnlyConnectionUsable (direct adapter) and openReadOnlyDatabase (pool) recovered missing-shadow and shadow-replay errors but rethrew this one raw, so 'LadybugDB unavailable for __wiki__' repeated forever. Add isReadOnlyCheckpointInProgressError (LADYBUGDB-CONTRACT, live-verified against 0.19.1) and route it through the same writable-open recovery — including at OPEN time, where the refusal fires before any probe can run (pool: init() moved inside the try; direct: doInitLbug catch). 2. The existing shadow-replay recovery was not durable. Reproduction matrix on 0.19.1: the writable probe replays the WAL in MEMORY only — without an explicit CHECKPOINT the engine drops the pages at close and the follow-up read-only open silently serves the pre-checkpoint state. Both recovery paths now CHECKPOINT after the probe, which applies the replay, consumes the sidecars, and clears the checkpoint locks. Verified end-to-end: real engine 0.19.1, killed-mid-CHECKPOINT state → exact refusal → pool adapter self-heals → 80,800 rows intact, sidecars consumed. The planted-signature integration test runs on any engine version (refusal asserted only where the engine emits it, 0.19+). * docs(architecture): list checkpoint-in-flight artifacts and the read-path self-heal * fix(lbug): address review findings — shared cursor closer, tighten structural guard Both findings from the gitnexus-check review on this PR: 1. pool-adapter.ts: the recovery CHECKPOINT closed its cursor with a bare unawaited result.close?.(). Use the shared best-effort closer (closeQueryResults) the repo already funnels both adapters through, so a cursor-close failure stays cleanup and cannot escape as an unhandled rejection. 2. sidecar-recovery.test.ts: the structural regex omitted the leading negation, so as a substring match it also accepted the inverted predicate (recover ONLY shadow-replay, exclude checkpoint) — the exact regression the guard exists to prevent. Pin the full '!isReadOnlyShadowReplayError(err) && !isReadOnlyCheckpointInProgressError(err)' throw-through shape. * test(lbug): wire the recovery plant into lbug-db/LBUG_NATIVE; force the refusal on any engine pin Address the tri-review findings (all four): P1 — the planted-signature integration test was collected by the parallel 'default' project and never by the serialized 'lbug-db' project, and Windows/macOS CI never ran it: register it next to its sibling in vitest.config.ts (lbug-db include + default exclude) and in cross-platform-tests.ts LBUG_NATIVE, per TESTING.md's rule for native @ladybugdb/core suites. Verified via 'vitest list --project lbug-db'. P2 — on the committed 0.18.3 pin the plant passes as 'pool opens and count(n)=300' without ever exercising the new classifier or recovery CHECKPOINT. Add forced-refusal behavioral tests for BOTH adapters: a mocked native layer whose first read-only Database refuses with the canonical 0.19 message, asserting the exact self-heal shape (ro-refused -> writable open -> CHECKPOINT -> ro retry) and that a healthy db never triggers a writable open. The direct adapter is lazy, so its refusal is scripted at the first probe query rather than init(). P2 — the LADYBUGDB-CONTRACT header on isReadOnlyCheckpointInProgressError claimed '^0.18.0' like its siblings; only 0.19.x emits this string (0.18.3 tolerates the state). State the first-observed version so a bump reviewer validates the right binary. P2 — the pool's writable replay recovery quarantined on missing-shadow even when the error came from the post-probe CHECKPOINT, where the main file has already changed: mirror the direct adapter's probeSucceeded guard (replaySucceeded) and fail closed instead of parking a live sidecar. Also assert walBuffer.byteLength > 0 in the plant so the fixture cannot silently degrade into an empty shell on tolerant engines. * test(lbug): fix hosted-CI failures — Windows handle release, version-gated plant, prettier Address the CHANGES_REQUESTED review of the hosted run: Windows blocker (Win32 Error 33, locked file region at the pooled reopen): the fixture now makes handle release explicit — waitForFixtureRelease probe-reads the db and its residual WAL with bounded retries after every native close (plant, raw refusal probe, pool close), mirroring the adapter's own Windows handle-release probing. The WAL is re-planted from the captured bytes instead of renamed: a close-time auto-checkpoint can consume the live .wal out from under the rename (ENOENT, second flake). Version-gate the plant itself: on < 0.19 engines that tolerate the planted signature, its synthetic sidecars are not a consistent staging state for the old engine (double-apply replays surfaced as 'Person already exists in catalog' — the third flake), and no refusal can be forced there anyway. The plant now skips below 0.19 with that rationale in-file; the behavioral contract on every pin stays with the forced-refusal units, and this native suite remains registered in lbug-db + LBUG_NATIVE so Win/macOS exercise it as soon as the pin moves off 0.18.3. Also: prettier on the two flagged files (format gate). * fix(lbug): keep failed checkpoint heals from re-entering CHECKPOINT Wrap recovery failures without repeating native refusal text, skip already-wrapped errors in doInitLbug, and pin constructor-time heal plus the Windows reopen skip. Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3340) - Clean each forced-heal tmpDir in afterEach so earlier cases do not leak - Compare major.minor when version-gating the interrupted-checkpoint plant Co-authored-by: Cursor <cursoragent@cursor.com> * Address PR review feedback (#3340) Reset the pool forced-refusal mock Database sequence in native.reset() so later tests can still script the first construction as the checkpoint victim. * Address PR review feedback (#3340) Count every MATCH on the pool forced-refusal mock and assert the writable replay probe so CHECKPOINT-without-probe cannot stay green. --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
224 lines
12 KiB
TypeScript
224 lines
12 KiB
TypeScript
import { defineConfig } from 'vitest/config';
|
|
import PerfSequencer from './test/helpers/perf-sequencer.js';
|
|
|
|
export default defineConfig({
|
|
test: {
|
|
// Shared settings — inherited by all projects via extends: true
|
|
testTimeout: 30000,
|
|
hookTimeout: 120000,
|
|
pool: 'forks',
|
|
globals: true,
|
|
teardownTimeout: 3000,
|
|
// E2E harnesses pin a small NODE_OPTIONS heap so spawned CLI children
|
|
// stay light; without this opt-out the #2649 auto-heap override would
|
|
// respawn every such child with a RAM-sized cap. Children inherit it via
|
|
// the harnesses' `{ ...process.env }` spreads. Tests that exercise the
|
|
// respawn behavior itself delete GITNEXUS_MEMORY in their own setup.
|
|
// Tests assert the English CLI contract unless a case opts into another
|
|
// language explicitly. Do not inherit a developer shell's CLI locale.
|
|
env: { GITNEXUS_MEMORY: 'off', GITNEXUS_LANG: 'en' },
|
|
// N-API destructors can crash worker forks on macOS during process exit.
|
|
// This is independent of the QueryResult lifetime fix in @ladybugdb/core 0.15.2 —
|
|
// it's a vitest forks + native addon interaction where destructors run in
|
|
// arbitrary order at exit. Tests themselves pass; only the exit crashes.
|
|
// TODO: remove once LadybugDB fixes all N-API destructor ordering issues.
|
|
dangerouslyIgnoreUnhandledErrors: true,
|
|
|
|
// Coverage stays at root (not supported in project configs)
|
|
coverage: {
|
|
provider: 'v8',
|
|
include: ['src/**/*.ts'],
|
|
exclude: [
|
|
'src/cli/index.ts', // CLI entry point (commander wiring)
|
|
'src/server/**', // HTTP server (requires network)
|
|
'src/core/wiki/**', // Wiki generation (requires LLM)
|
|
],
|
|
// Auto-ratchet: vitest bumps thresholds when coverage exceeds them.
|
|
// CI will fail if a PR drops below these floors.
|
|
thresholds: {
|
|
statements: 26,
|
|
branches: 23,
|
|
functions: 28,
|
|
lines: 27,
|
|
},
|
|
},
|
|
|
|
// Balance shards by estimated work rather than file count, so the
|
|
// spawn-heavy sequential suites spread evenly across shard runners instead
|
|
// of clustering onto one (see test/helpers/perf-sequencer.ts). Only shard()
|
|
// is overridden — groupOrder and sort order are left to the base sequencer.
|
|
sequence: {
|
|
sequencer: PerfSequencer,
|
|
},
|
|
|
|
// LadybugDB's native mmap addon causes file-lock conflicts when vitest
|
|
// runs lbug test files in parallel forks on Windows. The 'lbug-db'
|
|
// project forces sequential execution (fileParallelism: false).
|
|
//
|
|
// Each file runs in its own fork — the fork exits after the file
|
|
// completes, triggering an N-API destructor segfault that is caught
|
|
// by dangerouslyIgnoreUnhandledErrors. Tests themselves pass; only
|
|
// the exit crashes. This is safer than isolate: false, which causes
|
|
// native state corruption after 2-3 open/close cycles in the same fork.
|
|
projects: [
|
|
{
|
|
extends: true,
|
|
test: {
|
|
name: 'lbug-db',
|
|
include: [
|
|
'test/integration/skip-fts.test.ts',
|
|
'test/integration/impact-callable-value-references.test.ts',
|
|
'test/integration/impact-epistemic-lower-bound.test.ts',
|
|
'test/integration/impact-scope-omission-persistence.test.ts',
|
|
'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/local-backend.test.ts',
|
|
'test/integration/local-backend-calltool.test.ts',
|
|
'test/integration/spring-aop-mcp.test.ts',
|
|
// Native @ladybugdb/core: loads a real graph through loadGraphToLbug
|
|
// and asserts the Destination NULL/join invariant on the stored rows.
|
|
'test/integration/spring-destinations-lbug.test.ts',
|
|
'test/integration/spring-destinations-incremental.test.ts',
|
|
'test/integration/search-core.test.ts',
|
|
'test/integration/search-pool.test.ts',
|
|
'test/integration/fts-description-search.test.ts',
|
|
'test/integration/fts-fullfile-search.test.ts',
|
|
'test/integration/fts-cjk-segmentation-search.test.ts',
|
|
'test/integration/augmentation.test.ts',
|
|
'test/integration/staleness-and-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/api-impact-e2e.test.ts',
|
|
'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-interrupted-checkpoint-recovery.test.ts',
|
|
'test/integration/lbug-readonly-init.test.ts',
|
|
'test/integration/analyze-wal-checkpoint-failure.test.ts',
|
|
'test/integration/lbug-non-ascii-path.test.ts',
|
|
'test/integration/lbug-conn-serialization.test.ts',
|
|
'test/integration/load-cached-embeddings-spill.test.ts',
|
|
'test/integration/group/manifest-resolve-symbol-2325.test.ts',
|
|
'test/integration/group/manifest-synthetic-impact-lbug.test.ts',
|
|
'test/integration/group/http-route-resolve-symbol.test.ts',
|
|
'test/integration/group/graphql-resolve-symbol.test.ts',
|
|
'test/integration/fts-stemmer-sweep.test.ts',
|
|
'test/integration/lbug-multiwriter-deadlock.test.ts',
|
|
'test/integration/extension-binary-real.test.ts',
|
|
'test/integration/lbug-delete-nodes-for-files.test.ts',
|
|
'test/integration/lbug-query-importers-batch.test.ts',
|
|
'test/integration/impact-ambiguous-blast-radius.test.ts',
|
|
// #2915. Native @ladybugdb/core via withTestLbugDB(poolAdapter:true),
|
|
// and it drives detect_changes over a real git repo — the mmap
|
|
// file-lock exposure this project serializes (TESTING.md § Vitest
|
|
// projects), on the Windows/macOS platforms #2915 was reported from.
|
|
'test/integration/detect-changes-path-anchoring.test.ts',
|
|
// #2915. Native @ladybugdb/core via withTestLbugDB(poolAdapter:true) —
|
|
// the wiki's graph queries executed by a real engine rather than a
|
|
// fake that answers on `query.includes(...)`.
|
|
'test/integration/wiki-graph-queries-engine.test.ts',
|
|
'test/unit/incremental-dirty-recovery.test.ts',
|
|
'test/unit/incremental-orchestration.test.ts',
|
|
// #2841. Native @ladybugdb/core: it runs real analyses, reopens the
|
|
// DB under different extension-install policies, and reads
|
|
// SHOW_INDEXES on the writable connection — exactly the mmap
|
|
// file-lock exposure this project exists to serialize (TESTING.md
|
|
// § Vitest projects). Registering it here does NOT narrow where it
|
|
// runs: vitest applies `--shard` once to the combined cross-project
|
|
// spec list (PerfSequencer/assignShards is a complete, disjoint
|
|
// partition), and run-cross-platform.ts hands vitest explicit file
|
|
// paths, which resolve against every project's include list. Its
|
|
// `incremental-vector-extension-ordering` /
|
|
// `incremental-fts-drop-ordering` siblings are equally native and
|
|
// still sit in `default` — pre-existing drift, deliberately left
|
|
// alone here.
|
|
'test/unit/incremental-index-extension-dml-gate.test.ts',
|
|
],
|
|
fileParallelism: false,
|
|
sequence: { groupOrder: 1 },
|
|
},
|
|
},
|
|
{
|
|
extends: true,
|
|
test: {
|
|
name: 'default',
|
|
sequence: { groupOrder: 3 },
|
|
include: ['test/**/*.test.ts'],
|
|
exclude: [
|
|
'test/integration/skip-fts.test.ts',
|
|
'test/integration/impact-callable-value-references.test.ts',
|
|
'test/integration/impact-epistemic-lower-bound.test.ts',
|
|
'test/integration/impact-scope-omission-persistence.test.ts',
|
|
'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/local-backend.test.ts',
|
|
'test/integration/local-backend-calltool.test.ts',
|
|
'test/integration/spring-aop-mcp.test.ts',
|
|
'test/integration/spring-destinations-lbug.test.ts',
|
|
'test/integration/spring-destinations-incremental.test.ts',
|
|
'test/integration/search-core.test.ts',
|
|
'test/integration/search-pool.test.ts',
|
|
'test/integration/fts-description-search.test.ts',
|
|
'test/integration/fts-fullfile-search.test.ts',
|
|
'test/integration/fts-cjk-segmentation-search.test.ts',
|
|
'test/integration/augmentation.test.ts',
|
|
'test/integration/staleness-and-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/api-impact-e2e.test.ts',
|
|
'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-interrupted-checkpoint-recovery.test.ts',
|
|
'test/integration/lbug-readonly-init.test.ts',
|
|
'test/integration/analyze-wal-checkpoint-failure.test.ts',
|
|
'test/integration/lbug-non-ascii-path.test.ts',
|
|
'test/integration/lbug-conn-serialization.test.ts',
|
|
'test/integration/load-cached-embeddings-spill.test.ts',
|
|
'test/integration/group/manifest-resolve-symbol-2325.test.ts',
|
|
'test/integration/group/manifest-synthetic-impact-lbug.test.ts',
|
|
'test/integration/group/http-route-resolve-symbol.test.ts',
|
|
'test/integration/group/graphql-resolve-symbol.test.ts',
|
|
'test/integration/skills-e2e.test.ts',
|
|
'test/integration/fts-extension-e2e.test.ts',
|
|
'test/integration/fts-stemmer-sweep.test.ts',
|
|
'test/integration/lbug-multiwriter-deadlock.test.ts',
|
|
'test/integration/extension-binary-real.test.ts',
|
|
'test/integration/lbug-delete-nodes-for-files.test.ts',
|
|
'test/integration/lbug-query-importers-batch.test.ts',
|
|
'test/integration/impact-ambiguous-blast-radius.test.ts',
|
|
'test/integration/detect-changes-path-anchoring.test.ts',
|
|
'test/integration/wiki-graph-queries-engine.test.ts',
|
|
'test/unit/incremental-dirty-recovery.test.ts',
|
|
'test/unit/incremental-orchestration.test.ts',
|
|
// Excluded here because it is included by `lbug-db` above; a file
|
|
// in two projects would be collected (and run) twice.
|
|
'test/unit/incremental-index-extension-dml-gate.test.ts',
|
|
],
|
|
},
|
|
},
|
|
{
|
|
extends: true,
|
|
test: {
|
|
name: 'cli-e2e',
|
|
include: [
|
|
'test/integration/skills-e2e.test.ts',
|
|
// Spawns the real CLI per test; runs sequentially (fileParallelism:
|
|
// false) so it doesn't aggravate the under-load timeout-flake class.
|
|
'test/integration/fts-extension-e2e.test.ts',
|
|
],
|
|
fileParallelism: false,
|
|
sequence: { groupOrder: 2 },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|