Files
gbrain/test/audit/audit-dir-preload.test.ts
T
42c4ea929f fix(test): isolate audit writes to a per-run scratch dir in the shared bootstrap (#2823) (#2966)
The content-sanity gate's audit logger (logContentSanityAssessment)
defaults, via audit-writer.ts::resolveAuditDir(), to writing
~/.gbrain/audit/content-sanity-YYYY-Www.jsonl on disk. A GBRAIN_AUDIT_DIR
env override exists, but nothing in the shared test bootstrap ever set
it, so any test that exercised an audit-emitting code path without
wrapping the call in its own withEnv() fell through to the operator's
real audit trail. test/import-file.test.ts's oversize-boundary fixture
('borderline-slug', content just under MAX_FILE_SIZE but over
DEFAULT_BYTES_BLOCK) fired a real soft_block event into the developer's
live ~/.gbrain/audit on every run — which doctor's
content_sanity_audit_recent check then reported as production signal.

Fix: add a bootstrap preload (test/helpers/audit-dir-preload.ts, wired
via bunfig.toml) that points GBRAIN_AUDIT_DIR at a fresh per-process
mkdtemp dir before any test file loads. Each run-unit-shard.sh shard is
its own bun process, so each shard gets its own scratch dir with no
cross-shard collision. This closes the leak for every audit-emitting
test, not just this fixture. It respects a developer-exported override
(only sets the var when unset), and files that manage their own
per-test GBRAIN_AUDIT_DIR via withEnv() are unaffected.

Also fix a latent isolation bug this surfaced: gbrain-home-isolation.test.ts
unconditionally deleted GBRAIN_AUDIT_DIR in a finally block instead of
restoring the prior value, which clobbered the bootstrap's scratch dir
for every test file that ran after it in the same shard process.

Adds test/audit/audit-dir-preload.test.ts to pin the behavior: it
reproduces the exact soft_block event shape and asserts it lands in the
scratch dir, never in ~/.gbrain/audit.

Reported by @paul-0320.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 23:07:58 -07:00

80 lines
3.6 KiB
TypeScript

/**
* Regression gate for #2823: the shared test bootstrap
* (`test/helpers/audit-dir-preload.ts`, wired via `bunfig.toml`'s
* `preload`) must redirect `GBRAIN_AUDIT_DIR` to a per-run scratch
* directory BEFORE any test file runs, so audit-emitting code paths never
* fall through to the operator's real `~/.gbrain/audit/`.
*
* Before the fix, `test/import-file.test.ts`'s oversize-content boundary
* fixture (`'borderline-slug'`) fired a real `soft_block` content-sanity
* event straight into the developer's live audit trail on every test run.
* This file reproduces that exact event shape directly against the audit
* module (no PGLite/import-file machinery needed) and asserts it lands
* only in the scratch dir.
*/
import { describe, test, expect } from 'bun:test';
import { homedir, tmpdir } from 'os';
import { join } from 'path';
import { readFileSync, existsSync } from 'fs';
import { resolveAuditDir } from '../../src/core/audit/audit-writer.ts';
import {
logContentSanityAssessment,
readRecentContentSanityEvents,
computeContentSanityAuditFilename,
} from '../../src/core/audit/content-sanity-audit.ts';
import { assessContentSanity } from '../../src/core/content-sanity.ts';
describe('shared test-bootstrap audit isolation (#2823)', () => {
test('GBRAIN_AUDIT_DIR is set by the preload to a scratch dir, not the real ~/.gbrain/audit', () => {
const dir = process.env.GBRAIN_AUDIT_DIR;
expect(dir).toBeTruthy();
expect(dir).not.toBe(join(homedir(), '.gbrain', 'audit'));
// mkdtempSync(tmpdir(), ...) always lives directly under os.tmpdir().
expect(dir!.startsWith(tmpdir())).toBe(true);
});
test('resolveAuditDir() resolves to the preload-set scratch dir', () => {
const expected = process.env.GBRAIN_AUDIT_DIR;
expect(expected).toBeTruthy();
expect(resolveAuditDir()).toBe(expected!);
});
test('an oversize content-sanity event (the import-file.test.ts "borderline-slug" shape) never reaches the real ~/.gbrain/audit', () => {
// Unique per test-run so a stale match from a prior manual run can
// never produce a false pass.
const sentinelSlug = `borderline-slug-audit-dir-preload-${Date.now()}-${Math.random().toString(36).slice(2)}`;
const realAuditDir = join(homedir(), '.gbrain', 'audit');
const realAuditFile = join(realAuditDir, computeContentSanityAuditFilename());
// Reproduce the exact disposition the leaking fixture hits: body bytes
// over DEFAULT_BYTES_BLOCK (500_000) with no junk pattern match →
// shouldSkipEmbed=true, no shouldQuarantine → classified 'soft_block'.
const result = assessContentSanity({
compiled_truth: 'x'.repeat(600_000),
timeline: '',
title: 'Borderline',
});
expect(result.shouldSkipEmbed).toBe(true);
expect(result.shouldQuarantine).toBe(false);
logContentSanityAssessment(sentinelSlug, 'default', result);
// 1. The event IS readable back through the audit module — proves the
// write succeeded and landed in the dir resolveAuditDir() reports.
const recent = readRecentContentSanityEvents(1);
const found = recent.find((e) => e.slug === sentinelSlug);
expect(found).toBeDefined();
expect(found?.event_type).toBe('soft_block');
// 2. The real ~/.gbrain/audit content-sanity file for the current ISO
// week — if it exists at all on this machine — does NOT contain the
// sentinel slug. This is the actual regression: before the fix, this
// assertion would fail on any machine with a real ~/.gbrain.
if (existsSync(realAuditFile)) {
const contents = readFileSync(realAuditFile, 'utf8');
expect(contents).not.toContain(sentinelSlug);
}
});
});