Files
gstack/test/qa-deadline-publication-observer.test.ts
T
Garry Tan dcaea52800 v1.91.7.0 feat: add functional QA and pre-publication docs checks (#2983)
* feat: add surface-aware exploratory QA and ship documentation gates

* test: preserve delegated QA setup authority after main integration

* fix(qa): clarify exploration order and preserve report artifacts

* test(qa): follow the shared setup reference directly

* refactor(ship): make verification and recovery routes explicit

* test(ship): align evidence and review guards with explicit routes

* fix(workflows): clarify ship recovery and functional QA evidence

* fix(workflows): clarify approval recovery and full QA coverage

* refactor(workflows): order review transactions and clarify ship state

* fix(ship): clarify final verification and fail closed at publication

* fix(evals): attribute native atomic documentation writes

* fix(ship): clarify recovery and documentation lifecycle guidance

* fix(test): preserve observed native placeholder styling in CI

* fix(codex): report watchdog timeouts without a process-exit race

* Checkpoint functional QA implementation and workflow validation repairs

* Fix documentation and shared-review fixture contracts

* docs: clarify judge reuse and evaluation supervision

* test: align review evidence and selected case contracts

* test: verify append-only documentation checkpoints and recovery

* fix: qualify QA workflows and CI validation repairs

* fix: launch shared-libs fixture scripts on Windows

* fix: qualify QA deadlines, fixture isolation, and shard cleanup

* fix: preserve qualified QA and cancellation repairs

* fix: enforce functional fixture authority and share strict event decoding

* fix: retain free-test evidence and explain recovery

* fix: reject malformed native evidence after decoder consolidation

* test: use reliable capture for telemetry privacy filters

* test: refresh measured quick coverage and document validation costs

* Fix native fixture receipts and preserve VM validation evidence

* Align negative judge controls with upstream clarity policy

* Fix report-only QA preparation and public evidence handling

* Clarify QA-only preparation and current-report preservation

* Stream Ship quality judgments with an explicit 64k response contract

* Validate compact judge reasoning locally with supported wire schema

* Align functional QA fixture instructions with evidence acceptance

* Bind native browser diagnostics to execution evidence and align review verdicts

* Preserve native diagnostic line boundaries

* Serialize functional QA evidence from native captures

* Keep large QA evidence fixture payload out of Windows argv
2026-09-29 06:07:35 -07:00

151 lines
7.8 KiB
TypeScript

import { describe, expect, spyOn, test } from 'bun:test';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import { startQaDeadline, readQaDeadline } from '../lib/qa-deadline';
import { observeQAWrites, type QAWriteObservation } from './helpers/qa-functional-observer';
const linkFile = fs.linkSync;
async function publication(directory: string, during?: (root: string, temporary: string, target: string,
observer: Awaited<ReturnType<typeof observeQAWrites>>) => QAWriteObservation | void) {
const root = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'qa-publish-')));
fs.chmodSync(root, 0o700);
for (const name of ['.qa-state', 'qa-reports', 'reports', 'src']) fs.mkdirSync(path.join(root, name), { mode: 0o700 });
const observer = await observeQAWrites(root);
const target = path.join(root, directory, 'deadline.json');
const link = fs.linkSync;
let observation: QAWriteObservation | undefined;
let temporary = '';
let linksDuring = 0;
const intercept = spyOn(fs, 'linkSync').mockImplementation((from, to) => {
link(from, to);
temporary = String(from);
linksDuring = fs.lstatSync(to).nlink;
observation = during?.(root, temporary, String(to), observer) || undefined;
if (!observation) observer.drain();
});
try {
const state = startQaDeadline(target, '60');
intercept.mockRestore();
observation ??= observer.stop();
return { observation, linksDuring, temporary: path.relative(root, temporary),
linksAfter: fs.lstatSync(target).nlink, state, read: readQaDeadline(target) };
} finally {
intercept.mockRestore();
if (!observation) observer.stop();
fs.rmSync(root, { recursive: true, force: true });
}
}
(process.platform === 'linux' ? describe : describe.skip)('deadline publication through the registered kernel observer', () => {
for (const directory of ['reports', 'qa-reports', '.qa-state']) {
test(`accepts only the transient real publication in ${directory}`, async () => {
const result = await publication(directory);
expect(result.linksDuring).toBe(2);
expect(result.linksAfter).toBe(1);
expect(result.read).toEqual(result.state);
expect(result.observation.failures).toEqual([]);
expect(result.observation.complete).toBe(true);
expect(result.observation.events.some(event => event.path === `${directory}/deadline.json` && (event.mask & 0x100))).toBe(true);
expect(result.observation.events.some(event => event.path === result.temporary && (event.mask & 0x200))).toBe(true);
});
}
test('does not exempt a correctly named source-directory hardlink pair', async () => {
const result = await publication('src');
expect(result.observation.complete).toBe(false);
expect(result.observation.failures.some(failure => /path=src\/.*dev=\d+ ino=\d+ nlink=2/.test(failure))).toBe(true);
});
for (const mutation of ['source-hardlink', 'third-alias', 'wrong-name', 'different-directory', 'different-inode', 'writable', 'malformed', 'extra-field', 'invalid-time', 'invalid-budget'] as const) {
test(`rejects ${mutation} during the real publication even after cleanup`, async () => {
const result = await publication('qa-reports', (root, temporary, target, observer) => {
const bytes = fs.readFileSync(target);
const alias = path.join(root, mutation === 'source-hardlink' ? 'src' : mutation === 'different-directory' ? 'reports' : 'qa-reports',
mutation === 'different-directory' ? path.basename(temporary) : 'untrusted-alias');
if (mutation === 'source-hardlink' || mutation === 'third-alias') linkFile(target, alias);
if (mutation === 'wrong-name' || mutation === 'different-directory') fs.renameSync(temporary, alias);
if (mutation === 'different-inode') {
fs.unlinkSync(temporary);
fs.writeFileSync(temporary, bytes, { mode: 0o400 });
linkFile(target, alias);
}
if (mutation === 'writable') fs.chmodSync(target, 0o600);
if (['malformed', 'extra-field', 'invalid-time', 'invalid-budget'].includes(mutation)) {
const state = JSON.parse(bytes.toString());
if (mutation === 'extra-field') state.extra = true;
if (mutation === 'invalid-time') state.startedAt = 'tomorrow';
if (mutation === 'invalid-budget') state.budgetMs = 0;
fs.chmodSync(target, 0o600);
fs.writeFileSync(target, mutation === 'malformed' ? '{' : JSON.stringify(state));
fs.chmodSync(target, 0o400);
}
observer.drain();
if (mutation === 'wrong-name' || mutation === 'different-directory') fs.renameSync(alias, temporary);
if (mutation === 'source-hardlink' || mutation === 'third-alias' || mutation === 'different-inode') fs.unlinkSync(alias);
if (mutation === 'different-inode') { fs.unlinkSync(temporary); linkFile(target, temporary); }
fs.chmodSync(target, 0o600);
fs.writeFileSync(target, bytes);
fs.chmodSync(target, 0o400);
});
expect(result.observation.complete).toBe(false);
expect(result.observation.failures.some(failure => /path=qa-reports\/.*dev=\d+ ino=\d+ nlink=[23]/.test(failure))).toBe(true);
expect(result.linksAfter).toBe(1);
});
}
test('rejects an external symlink in place of the deadline temporary', async () => {
const external = fs.mkdtempSync(path.join(os.tmpdir(), 'qa-external-'));
const witness = path.join(external, 'witness');
fs.writeFileSync(witness, 'external');
try {
const result = await publication('reports', (_root, temporary, target, observer) => {
fs.unlinkSync(temporary);
fs.symlinkSync(witness, temporary);
observer.drain();
fs.unlinkSync(temporary);
linkFile(target, temporary);
});
expect(result.observation.complete).toBe(false);
expect(result.observation.failures.some(failure => failure.includes('Fixture path traverses a link') && failure.includes('path=reports/.qa-deadline-'))).toBe(true);
expect(fs.readFileSync(witness, 'utf8')).toBe('external');
} finally { fs.rmSync(external, { recursive: true, force: true }); }
});
for (const mutation of ['persistent-pair', 'new-inode', 'changed-state', 'writable-final'] as const) {
test(`requires final one-link immutable settlement: ${mutation}`, async () => {
const result = await publication('reports', (_root, temporary, target, observer) => {
observer.drain();
if (mutation !== 'persistent-pair') fs.unlinkSync(temporary);
if (mutation === 'new-inode') {
const bytes = fs.readFileSync(target);
fs.writeFileSync(target + '.replacement', bytes, { mode: 0o400 });
fs.renameSync(target + '.replacement', target);
}
if (mutation === 'changed-state') {
const state = JSON.parse(fs.readFileSync(target, 'utf8'));
state.budgetMs += 1;
fs.chmodSync(target, 0o600);
fs.writeFileSync(target, JSON.stringify(state));
fs.chmodSync(target, 0o400);
}
if (mutation === 'writable-final') fs.chmodSync(target, 0o600);
return observer.stop();
});
expect(result.observation.complete).toBe(false);
expect(result.observation.failures.some(failure => /path=reports\/deadline.json.*dev=\d+ ino=\d+ nlink=[12]/.test(failure))).toBe(true);
});
}
test('does not hide a lost directory watch behind an authenticated publication', async () => {
const result = await publication('reports', (root, _temporary, _target, observer) => {
observer.drain();
fs.renameSync(path.join(root, 'reports'), path.join(root, 'moved-reports'));
fs.renameSync(path.join(root, 'moved-reports'), path.join(root, 'reports'));
});
expect(result.observation.complete).toBe(false);
expect(result.observation.failures).toContain('watch target moved or unmounted: reports');
});
});