mirror of https://github.com/garrytan/gstack.git
123 lines
4.9 KiB
TypeScript
123 lines
4.9 KiB
TypeScript
/**
|
|
* Regression tests for the /retro test-detection + anti-fabrication fix.
|
|
*
|
|
* Background: a generated retro claimed "0 new tests added" and invented a
|
|
* non-existent 5,165-file "bootstrap commit" to reconcile that zero against a
|
|
* large total-test count. Two root causes in retro/SKILL.md(.tmpl):
|
|
*
|
|
* 1. The "tests changed in window" command used `grep -E '\.(test|spec)\.'`,
|
|
* which only matches JS/TS files (foo.test.ts / foo.spec.js). It returned 0
|
|
* for Python (test_*.py), Terraform (*.tftest.hcl), and Bats (*.bats) suites.
|
|
* 2. There was no guard forbidding per-commit figures that aren't read from the
|
|
* commit itself, so the model fabricated a "bootstrap" narrative.
|
|
*
|
|
* These are static invariants against the skill body — they fail the build if the
|
|
* language-agnostic pattern is narrowed again or the plausibility guard is removed.
|
|
*/
|
|
import { describe, expect, test } from "bun:test";
|
|
import * as fs from "node:fs";
|
|
import * as path from "node:path";
|
|
|
|
const ROOT = path.resolve(import.meta.dir, "..");
|
|
const RETRO_TMPL = path.join(ROOT, "retro", "SKILL.md.tmpl");
|
|
const RETRO_MD = path.join(ROOT, "retro", "SKILL.md");
|
|
|
|
function readTmpl(): string {
|
|
return fs.readFileSync(RETRO_TMPL, "utf-8");
|
|
}
|
|
function readMd(): string {
|
|
return fs.readFileSync(RETRO_MD, "utf-8");
|
|
}
|
|
|
|
// The test-detection pattern must recognise these representative paths.
|
|
const MUST_MATCH = [
|
|
"tests/apps/engine_attribution/unit/test_register_tenant_context.py", // Python prefix
|
|
"libs/foo/bar_test.py", // Python suffix
|
|
"apps/dashboard/src/x.test.ts", // JS/TS infix
|
|
"apps/dashboard/src/x.spec.tsx", // JS/TS spec
|
|
"infrastructure/terraform/modules/cloud_run/tests/cloud_run_v2.tftest.hcl", // Terraform
|
|
"tests/infrastructure/test_assert_no_cloud_run_destroy.bats", // Bats
|
|
];
|
|
// The pattern must NOT classify these production files as tests.
|
|
const MUST_NOT_MATCH = [
|
|
"apps/engine_attribution/main.py",
|
|
"infrastructure/terraform/environments/production/main.tf",
|
|
"packages/components_ui/src/Button.tsx",
|
|
];
|
|
|
|
// The literal grep -iE pattern embedded in the skill (kept in sync with both files).
|
|
const TEST_RE =
|
|
/\(\^\|\/\)test_\|_test\\\.\|\\\.test\\\.\|\\\.spec\\\.\|_spec\\\.\|\\\.tftest\\\.hcl\$\|\\\.bats\$\|\(\^\|\/\)\(tests\?\|__tests__\|spec\)\//;
|
|
|
|
describe("retro test-detection — language-agnostic pattern present in both layers", () => {
|
|
for (const [label, read] of [
|
|
["template", readTmpl],
|
|
["compiled SKILL.md", readMd],
|
|
] as const) {
|
|
test(`${label} embeds the language-agnostic TEST pattern (Python/TF/Bats, not just JS)`, () => {
|
|
const body = read();
|
|
expect(body).toMatch(TEST_RE);
|
|
// belt-and-braces: the specific non-JS tokens must be there
|
|
expect(body).toContain("tftest\\.hcl$");
|
|
expect(body).toContain("\\.bats$");
|
|
expect(body).toContain("(^|/)test_");
|
|
});
|
|
|
|
test(`${label} does NOT use the narrow JS-only grep as a counting command`, () => {
|
|
const body = read();
|
|
// The narrow pattern may appear ONLY inside the explanatory "Do NOT narrow" comment.
|
|
const narrowOnCommandLine = body
|
|
.split("\n")
|
|
.filter((l) => l.includes("(test|spec)") && !l.includes("Do NOT narrow"));
|
|
expect(narrowOnCommandLine).toEqual([]);
|
|
});
|
|
}
|
|
});
|
|
|
|
describe("retro test-detection — pattern behaviour (model the embedded regex in JS)", () => {
|
|
// Mirror of the embedded grep -iE pattern, as a JS RegExp, case-insensitive.
|
|
const re = /(^|\/)test_|_test\.|\.test\.|\.spec\.|_spec\.|\.tftest\.hcl$|\.bats$|(^|\/)(tests?|__tests__|spec)\//i;
|
|
|
|
for (const p of MUST_MATCH) {
|
|
test(`matches test file: ${p}`, () => {
|
|
expect(re.test(p)).toBe(true);
|
|
});
|
|
}
|
|
for (const p of MUST_NOT_MATCH) {
|
|
test(`does not match production file: ${p}`, () => {
|
|
expect(re.test(p)).toBe(false);
|
|
});
|
|
}
|
|
});
|
|
|
|
describe("retro anti-fabrication — Step 1.5 plausibility guard present", () => {
|
|
for (const [label, read] of [
|
|
["template", readTmpl],
|
|
["compiled SKILL.md", readMd],
|
|
] as const) {
|
|
test(`${label} has the Step 1.5 plausibility guard`, () => {
|
|
const body = read();
|
|
expect(body).toMatch(/### Step 1\.5: Per-commit plausibility guard/);
|
|
});
|
|
|
|
test(`${label} forbids inventing a bootstrap/foundation narrative`, () => {
|
|
const body = read();
|
|
expect(body).toMatch(/No invented "bootstrap"\/"foundation"\/"initial import" narrative/);
|
|
});
|
|
|
|
test(`${label} requires per-commit figures from git show --shortstat`, () => {
|
|
const body = read();
|
|
expect(body).toMatch(/git show --shortstat <hash>/);
|
|
});
|
|
|
|
test(`${label} guard sits between the command block and Step 2`, () => {
|
|
const body = read();
|
|
const guard = body.indexOf("### Step 1.5: Per-commit plausibility guard");
|
|
const step2 = body.indexOf("### Step 2: Compute Metrics");
|
|
expect(guard).toBeGreaterThan(-1);
|
|
expect(step2).toBeGreaterThan(-1);
|
|
expect(guard).toBeLessThan(step2);
|
|
});
|
|
}
|
|
});
|