mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-08 06:26:45 +02:00
test: /review hardening — NOT-READY env isolation, workdir cleanup, perf
Applied from the adversarial subagent pass during /review on this branch: - test/benchmark-cli.test.ts — new "NOT READY path fires when auth env vars are stripped" test. The default dry-run test always showed OK on dev machines with auth, hiding regressions in the remediation-hint branch. Stripped env (no auth vars, HOME→empty tmpdir) now force- exercises gpt + gemini NOT READY paths and asserts every NOT READY line includes a concrete remediation hint (install/login/export). (claude adapter's os.homedir() call is Bun-cached; the 2-of-3 adapter coverage is sufficient to exercise the branch.) - test/taste-engine.test.ts — session-cap test rewritten to seed the profile with 50 entries + one real CLI call, instead of 55 sequential subprocess spawns. Same coverage (FIFO eviction at the boundary), ~5s faster CI time. Also pins first-casing-wins on the Geist/GEIST merge assertion — bumpPref() keeps the first-arrival casing, so the test documents that policy. - test/skill-e2e-benchmark-providers.test.ts — workdir creation moved from module-load into beforeAll, cleanup added in afterAll. Previous shape leaked a /tmp/bench-e2e-* dir every CI run. - test/publish-dry-run.test.ts — removed unused empty test/helpers mkdirSync from the sandbox setup. The bin doesn't import from there, so the empty dir was a footgun for future maintainers. - test/helpers/providers/gpt.ts — expanded the inline comment on `--skip-git-repo-check` to explicitly note that `-s read-only` is now load-bearing safety (the trust prompt was the secondary boundary; removing read-only while keeping skip-git-repo-check would be unsafe). Net: 45 passing tests (was 44), session-cap test 5s faster, one real regression surface covered that didn't exist before. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -208,13 +208,16 @@ describe('taste-engine: dimension extraction', () => {
|
||||
expect(p.dimensions.aesthetics.approved[0].value).toBe('brutalist');
|
||||
});
|
||||
|
||||
test('value matching is case-insensitive', () => {
|
||||
test('value matching is case-insensitive (first casing wins)', () => {
|
||||
run(['approved', 'v1', '--reason', 'fonts: Geist']);
|
||||
run(['approved', 'v2', '--reason', 'fonts: GEIST']);
|
||||
const p = readProfile();
|
||||
// Should merge into a single entry
|
||||
expect(p.dimensions.fonts.approved).toHaveLength(1);
|
||||
expect(p.dimensions.fonts.approved[0].approved_count).toBe(2);
|
||||
// Canonical value is the first-arrival casing. bumpPref() stores value on
|
||||
// insert and never overwrites on subsequent bumps.
|
||||
expect(p.dimensions.fonts.approved[0].value).toBe('Geist');
|
||||
});
|
||||
|
||||
test('unknown dimension labels are silently ignored', () => {
|
||||
@@ -231,14 +234,33 @@ describe('taste-engine: dimension extraction', () => {
|
||||
|
||||
describe('taste-engine: session cap', () => {
|
||||
test('sessions truncate to last 50 entries (FIFO)', () => {
|
||||
for (let i = 0; i < 55; i++) {
|
||||
run(['approved', `v${i}`, '--reason', 'fonts: Geist']);
|
||||
}
|
||||
// Seed the profile with 50 existing sessions, then one real CLI call writes
|
||||
// the 51st → the oldest must drop. Avoids 55 sequential subprocess spawns.
|
||||
const seededSessions = Array.from({ length: 50 }, (_, i) => ({
|
||||
ts: new Date(Date.now() - (50 - i) * 1000).toISOString(),
|
||||
action: 'approved' as const,
|
||||
variant: `seed-${i}`,
|
||||
}));
|
||||
writeProfile({
|
||||
version: 1,
|
||||
updated_at: new Date().toISOString(),
|
||||
dimensions: {
|
||||
fonts: { approved: [], rejected: [] },
|
||||
colors: { approved: [], rejected: [] },
|
||||
layouts: { approved: [], rejected: [] },
|
||||
aesthetics: { approved: [], rejected: [] },
|
||||
},
|
||||
sessions: seededSessions,
|
||||
});
|
||||
const r = run(['approved', 'new-one', '--reason', 'fonts: Geist']);
|
||||
expect(r.status).toBe(0);
|
||||
const p = readProfile();
|
||||
expect(p.sessions).toHaveLength(50);
|
||||
// Last 5 should be preserved, first 5 dropped
|
||||
expect(p.sessions[0].variant).toBe('v5');
|
||||
expect(p.sessions[49].variant).toBe('v54');
|
||||
// The oldest seed (seed-0) must have been evicted FIFO; seed-1 is now first;
|
||||
// the new entry is last.
|
||||
expect(p.sessions[0].variant).toBe('seed-1');
|
||||
expect(p.sessions[48].variant).toBe('seed-49');
|
||||
expect(p.sessions[49].variant).toBe('new-one');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user