mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-18 22:02:49 +08:00
7b219a71c9
Sync swallowed lock-acquisition failures and returned counters indistinguishable from a clean index. Throw LockUnavailableError so the CLI reports failure and the watcher retains its retry behavior without inspecting counters. Preserve quiet output and clean up resources when sync rejects. Cover live locks, recovery, watcher retries, and CLI exit statuses. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
2.7 KiB
TypeScript
81 lines
2.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
import { spawnSync } from 'child_process';
|
|
import * as fs from 'fs';
|
|
import * as os from 'os';
|
|
import * as path from 'path';
|
|
import CodeGraph from '../src/index';
|
|
|
|
const BIN = path.resolve(__dirname, '../dist/bin/codegraph.js');
|
|
|
|
describe('codegraph sync lock contention (#1361)', () => {
|
|
let testDir: string;
|
|
|
|
beforeEach(async () => {
|
|
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-cli-sync-'));
|
|
fs.writeFileSync(path.join(testDir, 'index.ts'), 'export function original() { return 1; }');
|
|
const cg = CodeGraph.initSync(testDir);
|
|
try {
|
|
await cg.indexAll();
|
|
} finally {
|
|
cg.destroy();
|
|
}
|
|
});
|
|
|
|
afterEach(() => {
|
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
});
|
|
|
|
function sync(quiet: boolean) {
|
|
return spawnSync(process.execPath, [BIN, 'sync', testDir, ...(quiet ? ['--quiet'] : [])], {
|
|
encoding: 'utf8',
|
|
timeout: 30_000,
|
|
env: {
|
|
...process.env,
|
|
CODEGRAPH_TELEMETRY: '0', DO_NOT_TRACK: '1',
|
|
CODEGRAPH_NO_PROMPT_HOOK: '1', CODEGRAPH_NO_DAEMON: '1',
|
|
NODE_NO_WARNINGS: '1', NO_COLOR: '1',
|
|
},
|
|
});
|
|
}
|
|
|
|
it.each([false, true])('reports contention and recovers after release (quiet=%s)', (quiet) => {
|
|
fs.writeFileSync(path.join(testDir, 'index.ts'), 'export function changedUnderLock() { return 2; }');
|
|
const lockPath = path.join(testDir, '.codegraph', 'codegraph.lock');
|
|
fs.writeFileSync(lockPath, String(process.pid));
|
|
|
|
const locked = sync(quiet);
|
|
expect(locked.error).toBeUndefined();
|
|
expect(locked.status).toBe(1);
|
|
expect(locked.stdout + locked.stderr).not.toContain('Already up to date');
|
|
if (quiet) {
|
|
expect(locked.stdout + locked.stderr).toBe('');
|
|
} else {
|
|
expect(locked.stdout + locked.stderr).toMatch(/busy|lock/i);
|
|
expect(locked.stdout + locked.stderr).toMatch(/retry/i);
|
|
}
|
|
expect(fs.readFileSync(lockPath, 'utf8')).toBe(String(process.pid));
|
|
const before = CodeGraph.openSync(testDir);
|
|
try {
|
|
expect(before.searchNodes('changedUnderLock')).toHaveLength(0);
|
|
} finally {
|
|
before.destroy();
|
|
}
|
|
|
|
fs.unlinkSync(lockPath);
|
|
const unlocked = sync(quiet);
|
|
expect(unlocked.error).toBeUndefined();
|
|
expect(unlocked.status).toBe(0);
|
|
const after = CodeGraph.openSync(testDir);
|
|
try {
|
|
expect(after.searchNodes('changedUnderLock')).toHaveLength(1);
|
|
} finally {
|
|
after.destroy();
|
|
}
|
|
|
|
const unchanged = sync(quiet);
|
|
expect(unchanged.status).toBe(0);
|
|
if (quiet) expect(unchanged.stdout + unchanged.stderr).toBe('');
|
|
else expect(unchanged.stdout).toContain('Already up to date');
|
|
});
|
|
});
|