Files
codegraph/__tests__/extraction-old-git.test.ts
Colby Mchenry 0a9f3de72c fix: make the full test suite pass on Windows (#2053)
* test: close resolution graphs before removing Windows fixtures

Close ArkTS and JVM graph connections in afterEach, including failed assertions. Verified both files on Windows 11 and macOS (32 tests).

* test: compare explore source lines across LF and CRLF checkouts

Normalize only line endings when matching emitted source against checked-out fixtures. Preserve the closure coverage and whole-line assertions. Both files pass on Windows 11 and macOS (15 tests).

* test: exercise old Git fallback without a POSIX shell shim

Inject only the unsupported ls-files invocation and run every other Git command against real repositories. Assert that each rejected call retries, use portable graph paths, and clean up fixtures. Passes on Windows 11 and macOS.

* test: normalize Python fixture before generating CRLF cases

The tests added in #2004 produced CRCRLF from a Windows checkout. Normalize the input to LF before constructing either variant. All 28 native and WASM cases pass on Windows 11 and macOS.

* test: allow time for eviction fixture indexes on Windows

The #2036 eviction test builds and reconciles nine real indexes and consistently exceeds its default five-second timeout on the VM. Allow 15 seconds for this test alone, preserving every assertion. The full file passes on Windows 11 and macOS (21 tests).

* test: finish MCP processes before resetting Windows fixtures

Launch actual servers with their runtime flags and await termination before directory removal. Use asynchronous removal retries for daemon fixtures. Establish a ready daemon and stop its first proxy before simulating PID reuse, then make source stale without opening a competing SQLite writer. Preserve lock and database byte-equality assertions. All touched files validated on Windows 11 and macOS.

* test: allow Windows named-pipe fallback polling to finish

The VM takes up to 25.7 seconds to complete the nominal six-second retry loop. Allow 30 seconds for the three fallback waits and 40 seconds for the two shorter enclosing tests, retaining their lock-preservation and read-only assertions. The full daemon file is validated on Windows 11 and macOS.

* fix(daemon): retry transient Windows PID-file sharing violations

A transient EPERM replacing daemon.pid aborted startup, observed in the quiet-client test and reproduced with a real locked handle at ba3c21e5. Retry only Windows sharing errors with five bounded delays, rechecking ownership each time and cleaning temporary files. Keep publication synchronous before client processing. Cover transient/permanent failures, changed ownership, non-retryable errors, and a real Windows handle; require a confirmed daemon round-trip before measuring a quiet session. Validated on Windows 11 and macOS.
2026-09-27 16:26:10 +00:00

103 lines
4.2 KiB
TypeScript

/**
* Regression: git older than 2.36 rejects `ls-files -s --recurse-submodules` (#1549).
*
* Kept in its own file rather than appended to extraction.test.ts: that suite
* loads every tree-sitter grammar in `beforeAll`, and running a git-scan case
* after it pushed the worker past its memory ceiling.
*/
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { execFileSync } from 'child_process';
import { scanDirectory } from '../src/extraction';
// Fail only the unsupported invocation; repository discovery and fallback Git
// commands still execute against real repositories on every platform.
vi.mock('child_process', async (importOriginal) => {
const actual = await importOriginal<typeof import('child_process')>();
return { ...actual, execFileSync: vi.fn(actual.execFileSync) };
});
function createTempDir(): string {
return fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-test-'));
}
// git < 2.36 rejects `ls-files -s --recurse-submodules` outright: the guard in
// builtin/ls-files.c listed `show_stage` among the modes that die, and it was
// only dropped in 2.36. The die is unconditional — it does not check whether the
// repo has submodules — so on Ubuntu 22.04 (git 2.34.1), Debian 11 (2.30.2) and
// older, every call threw, `getGitVisibleFiles` swallowed it, and the whole
// git-visible path went with it: `includeIgnored`, gitlink recursion and the
// `codegraph.json` `include` allowlist all silently stopped applying (#1549).
//
// Inject the old Git error on any installed version, including Git for Windows.
describe('Old git without `ls-files -s --recurse-submodules` support (#1549)', () => {
let tempDir: string;
const runGit = (cwd: string, ...args: string[]) =>
execFileSync('git', args, { cwd, stdio: 'pipe' });
const makeRepo = (dir: string, base: string) => {
fs.mkdirSync(dir, { recursive: true });
runGit(dir, 'init', '-q');
runGit(dir, 'config', 'user.email', 'test@test.com');
runGit(dir, 'config', 'user.name', 'Test');
fs.writeFileSync(path.join(dir, `${base}.ts`), `export const ${base} = 1;`);
runGit(dir, 'add', '-A');
runGit(dir, 'commit', '-q', '-m', `${base} init`);
};
beforeEach(async () => {
const actual = await vi.importActual<typeof import('child_process')>('child_process');
vi.mocked(execFileSync).mockImplementation(actual.execFileSync);
tempDir = createTempDir();
});
afterEach(() => {
vi.mocked(execFileSync).mockReset();
fs.rmSync(tempDir, { recursive: true, force: true });
});
it('still honours includeIgnored when `ls-files --recurse-submodules` is unsupported', async () => {
const root = path.join(tempDir, 'root');
makeRepo(root, 'a');
// An embedded repo that .gitignore excludes but codegraph.json opts back in.
makeRepo(path.join(root, 'dir_b'), 'b');
fs.writeFileSync(path.join(root, '.gitignore'), 'dir_b/\n');
fs.writeFileSync(
path.join(root, 'codegraph.json'),
JSON.stringify({ includeIgnored: ['dir_b/'] }),
);
runGit(root, 'add', '-A');
runGit(root, 'commit', '-q', '-m', 'ignore dir_b');
// Baseline: the real git resolves both files.
const withRealGit = scanDirectory(root);
expect(withRealGit).toContain('a.ts');
expect(withRealGit).toContain('dir_b/b.ts');
const actual = await vi.importActual<typeof import('child_process')>('child_process');
let rejected = 0;
let retried = 0;
vi.mocked(execFileSync).mockImplementation(((file, args, options) => {
if (file === 'git' && Array.isArray(args) && args[0] === 'ls-files' && args.includes('-s')) {
if (args.includes('--recurse-submodules')) {
rejected++;
throw new Error('fatal: ls-files --recurse-submodules unsupported mode');
}
retried++;
}
return actual.execFileSync(file, args, options);
}) as typeof execFileSync);
// The opted-in file must survive the unsupported-mode failure, not vanish.
const withOldGit = scanDirectory(root);
expect(withOldGit).toContain('a.ts');
expect(withOldGit).toContain('dir_b/b.ts');
expect(rejected).toBeGreaterThan(0);
expect(retried).toBe(rejected);
});
});