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>
76 lines
2.5 KiB
TypeScript
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"/);
|
|
});
|