Files
gstack/test/gstack-config-cross-project.test.ts
T
Garry TanandClaude Fable 5 84c0eee9f2 test: spawn timeouts on absorbed-PR tests (v1.77 sync-spawn tripwire)
The absorbed community tests (#2748, #2676, #2714, #2720) were authored
before the v1.77 tripwire required a timeout on every sync spawn in the test
trees.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 21:12:39 +00:00

69 lines
2.6 KiB
TypeScript

/**
* #2673: gstack-config set must reject malformed cross_project_learnings.
*
* Empty get is the first-run prompt sentinel (pinned in
* gstack-config-defaults.test.ts). Skills only enable on the literal "true".
* A typo used to store verbatim and exit 0, so the feature stayed off and
* the prompt never returned. Unlike pair_agent / redact_prepush_hook, do
* not coerce to a default — that would still persist a value and still
* kill the sentinel. Follow codex_reviews: reject, leave existing.
*/
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
import { spawnSync } from "child_process";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
const CONFIG = path.resolve(import.meta.dir, "..", "bin", "gstack-config");
let stateRoot: string;
function cfg(args: string[]): { code: number; out: string; err: string } {
const r = spawnSync(CONFIG, args, {
timeout: 30_000,
encoding: "utf8",
env: { ...process.env, GSTACK_STATE_ROOT: stateRoot },
});
return { code: r.status ?? 0, out: r.stdout ?? "", err: r.stderr ?? "" };
}
beforeEach(() => {
stateRoot = fs.mkdtempSync(path.join(os.tmpdir(), "gstack-config-xproj-"));
});
afterEach(() => {
fs.rmSync(stateRoot, { recursive: true, force: true });
});
describe("cross_project_learnings set domain (#2673)", () => {
test("empty get is still the first-run sentinel", () => {
const r = cfg(["get", "cross_project_learnings"]);
expect(r.code).toBe(0);
expect(r.out).toBe("");
});
test("true and false round-trip", () => {
expect(cfg(["set", "cross_project_learnings", "true"]).code).toBe(0);
expect(cfg(["get", "cross_project_learnings"]).out).toBe("true");
expect(cfg(["set", "cross_project_learnings", "false"]).code).toBe(0);
expect(cfg(["get", "cross_project_learnings"]).out).toBe("false");
});
test("typo is rejected and does not write", () => {
const r = cfg(["set", "cross_project_learnings", "ture"]);
expect(r.code).toBe(1);
expect(r.err).toContain("not recognized");
expect(r.err).toContain("cross_project_learnings");
const got = cfg(["get", "cross_project_learnings"]);
expect(got.code).toBe(0);
expect(got.out).toBe("");
});
test("typo leaves an existing valid value unchanged", () => {
expect(cfg(["set", "cross_project_learnings", "true"]).code).toBe(0);
const r = cfg(["set", "cross_project_learnings", "yes"]);
expect(r.code).toBe(1);
expect(r.err).toContain("Existing value left unchanged");
expect(cfg(["get", "cross_project_learnings"]).out).toBe("true");
});
});