mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-28 07:32:14 +02:00
* fix(browse): prepare reliable cookie import wave for validation * ci: sequence quality and behavior for validation branch * fix(browse): isolate Windows qualification and preserve native diagnostics * test(browse): cover cookie workflow quality and isolate Windows user paths * test(browse): trace native member startup and initialize fresh folders * fix(browse): keep Windows member stdin alive through EOF * fix(browse): latch native timeouts and compare contained Edge startup * test(browse): verify native version metadata and actual Windows argv * test(browse): qualify Dia import on isolated macOS CI * fix(browse): require picker origin for session mutations * fix(browse): bound credential reads through stream completion * test(browse): inspect owned Windows process arguments natively * test(evals): preserve passing coverage during cookie repair reruns * test(browse): isolate Dia qualification in a fresh macOS account * test(browse): pass bounded integer timeouts to native Mac probes * test(browse): distinguish Windows profile initialization from containment * test(browse): await descendant pipe readiness before parent exit * test(browse): initialize and restore isolated macOS Keychain state * test(browse): initialize Windows fixture folders before qualification * test(ci): pin the same Node runtime across Windows checks * test(browse): distinguish native macOS browser preflight stages * test(browse): isolate Windows descendant console lifetime * test(browse): preserve native receipts and identify fixture lock holders * test(browse): prepare dependency resolution before native Mac worker startup * test(ci): include lock and close checks in native diagnostics * test(browse): preserve native owner probe stages and subprocess deadlines * fix(browse): classify Chromium profile-in-use exit precisely * test(browse): retain Mac qualification evidence through cleanup failures * test(browse): bound Mac fixture paths and retire its owned user domain * test(browse): accept vanished fixture entries without weakening cleanup * test(browse): identify probe-created macOS user domains safely * test(browse): observe Mac user domains without targeting them first * test(browse): use passive fresh-user ownership throughout Mac qualification * test(browse): distinguish profile and registered-home Keychain lookups * test(browse): qualify Dia under one registered account home * test(browse): identify Dia startup and owned process-group failures * test(browse): classify bounded Dia startup diagnostics without leaking output * fix(test): preserve native Mac sandboxing and reap owned browser children * fix(browse): preserve Chromium sandboxing for native profile imports * test(browse): inspect signed Mach-O architecture without launching Xcode tools * test(browse): sample pending Dia startup and reap on all cleanup paths * test(browse): compare protected Dia launches in fresh Bun and Node accounts * test(browse): inspect isolated Mac GUI readiness without browser access * v1.90.0.0 fix: bind cookie picker actions to their document * test: validate cookie guards and fit nested launch fixtures * ci: configure the bundled Chromium sandbox helper * fix(browse): classify Playwright authentication timeouts * test: retain bounded Windows lifecycle diagnostics * test(cso): reuse bounded NTFS precision candidates * test(review): handle explicit preservation choices safely * test(browse): remove owned fixture directories with explicit primitives * test(review): distinguish descriptive reuse from edit commitments * test: admit only the approved unscored cookie workflow refusal * test: keep the Office Hours judge mock export-complete * fix: keep dependency-free CI planners independent of the model SDK * test: observe the exact holder after a native fixture unlink failure * fix: start seeded PTY observations at owned readiness * test: acquire identity-bound Windows deletion admission before profile resets * test: preserve qualified Git index bits without authorizing mutations
215 lines
14 KiB
TypeScript
215 lines
14 KiB
TypeScript
import { expect, test } from 'bun:test';
|
|
import { resolvePaidShardBudget, retriesForFiles, planPaidShards, parseRunManifest, verifySliceResults, runPaidShard, buildRunManifest, paidShardWallUpperBoundMs, collectPaidTestFiles, selectPaidTestFiles, isOverlayTestFile, OVERLAY_MAX_ACTIVE_SHARDS, DEFAULT_SHARD_TIMEOUT_MS, DEFAULT_JOBS } from '../scripts/test-paid-shards';
|
|
import { FINDING_RETRY_BUDGETS, ALL_TIERS, AUTOPLAN_CHAIN_BUDGET } from './helpers/eval-budgets';
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
|
|
for (const budget of FINDING_RETRY_BUDGETS) {
|
|
test(`${budget.file}: supervision preserves every existing attempt and retry`, () => {
|
|
expect(budget.testMs).toBe(1_500_000);
|
|
expect(budget.retries).toBe(1);
|
|
expect(retriesForFiles([budget.file])).toBe(budget.retries);
|
|
expect(budget.shardReserveMs).toBe(AUTOPLAN_CHAIN_BUDGET.shardReserveMs);
|
|
expect(budget.shardMs).toBe(budget.cases * budget.testMs * (budget.retries + 1) + budget.shardReserveMs);
|
|
expect(resolvePaidShardBudget([budget.file])).toEqual({ timeoutMs: budget.shardMs, source: 'registered', policyId: budget.id });
|
|
const source = fs.readFileSync(path.join(import.meta.dir, '..', budget.file), 'utf8');
|
|
if (budget.file === 'test/skill-e2e-plan-ceo-split-overflow.test.ts') {
|
|
// This case shares its unchanged allowance with final semantic validation.
|
|
// Its actual registration adapter also verifies the elapsed-time routing.
|
|
expect([...source.matchAll(/const deadlineAt = Date\.now\(\) \+ 1_500_000;/g)]).toHaveLength(budget.cases);
|
|
expect([...source.matchAll(/timeoutMs:\s*deadlineAt - Date\.now\(\)/g)]).toHaveLength(budget.cases);
|
|
expect(source).toContain("floor: FLOOR, kind: 'scope', deadlineAt");
|
|
} else if (budget.file === 'test/skill-e2e-plan-eng-finding-count.test.ts') {
|
|
// Its terminal assessment shares the original allowance with the actor.
|
|
expect([...source.matchAll(/const startedAt = Date\.now\(\);/g)]).toHaveLength(budget.cases);
|
|
expect([...source.matchAll(/const deadlineAt = startedAt \+ 1_500_000;/g)]).toHaveLength(budget.cases);
|
|
expect([...source.matchAll(/timeoutMs:\s*deadlineAt - Date\.now\(\)/g)]).toHaveLength(budget.cases);
|
|
expect(source).toContain('deadlineAt: Math.min(input.deadlineAt, deadlineAt)');
|
|
} else {
|
|
expect([...source.matchAll(/timeoutMs:\s*1_500_000\b/g)]).toHaveLength(budget.cases);
|
|
}
|
|
expect([...source.matchAll(/1_500_000\s*\/\* physical ceiling:/g)]).toHaveLength(budget.cases);
|
|
// Current periodic CI already supports this supervision wall.
|
|
const workflow = fs.readFileSync(path.join(import.meta.dir, '../.github/workflows/evals-periodic.yml'), 'utf8');
|
|
expect(workflow).toMatch(/timeout-minutes: 355/);
|
|
expect(budget.shardMs).toBeLessThan(355 * 60_000);
|
|
});
|
|
|
|
test(`${budget.file}: own-shard allocation leaves ordinary and explicit limits intact`, () => {
|
|
expect(() => resolvePaidShardBudget([budget.file, 'test/other.test.ts'])).toThrow('own shard');
|
|
const shards = planPaidShards(['test/a.test.ts', budget.file, 'test/z.test.ts'], { maxFilesPerShard: 3 });
|
|
expect(shards).toContainEqual([budget.file]);
|
|
expect(shards.flat().sort()).toEqual(['test/a.test.ts', budget.file, 'test/z.test.ts'].sort());
|
|
expect(resolvePaidShardBudget([budget.file], 123)).toEqual({ timeoutMs: 123, source: 'explicit', policyId: budget.id });
|
|
});
|
|
|
|
const planned = () => ({ version: 1 as const, tier: 'periodic' as const, evalsAll: true, sliceCount: 1, selectionReason: 'budget regression',
|
|
entries: [{ file: budget.file, slice: 1, status: 'planned' as const, budget: resolvePaidShardBudget([budget.file]) }] });
|
|
const results = (manifest = planned()) => [{ version: 1 as const, tier: 'periodic' as const, sliceIndex: 1, sliceCount: 1,
|
|
outcomes: [{ files: [budget.file], status: 'passed' as const, exitCode: 0, elapsedMs: 1, executedTests: budget.cases, skippedTests: 0, budget: manifest.entries[0]!.budget }] }];
|
|
|
|
test(`${budget.file}: manifest and result retain exact allocation and authenticated subsets`, () => {
|
|
const m = planned(); expect(parseRunManifest(JSON.stringify(m))).toEqual(m);
|
|
expect(verifySliceResults(m, results(m))).toEqual({ ok: true, problems: [] });
|
|
const selected = results(m); selected[0]!.outcomes[0]!.executedTests = 1;
|
|
expect(verifySliceResults(m, selected).ok).toBe(budget.cases === 1);
|
|
expect(verifySliceResults({ ...m, evalsAll: false }, selected)).toEqual({ ok: true, problems: [] });
|
|
expect(verifySliceResults({ ...m, evalsAll: undefined } as any, selected).ok).toBe(budget.cases === 1);
|
|
m.entries[0]!.budget = resolvePaidShardBudget([budget.file], 123);
|
|
expect(verifySliceResults(m, results(m))).toEqual({ ok: true, problems: [] });
|
|
});
|
|
for (const mutation of ['missing', 'wrong-time', 'wrong-policy', 'duplicate'] as const) test(`${budget.file}: rejects ${mutation} manifest budget`, () => {
|
|
const m: any = planned();
|
|
if (mutation === 'missing') delete m.entries[0].budget;
|
|
if (mutation === 'wrong-time') m.entries[0].budget.timeoutMs--;
|
|
if (mutation === 'wrong-policy') m.entries[0].budget.policyId = 'foreign';
|
|
if (mutation === 'duplicate') m.entries.push(structuredClone(m.entries[0]));
|
|
expect(() => parseRunManifest(JSON.stringify(m))).toThrow();
|
|
});
|
|
for (const mutation of ['wrong-time', 'missing', 'skipped', 'no-case', 'extra-case', 'fractional', 'nonzero', 'packed'] as const) test(`${budget.file}: rejects ${mutation} execution evidence`, () => {
|
|
const m = planned(), r: any = results(m), o = r[0].outcomes[0];
|
|
if (mutation === 'wrong-time') o.budget = { ...o.budget, timeoutMs: 1800000 };
|
|
if (mutation === 'missing') delete o.budget;
|
|
if (mutation === 'skipped') o.skippedTests = 1;
|
|
if (mutation === 'no-case') o.executedTests = 0;
|
|
if (mutation === 'extra-case') o.executedTests = budget.cases + 1;
|
|
if (mutation === 'fractional') o.executedTests = 0.5;
|
|
if (mutation === 'nonzero') o.exitCode = 1;
|
|
if (mutation === 'packed') o.files.push('test/other.test.ts');
|
|
expect(verifySliceResults(m, r).ok).toBe(false);
|
|
});
|
|
test(`${budget.file}: packed neighbor cannot also claim the separately reported workflow`, () => {
|
|
const m: any = planned(), r: any = results(m);
|
|
m.entries.push({ file: 'test/other.test.ts', slice: 1, status: 'planned' });
|
|
r[0].outcomes.push({ files: ['test/other.test.ts', budget.file], status: 'passed', exitCode: 0, elapsedMs: 1, executedTests: 1, skippedTests: 0 });
|
|
expect(verifySliceResults(m, r).ok).toBe(false);
|
|
});
|
|
}
|
|
|
|
test('ordinary tiers and Autoplan allocations remain unchanged', () => {
|
|
expect(ALL_TIERS).toEqual({ JUDGE_MS: 120000, CAPTURE_MS: 300000, CAPTURE_LONG_MS: 600000, PTY_MS: 900000, PTY_LONG_MS: 1200000 });
|
|
expect(resolvePaidShardBudget(['test/other.test.ts'])).toEqual({ timeoutMs: 1800000, source: 'default', policyId: null });
|
|
expect(resolvePaidShardBudget([AUTOPLAN_CHAIN_BUDGET.file])).toEqual({ timeoutMs: AUTOPLAN_CHAIN_BUDGET.shardMs, source: 'registered', policyId: AUTOPLAN_CHAIN_BUDGET.id });
|
|
expect(new Set(FINDING_RETRY_BUDGETS.map(b => b.file)).size).toBe(6);
|
|
});
|
|
|
|
test('actual shard launcher honors the explicit saved planner limit without a provider', async () => {
|
|
const budget = FINDING_RETRY_BUDGETS.find(b => b.file.includes('plan-eng-finding-count'))!;
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'finding-retry-wall-'));
|
|
try {
|
|
const outcome = await runPaidShard([budget.file], 1, 1, { rootDir: dir, logDir: dir, jobs: 2,
|
|
registeredBudgets: { [budget.file]: resolvePaidShardBudget([budget.file], 50) },
|
|
commandFor: () => ({ command: process.execPath, args: ['-e', 'setTimeout(()=>{},10000)'] }),
|
|
log: () => {}, env: { ...process.env, EVALS: '' } });
|
|
expect(outcome.status).toBe('timed-out');
|
|
expect(outcome.budget).toEqual({ timeoutMs: 50, source: 'explicit', policyId: budget.id });
|
|
expect(outcome.elapsedMs).toBeLessThan(5000);
|
|
} finally { fs.rmSync(dir, { recursive: true, force: true }); }
|
|
}, 10000);
|
|
|
|
const periodicWorkflow = Bun.YAML.parse(fs.readFileSync(path.join(import.meta.dir, '../.github/workflows/evals-periodic.yml'), 'utf8')) as any;
|
|
const periodicJob = periodicWorkflow.jobs['eval-slices'];
|
|
const periodicPlanStep = periodicWorkflow.jobs['plan-slices'].steps.find((step: any) => step.run?.includes('--tier periodic --emit-plan'));
|
|
const periodicSliceCount = Number(periodicPlanStep.run.match(/--slices\s+(\d+)/)?.[1]);
|
|
const periodicRunStep = periodicJob.steps.find((step: any) => step.run?.includes('--plan /tmp/paid-plan/manifest.json'));
|
|
const periodicWorkers = Number(periodicRunStep.env.EVALS_JOBS);
|
|
const livePlan = (discovered?: string[]) => buildRunManifest({ tier: 'periodic', sliceCount: periodicSliceCount,
|
|
evalsAll: true, dedicatedAutoplanSlice: true, env: { EVALS_ALL: '1' }, discovered });
|
|
|
|
test('live periodic census fits the declared CI wall including setup', () => {
|
|
const m = livePlan();
|
|
expect(periodicPlanStep.run).toContain('--autoplan-slice');
|
|
expect(periodicJob.strategy.matrix.slice).toEqual(Array.from({ length: periodicSliceCount }, (_, index) => index + 1));
|
|
expect(periodicWorkers).toBe(2);
|
|
const walls = Array.from({ length: periodicSliceCount }, (_, index) => {
|
|
const files = m.entries.filter(e => e.status === 'planned' && e.slice === index + 1).map(e => e.file);
|
|
const workers = files.some(isOverlayTestFile) ? Math.min(periodicWorkers, OVERLAY_MAX_ACTIVE_SHARDS) : periodicWorkers;
|
|
return paidShardWallUpperBoundMs(files, workers);
|
|
});
|
|
expect(Math.max(...walls) + 20 * 60_000).toBeLessThanOrEqual(periodicJob['timeout-minutes'] * 60_000);
|
|
expect(m.entries.filter(e => e.status === 'planned')).toHaveLength(99);
|
|
const overlays = m.entries.filter(e => e.status === 'planned' && e.slice === periodicSliceCount - 1);
|
|
expect(overlays).toHaveLength(6);
|
|
expect(overlays.every(e => isOverlayTestFile(e.file))).toBe(true);
|
|
expect(m.entries.filter(e => e.status === 'planned' && e.slice === periodicSliceCount).map(e => e.file)).toEqual([AUTOPLAN_CHAIN_BUDGET.file]);
|
|
});
|
|
|
|
test('registered allocation is deterministic and preserves every discovered file', () => {
|
|
const files = collectPaidTestFiles();
|
|
expect(files).toHaveLength(114);
|
|
const m = livePlan(files);
|
|
expect(livePlan([...files].reverse())).toEqual(m);
|
|
expect(m.entries.map(e => e.file).sort()).toEqual([...files].sort());
|
|
expect(new Set(m.entries.map(e => e.file)).size).toBe(files.length);
|
|
});
|
|
|
|
test('ordinary-only manifests retain round-robin allocation', () => {
|
|
const files = ['test/skill-e2e-a.test.ts', 'test/skill-e2e-b.test.ts', 'test/skill-e2e-c.test.ts'];
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ordinary-shard-plan-'));
|
|
try {
|
|
fs.mkdirSync(path.join(root, 'test'));
|
|
for (const file of files) fs.writeFileSync(path.join(root, file), '// no whole-file tier exclusion');
|
|
const m = buildRunManifest({ tier: 'periodic', sliceCount: 2, evalsAll: true,
|
|
rootDir: root, discovered: files, env: { EVALS_ALL: '1' } });
|
|
expect(m.entries.map(e => e.slice)).toEqual([1, 2, 1]);
|
|
} finally { fs.rmSync(root, { recursive: true, force: true }); }
|
|
});
|
|
|
|
test('explicit allocation keeps its timer across load scheduling', () => {
|
|
const m = buildRunManifest({ tier: 'periodic', sliceCount: 7, evalsAll: true,
|
|
dedicatedAutoplanSlice: true, timeoutMs: 2_000_000, env: { EVALS_ALL: '1' } });
|
|
for (const entry of m.entries.filter(e => e.budget)) {
|
|
expect(entry.budget).toEqual(resolvePaidShardBudget([entry.file], 2_000_000));
|
|
}
|
|
});
|
|
|
|
test('single-slice manifest retains all registered files with one allocation', () => {
|
|
const m = buildRunManifest({ tier: 'periodic', sliceCount: 1, evalsAll: true, env: { EVALS_ALL: '1' } });
|
|
expect(m.entries.filter(e => e.status === 'planned').every(e => e.slice === 1)).toBe(true);
|
|
for (const budget of FINDING_RETRY_BUDGETS) expect(m.entries.find(e => e.file === budget.file)?.budget).toEqual(resolvePaidShardBudget([budget.file]));
|
|
});
|
|
|
|
test('current detach supervision covers the live-census floor', () => {
|
|
const files = selectPaidTestFiles(collectPaidTestFiles(), 'periodic').selected;
|
|
const excess = files.reduce((n, file) => n + Math.max(0, resolvePaidShardBudget([file]).timeoutMs - DEFAULT_SHARD_TIMEOUT_MS), 0);
|
|
const floor = Math.ceil((Math.ceil(files.length / DEFAULT_JOBS) * DEFAULT_SHARD_TIMEOUT_MS + excess) / 1000 * 1.05);
|
|
const pkg = JSON.parse(fs.readFileSync(path.join(import.meta.dir, '../package.json'), 'utf8'));
|
|
const configured = Number(pkg.scripts['eval:bg:periodic'].match(/--timeout\s+(\d+)/)[1]);
|
|
expect(floor).toBe(65268);
|
|
expect(configured).toBeGreaterThanOrEqual(floor);
|
|
expect(pkg.scripts['eval:bg:gate']).toContain('--timeout 33800');
|
|
});
|
|
|
|
for (const jobs of [1, 2, 3]) test(`FIFO bound covers partial durations with ${jobs} workers`, () => {
|
|
const long = FINDING_RETRY_BUDGETS.find(b => b.cases === 2)!.file;
|
|
for (const files of [[], ['test/a.test.ts'], [long, 'test/a.test.ts', 'test/b.test.ts'],
|
|
['test/a.test.ts', 'test/b.test.ts', long, 'test/c.test.ts', 'test/d.test.ts'],
|
|
[long, FINDING_RETRY_BUDGETS[1]!.file, 'test/a.test.ts', 'test/b.test.ts']]) {
|
|
const ceilings = files.map(file => resolvePaidShardBudget([file]).timeoutMs);
|
|
const bound = paidShardWallUpperBoundMs(files, jobs);
|
|
const durationOptions = ceilings.map(ms => [0, Math.floor(ms / 2), ms]);
|
|
const visit = (durations: number[]) => {
|
|
if (durations.length < files.length) {
|
|
for (const duration of durationOptions[durations.length]!) visit([...durations, duration]);
|
|
return;
|
|
}
|
|
const workers = Array<number>(jobs).fill(0);
|
|
for (const duration of durations) {
|
|
const worker = workers.indexOf(Math.min(...workers));
|
|
workers[worker] += duration;
|
|
}
|
|
expect(Math.max(...workers)).toBeLessThanOrEqual(bound);
|
|
};
|
|
visit([]);
|
|
}
|
|
});
|
|
|
|
test('FIFO bound retains exact uniform waves and explicit limits', () => {
|
|
const files = Array.from({ length: 19 }, (_, i) => `test/ordinary-${i}.test.ts`);
|
|
expect(paidShardWallUpperBoundMs(files, 2)).toBe(10 * DEFAULT_SHARD_TIMEOUT_MS);
|
|
expect(paidShardWallUpperBoundMs(files, 2, 100)).toBe(1000);
|
|
expect(() => paidShardWallUpperBoundMs(files, 0)).toThrow('Worker count');
|
|
expect(() => paidShardWallUpperBoundMs(files, 1.5)).toThrow('Worker count');
|
|
});
|