singularity-forge/src/tests/headless-feedback.test.ts
Mikael Hugo cf32e79578 feat(memory-embeddings): read SF_LLM_GATEWAY_KEY from env as auth.json fallback
Enables CI and containerised deployments without writing secrets to disk.
Auth.json still takes precedence when present.

- readGatewayFromAuthJson now falls back to SF_LLM_GATEWAY_KEY env var
- SF_LLM_GATEWAY_URL env var also supported for endpoint override
- Added tests for env fallback, auth.json preference, and default URL

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-15 17:13:40 +02:00

76 lines
2.5 KiB
TypeScript

/**
* Smoke test for `sf headless feedback add|list|resolve`.
*
* Mirrors the headless-mark-state.test.ts pattern: source-level
* regex assertions on dispatch wiring and help text. Live-DB
* integration is dogfooded by inserting real entries against the
* SF repo's own .sf/sf.db (see commit message).
*/
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { test } from "vitest";
const __dirname = dirname(fileURLToPath(import.meta.url));
const headlessSrc = readFileSync(join(__dirname, "..", "headless.ts"), "utf-8");
const helpSrc = readFileSync(join(__dirname, "..", "help-text.ts"), "utf-8");
const handlerSrc = readFileSync(
join(__dirname, "..", "headless-feedback.ts"),
"utf-8",
);
test("headless.ts dispatches feedback command to handleFeedback", () => {
assert.match(
headlessSrc,
/options\.command === "feedback"/,
"feedback command must be dispatched",
);
assert.match(
headlessSrc,
/import\("\.\/headless-feedback\.js"\)/,
"headless.ts must import the new handler",
);
assert.match(
headlessSrc,
/feedback subcommand must be one of: add, list, resolve/,
"unknown subcommand must surface the readable error",
);
});
test("help text lists the three feedback subcommands", () => {
assert.match(helpSrc, /feedback add\s+File a self_feedback entry/);
assert.match(helpSrc, /feedback list\s+List self_feedback entries/);
assert.match(helpSrc, /feedback resolve\s+Resolve an entry/);
});
test("handler enforces required fields and severity values", () => {
assert.match(
handlerSrc,
/feedback add requires --summary <text>/,
"missing summary error must be user-readable",
);
assert.match(
handlerSrc,
/--severity must be one of: low, medium, high, critical/,
"invalid severity error must enumerate the allowed values",
);
assert.match(
handlerSrc,
/feedback resolve requires an id positional/,
"missing resolve id must be user-readable",
);
});
test("resolve is idempotent on already-resolved targets", () => {
assert.match(handlerSrc, /idempotent: true/);
assert.match(handlerSrc, /already resolved, or id not found/);
});
test("add path defaults blocking from severity, doesn't require it", () => {
// readBoolFlag(--blocking) OR severity === high|critical → blocking=true.
// The behaviour is documented in self-feedback.js (deriveBlocking),
// mirror it so operator-filed entries have consistent semantics.
assert.match(handlerSrc, /severity === "high" \|\| severity === "critical"/);
});