mirror of https://github.com/garrytan/gstack.git
104 lines
4.0 KiB
TypeScript
104 lines
4.0 KiB
TypeScript
/**
|
|
* Pins the consolidated E2E tier gate (test/helpers/e2e-gate.ts).
|
|
*
|
|
* Two invariants:
|
|
* 1. The env matrix — including the tierless-run trap: EVALS=1 with
|
|
* EVALS_TIER unset must SKIP both tiers (that is how `test:evals` /
|
|
* `eval:bg:all` have always treated whole-file tier gates; per-test
|
|
* diff selection covers those runs instead).
|
|
* 2. Module purity — e2e-gate.ts is imported at module scope by every
|
|
* tier-gated paid test file, one-process-each under the sharded
|
|
* runner. Its only import must be `bun:test` and it must contain no
|
|
* spawn/network/fs machinery (the reason it cannot live in
|
|
* e2e-helpers.ts, whose EVALS=1 module scope runs a ~30s claude ping).
|
|
*/
|
|
|
|
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { describeE2ETier, e2eTierEnabled } from './e2e-gate';
|
|
|
|
const SAVED_EVALS = process.env.EVALS;
|
|
const SAVED_TIER = process.env.EVALS_TIER;
|
|
|
|
function restoreEnv() {
|
|
if (SAVED_EVALS === undefined) delete process.env.EVALS;
|
|
else process.env.EVALS = SAVED_EVALS;
|
|
if (SAVED_TIER === undefined) delete process.env.EVALS_TIER;
|
|
else process.env.EVALS_TIER = SAVED_TIER;
|
|
}
|
|
|
|
describe('e2e-gate: env matrix (read at call time)', () => {
|
|
beforeEach(() => {
|
|
delete process.env.EVALS;
|
|
delete process.env.EVALS_TIER;
|
|
});
|
|
afterEach(restoreEnv);
|
|
|
|
test('EVALS unset → skip, even when EVALS_TIER matches', () => {
|
|
process.env.EVALS_TIER = 'gate';
|
|
expect(e2eTierEnabled('gate')).toBe(false);
|
|
expect(describeE2ETier('gate')).toBe(describe.skip);
|
|
expect(describeE2ETier('periodic')).toBe(describe.skip);
|
|
});
|
|
|
|
test('EVALS=1 + matching tier → run', () => {
|
|
process.env.EVALS = '1';
|
|
process.env.EVALS_TIER = 'gate';
|
|
expect(e2eTierEnabled('gate')).toBe(true);
|
|
expect(describeE2ETier('gate')).toBe(describe);
|
|
|
|
process.env.EVALS_TIER = 'periodic';
|
|
expect(e2eTierEnabled('periodic')).toBe(true);
|
|
expect(describeE2ETier('periodic')).toBe(describe);
|
|
});
|
|
|
|
test('EVALS=1 + other tier → skip', () => {
|
|
process.env.EVALS = '1';
|
|
process.env.EVALS_TIER = 'periodic';
|
|
expect(e2eTierEnabled('gate')).toBe(false);
|
|
expect(describeE2ETier('gate')).toBe(describe.skip);
|
|
|
|
process.env.EVALS_TIER = 'gate';
|
|
expect(e2eTierEnabled('periodic')).toBe(false);
|
|
expect(describeE2ETier('periodic')).toBe(describe.skip);
|
|
});
|
|
|
|
test('EVALS=1 + EVALS_TIER unset → skip both tiers (the tierless test:evals / eval:bg:all trap)', () => {
|
|
process.env.EVALS = '1';
|
|
expect(e2eTierEnabled('gate')).toBe(false);
|
|
expect(e2eTierEnabled('periodic')).toBe(false);
|
|
expect(describeE2ETier('gate')).toBe(describe.skip);
|
|
expect(describeE2ETier('periodic')).toBe(describe.skip);
|
|
});
|
|
});
|
|
|
|
describe('e2e-gate: module purity (side-effect-free import)', () => {
|
|
const source = fs.readFileSync(path.join(import.meta.dir, 'e2e-gate.ts'), 'utf-8');
|
|
|
|
test('the only import specifier is bun:test', () => {
|
|
const specifiers = [...source.matchAll(/from\s+['"]([^'"]+)['"]/g)].map((m) => m[1]);
|
|
expect(specifiers.length).toBeGreaterThan(0);
|
|
expect(specifiers.filter((s) => s !== 'bun:test')).toEqual([]);
|
|
// No dynamic escape hatches either.
|
|
expect(source).not.toMatch(/\brequire\s*\(/);
|
|
expect(source).not.toMatch(/\bimport\s*\(/);
|
|
});
|
|
|
|
test('no spawn / network / fs machinery in the module body', () => {
|
|
// Strip comments so prose explaining WHY the module must stay pure
|
|
// (which legitimately names spawnSync etc.) doesn't trip the scan.
|
|
const code = source
|
|
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
.replace(/\/\/[^\n]*/g, '');
|
|
for (const banned of [
|
|
'spawnSync', 'spawn(', 'execSync', 'child_process',
|
|
'Bun.spawn', 'Bun.file', 'Bun.write',
|
|
'fetch(', 'WebSocket', 'XMLHttpRequest',
|
|
'readFileSync', 'writeFileSync', 'mkdirSync', 'node:fs', "from 'fs'",
|
|
]) {
|
|
expect(code.includes(banned), `e2e-gate.ts must not contain "${banned}"`).toBe(false);
|
|
}
|
|
});
|
|
});
|