mirror of https://github.com/garrytan/gstack.git
feat: integrate worktree isolation into E2E test infrastructure
Add createTestWorktree(), harvestAndCleanup(), and describeWithWorktree() helpers to e2e-helpers.ts. Add harvest field to EvalTestEntry for eval-store integration. Register lib/worktree.ts as a global touchfile. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
0f1f1eb1c5
commit
df3accea16
|
|
@ -5,11 +5,13 @@
|
||||||
* tests across multiple files by category.
|
* tests across multiple files by category.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, test, afterAll } from 'bun:test';
|
import { describe, test, beforeAll, afterAll } from 'bun:test';
|
||||||
import type { SkillTestResult } from './session-runner';
|
import type { SkillTestResult } from './session-runner';
|
||||||
import { EvalCollector, judgePassed } from './eval-store';
|
import { EvalCollector, judgePassed } from './eval-store';
|
||||||
import type { EvalTestEntry } from './eval-store';
|
import type { EvalTestEntry } from './eval-store';
|
||||||
import { selectTests, detectBaseBranch, getChangedFiles, E2E_TOUCHFILES, GLOBAL_TOUCHFILES } from './touchfiles';
|
import { selectTests, detectBaseBranch, getChangedFiles, E2E_TOUCHFILES, GLOBAL_TOUCHFILES } from './touchfiles';
|
||||||
|
import { WorktreeManager } from '../../lib/worktree';
|
||||||
|
import type { HarvestResult } from '../../lib/worktree';
|
||||||
import { spawnSync } from 'child_process';
|
import { spawnSync } from 'child_process';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
@ -234,6 +236,59 @@ export function testConcurrentIfSelected(testName: string, fn: () => Promise<voi
|
||||||
(shouldRun ? test.concurrent : test.skip)(testName, fn, timeout);
|
(shouldRun ? test.concurrent : test.skip)(testName, fn, timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Worktree isolation ---
|
||||||
|
|
||||||
|
let worktreeManager: WorktreeManager | null = null;
|
||||||
|
|
||||||
|
export function getWorktreeManager(): WorktreeManager {
|
||||||
|
if (!worktreeManager) {
|
||||||
|
worktreeManager = new WorktreeManager();
|
||||||
|
worktreeManager.pruneStale();
|
||||||
|
}
|
||||||
|
return worktreeManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create an isolated worktree for a test. Returns the worktree path. */
|
||||||
|
export function createTestWorktree(testName: string): string {
|
||||||
|
return getWorktreeManager().create(testName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Harvest changes and clean up. Call in afterAll(). Returns HarvestResult for eval integration. */
|
||||||
|
export function harvestAndCleanup(testName: string): HarvestResult | null {
|
||||||
|
const mgr = getWorktreeManager();
|
||||||
|
const result = mgr.harvest(testName);
|
||||||
|
if (result) {
|
||||||
|
if (result.isDuplicate) {
|
||||||
|
process.stderr.write(`\n HARVEST [${testName}]: duplicate patch (skipped)\n`);
|
||||||
|
} else {
|
||||||
|
process.stderr.write(`\n HARVEST [${testName}]: ${result.changedFiles.length} files changed\n`);
|
||||||
|
process.stderr.write(` Patch: ${result.patchPath}\n`);
|
||||||
|
process.stderr.write(` ${result.diffStat}\n\n`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mgr.cleanup(testName);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience: describe block with automatic worktree isolation + harvest.
|
||||||
|
* Any test file can use this to get real repo context instead of a tmpdir.
|
||||||
|
* Note: tests with planted-bug fixtures should NOT use this — they need their fixture repos.
|
||||||
|
*/
|
||||||
|
export function describeWithWorktree(
|
||||||
|
name: string,
|
||||||
|
testNames: string[],
|
||||||
|
fn: (getWorktreePath: () => string) => void,
|
||||||
|
) {
|
||||||
|
describeIfSelected(name, testNames, () => {
|
||||||
|
let worktreePath: string;
|
||||||
|
beforeAll(() => { worktreePath = createTestWorktree(name); });
|
||||||
|
afterAll(() => { harvestAndCleanup(name); });
|
||||||
|
fn(() => worktreePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export { judgePassed } from './eval-store';
|
export { judgePassed } from './eval-store';
|
||||||
export { EvalCollector } from './eval-store';
|
export { EvalCollector } from './eval-store';
|
||||||
export type { EvalTestEntry } from './eval-store';
|
export type { EvalTestEntry } from './eval-store';
|
||||||
|
export type { HarvestResult } from '../../lib/worktree';
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,13 @@ export interface EvalTestEntry {
|
||||||
missed_bugs?: string[];
|
missed_bugs?: string[];
|
||||||
|
|
||||||
error?: string;
|
error?: string;
|
||||||
|
|
||||||
|
// Worktree harvest data
|
||||||
|
harvest?: {
|
||||||
|
filesChanged: number;
|
||||||
|
patchPath: string;
|
||||||
|
isDuplicate: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EvalResult {
|
export interface EvalResult {
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ export const GLOBAL_TOUCHFILES = [
|
||||||
'scripts/gen-skill-docs.ts',
|
'scripts/gen-skill-docs.ts',
|
||||||
'test/helpers/touchfiles.ts',
|
'test/helpers/touchfiles.ts',
|
||||||
'browse/test/test-server.ts',
|
'browse/test/test-server.ts',
|
||||||
|
'lib/worktree.ts',
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- Base branch detection ---
|
// --- Base branch detection ---
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue