test(remote-questions): add regression tests for race model (#3810)

Validates the race routing logic: raceRemoteAndLocal helper exists,
routing checks both hasRemote and ctx.hasUI, remote timeouts are
treated as non-wins, AbortController cancels the loser, and
isRemoteConfigured is exported from manager.ts.
This commit is contained in:
Jeremy 2026-04-08 16:20:13 -05:00
parent dadb0b136e
commit c1a68a48a9

View file

@ -760,6 +760,104 @@ test("ask-user-questions source-level: tryRemoteQuestions is called before the h
);
});
// ═══════════════════════════════════════════════════════════════════════════
// Race model tests (#3810) — local TUI races against remote channel
// ═══════════════════════════════════════════════════════════════════════════
test("ask-user-questions source-level: raceRemoteAndLocal function exists", () => {
const src = readFileSync(
join(__dirname, "..", "..", "ask-user-questions.ts"),
"utf-8",
);
assert.ok(
src.includes("async function raceRemoteAndLocal("),
"raceRemoteAndLocal helper should exist for racing local TUI against remote channel",
);
});
test("ask-user-questions source-level: race path uses isRemoteConfigured for routing", () => {
const src = readFileSync(
join(__dirname, "..", "..", "ask-user-questions.ts"),
"utf-8",
);
assert.ok(
src.includes("isRemoteConfigured()"),
"execute() should call isRemoteConfigured() for lightweight routing decision",
);
});
test("ask-user-questions source-level: race path checks both hasRemote and ctx.hasUI", () => {
// Regression: #3810 — the race should only activate when BOTH remote and local UI
// are available. Headless mode should still use remote-only, and no-remote should
// use local-only.
const src = readFileSync(
join(__dirname, "..", "..", "ask-user-questions.ts"),
"utf-8",
);
assert.ok(
src.includes("hasRemote && ctx.hasUI"),
"Race path should require both remote configured and local UI available",
);
assert.ok(
src.includes("hasRemote && !ctx.hasUI"),
"Headless path should handle remote-only when no local UI",
);
});
test("ask-user-questions source-level: race treats remote timeout as non-win", () => {
// Regression: the whole point of the race is that a remote timeout should NOT
// block the local TUI. The race helper must filter out timed_out results.
const src = readFileSync(
join(__dirname, "..", "..", "ask-user-questions.ts"),
"utf-8",
);
const raceFnStart = src.indexOf("async function raceRemoteAndLocal(");
const raceFnEnd = src.indexOf("\n}", raceFnStart);
const raceFnBody = src.slice(raceFnStart, raceFnEnd);
assert.ok(
raceFnBody.includes("timed_out"),
"raceRemoteAndLocal should check for timed_out in remote results",
);
assert.ok(
raceFnBody.includes("details?.error"),
"raceRemoteAndLocal should check for error in remote results",
);
});
test("ask-user-questions source-level: race uses AbortController to cancel loser", () => {
const src = readFileSync(
join(__dirname, "..", "..", "ask-user-questions.ts"),
"utf-8",
);
assert.ok(
src.includes("new AbortController()"),
"Race path should create an AbortController for cancellation",
);
assert.ok(
src.includes("controller.abort()"),
"raceRemoteAndLocal should abort the controller to cancel the losing side",
);
});
test("manager source-level: isRemoteConfigured export exists", () => {
const src = readFileSync(
join(__dirname, "..", "..", "remote-questions", "manager.ts"),
"utf-8",
);
assert.ok(
src.includes("export function isRemoteConfigured()"),
"manager.ts should export isRemoteConfigured for lightweight config checking",
);
// Must delegate to resolveRemoteConfig — no separate config parsing
const fnStart = src.indexOf("export function isRemoteConfigured()");
const fnEnd = src.indexOf("\n}", fnStart);
const fnBody = src.slice(fnStart, fnEnd);
assert.ok(
fnBody.includes("resolveRemoteConfig()"),
"isRemoteConfigured should delegate to resolveRemoteConfig",
);
});
test("config source-level: removeProviderToken uses auth.remove not auth.set with empty key", () => {
const commandSrc = readFileSync(
join(__dirname, "..", "..", "remote-questions", "remote-command.ts"),