gstack/test/declared-annotation.test.ts

130 lines
4.7 KiB
TypeScript

/**
* Declared annotation helper (plan-tune cathedral T7) — unit tests.
*
* Verifies the helper's contract:
* - Returns null for unknown signal_key.
* - Returns null when the profile doesn't exist or declared is unset.
* - Returns a phrase when declared >= 0.7 (strong high band).
* - Returns a phrase when declared <= 0.3 (strong low band).
* - Returns null when declared is in the middle band (0.3 < x < 0.7).
* - primaryDimensionFor picks the dimension with largest |delta| total.
* - Maps kebab signal_key to underscore Dimension correctly (D2 fix).
*/
import { describe, test, expect, beforeEach, afterEach } from 'bun:test';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { getDeclaredAnnotation, primaryDimensionFor } from '../scripts/declared-annotation';
let prevStateRoot: string | undefined;
let prevHome: string | undefined;
let stateRoot: string;
beforeEach(() => {
stateRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-annot-'));
prevStateRoot = process.env.GSTACK_STATE_ROOT;
prevHome = process.env.GSTACK_HOME;
process.env.GSTACK_STATE_ROOT = stateRoot;
delete process.env.GSTACK_HOME;
});
afterEach(() => {
if (prevStateRoot !== undefined) process.env.GSTACK_STATE_ROOT = prevStateRoot;
else delete process.env.GSTACK_STATE_ROOT;
if (prevHome !== undefined) process.env.GSTACK_HOME = prevHome;
fs.rmSync(stateRoot, { recursive: true, force: true });
});
function writeProfile(declared: Record<string, number>): void {
const p = path.join(stateRoot, 'developer-profile.json');
fs.writeFileSync(p, JSON.stringify({ declared }, null, 2));
}
// ----------------------------------------------------------------------
// primaryDimensionFor — kebab→underscore mapping
// ----------------------------------------------------------------------
describe('primaryDimensionFor', () => {
test('scope-appetite → scope_appetite (largest |delta| total)', () => {
expect(primaryDimensionFor('scope-appetite')).toBe('scope_appetite');
});
test('architecture-care → architecture_care (top dim by |delta|)', () => {
expect(primaryDimensionFor('architecture-care')).toBe('architecture_care');
});
test('unknown signal_key → null', () => {
expect(primaryDimensionFor('totally-not-a-key')).toBe(null);
});
test('empty/garbage input → null', () => {
expect(primaryDimensionFor('')).toBe(null);
});
});
// ----------------------------------------------------------------------
// getDeclaredAnnotation
// ----------------------------------------------------------------------
describe('getDeclaredAnnotation', () => {
test('returns null when no profile exists', () => {
expect(getDeclaredAnnotation('scope-appetite')).toBe(null);
});
test('returns null when declared unset for the dimension', () => {
writeProfile({});
expect(getDeclaredAnnotation('scope-appetite')).toBe(null);
});
test('returns null when declared is in middle band (0.5)', () => {
writeProfile({ scope_appetite: 0.5 });
expect(getDeclaredAnnotation('scope-appetite')).toBe(null);
});
test('returns high-band phrase when declared >= 0.7', () => {
writeProfile({ scope_appetite: 0.85 });
const annot = getDeclaredAnnotation('scope-appetite');
expect(annot).toBeTruthy();
expect(annot).toContain('boil the ocean');
});
test('returns high-band phrase at the exact 0.7 threshold', () => {
writeProfile({ scope_appetite: 0.7 });
expect(getDeclaredAnnotation('scope-appetite')).toContain('boil the ocean');
});
test('returns low-band phrase when declared <= 0.3', () => {
writeProfile({ scope_appetite: 0.2 });
const annot = getDeclaredAnnotation('scope-appetite');
expect(annot).toBeTruthy();
expect(annot).toContain('ship-small-fast');
});
test('returns low-band phrase at the exact 0.3 threshold', () => {
writeProfile({ scope_appetite: 0.3 });
expect(getDeclaredAnnotation('scope-appetite')).toContain('ship-small-fast');
});
test('returns null for unknown signal_key even when profile populated', () => {
writeProfile({ scope_appetite: 0.85 });
expect(getDeclaredAnnotation('totally-not-a-key')).toBe(null);
});
test('all 5 dimensions render distinct high-band phrases', () => {
// Use the 5 signal_keys known to map to each of the 5 dimensions.
writeProfile({
scope_appetite: 0.9,
risk_tolerance: 0.9,
detail_preference: 0.9,
autonomy: 0.9,
architecture_care: 0.9,
});
const scope = getDeclaredAnnotation('scope-appetite');
const arch = getDeclaredAnnotation('architecture-care');
expect(scope).toContain('boil the ocean');
expect(arch).toContain('design-right');
});
});