daemon: integrate QoL slices 1-5 (model/rename/catalog, history deletes, stats, repo prepare+imageUsed+built) — 95.3% cover green
This commit is contained in:
+111
@@ -37,9 +37,12 @@ interface FakePi {
|
||||
handlers: Map<string, (event: unknown, ctx: unknown) => unknown>;
|
||||
sentMessages: Array<{ message: string; options: unknown }>;
|
||||
aborted: number;
|
||||
setModelCalls: unknown[];
|
||||
setModelResult: boolean;
|
||||
on(event: string, handler: (event: unknown, ctx: unknown) => unknown): void;
|
||||
sendUserMessage(message: string, options?: unknown): void;
|
||||
abort(): void;
|
||||
setModel(model: unknown): Promise<boolean>;
|
||||
}
|
||||
|
||||
function makeFakePi(): FakePi {
|
||||
@@ -47,6 +50,8 @@ function makeFakePi(): FakePi {
|
||||
handlers: new Map(),
|
||||
sentMessages: [],
|
||||
aborted: 0,
|
||||
setModelCalls: [],
|
||||
setModelResult: true,
|
||||
on(event, handler) {
|
||||
this.handlers.set(event, handler);
|
||||
},
|
||||
@@ -56,6 +61,10 @@ function makeFakePi(): FakePi {
|
||||
abort() {
|
||||
this.aborted++;
|
||||
},
|
||||
setModel(model) {
|
||||
this.setModelCalls.push(model);
|
||||
return Promise.resolve(this.setModelResult);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -63,6 +72,10 @@ function makeFakeCtx(): unknown {
|
||||
return {
|
||||
cwd: "/work/repo",
|
||||
model: { id: "glm-5.3", provider: "zai-renaud" },
|
||||
modelRegistry: {
|
||||
find: (provider: string, id: string): unknown =>
|
||||
id === "glm-5.3" ? { id, provider } : undefined,
|
||||
},
|
||||
sessionManager: {
|
||||
getSessionId: () => SESSION_ID,
|
||||
getSessionName: () => undefined,
|
||||
@@ -342,6 +355,104 @@ async function main(): Promise<void> {
|
||||
await waitFor(() => pi.aborted > 0, 2000);
|
||||
check("3 abort calls pi.abort", pi.aborted === 1);
|
||||
|
||||
// --- Scenario 7: set_model / rename from the daemon ----------------
|
||||
const pushControl = (type: string, extra: Record<string, unknown>): void => {
|
||||
daemon.pushAll(
|
||||
JSON.stringify({
|
||||
v: 1,
|
||||
type,
|
||||
sessionId: SESSION_ID,
|
||||
seq: 0,
|
||||
ts: Date.now(),
|
||||
...extra,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
pushControl("set_model", {
|
||||
sessionId: "other-session",
|
||||
provider: "zai-renaud",
|
||||
modelId: "glm-5.3",
|
||||
});
|
||||
pushControl("set_model", {
|
||||
provider: "zai-renaud",
|
||||
modelId: "no-such-model",
|
||||
});
|
||||
const unknownNotice = await waitFor(
|
||||
() =>
|
||||
daemon.frames.some(
|
||||
(f) =>
|
||||
f.type === "error_notice" &&
|
||||
(f.reason as string)?.includes("model not found"),
|
||||
),
|
||||
2000,
|
||||
);
|
||||
check(
|
||||
"7 unknown model emits error_notice, foreign session ignored",
|
||||
unknownNotice && pi.setModelCalls.length === 0,
|
||||
JSON.stringify(daemon.frames.filter((f) => f.type === "error_notice")),
|
||||
);
|
||||
|
||||
pushControl("set_model", { modelId: "glm-5.3" });
|
||||
const noProvider = await waitFor(
|
||||
() =>
|
||||
daemon.frames.some(
|
||||
(f) =>
|
||||
f.type === "error_notice" &&
|
||||
(f.reason as string) === "set_model without provider",
|
||||
),
|
||||
2000,
|
||||
);
|
||||
check("7 missing provider rejected", noProvider);
|
||||
|
||||
pushControl("set_model", { provider: "zai-renaud", modelId: "glm-5.3" });
|
||||
await waitFor(() => pi.setModelCalls.length === 1, 2000);
|
||||
check(
|
||||
"7 set_model resolves via registry and calls pi.setModel",
|
||||
pi.setModelCalls.length === 1 &&
|
||||
JSON.stringify(pi.setModelCalls[0]) ===
|
||||
JSON.stringify({ id: "glm-5.3", provider: "zai-renaud" }),
|
||||
JSON.stringify(pi.setModelCalls),
|
||||
);
|
||||
|
||||
pi.setModelResult = false;
|
||||
pushControl("set_model", { provider: "zai-renaud", modelId: "glm-5.3" });
|
||||
const rejected = await waitFor(
|
||||
() =>
|
||||
daemon.frames.some(
|
||||
(f) =>
|
||||
f.type === "error_notice" &&
|
||||
(f.reason as string)?.startsWith("setModel rejected"),
|
||||
),
|
||||
2000,
|
||||
);
|
||||
check("7 setModel false mirrors error_notice", rejected);
|
||||
pi.setModelResult = true;
|
||||
|
||||
fire("model_select", { model: { id: "glm-5.4", provider: "zai-renaud" } });
|
||||
const infoSeen = await waitFor(
|
||||
() =>
|
||||
daemon.frames.some(
|
||||
(f) =>
|
||||
f.type === "session_info" &&
|
||||
(f.session as { model?: string })?.model === "glm-5.4",
|
||||
),
|
||||
2000,
|
||||
);
|
||||
check("7 model_select emits session_info", infoSeen);
|
||||
|
||||
pushControl("rename", { name: "renamed-from-daemon" });
|
||||
const renameSeen = await waitFor(
|
||||
() =>
|
||||
daemon.frames.some(
|
||||
(f) =>
|
||||
f.type === "session_info" &&
|
||||
(f.session as { name?: string })?.name === "renamed-from-daemon",
|
||||
),
|
||||
2000,
|
||||
);
|
||||
check("7 rename updates snapshot + session_info", renameSeen);
|
||||
|
||||
// --- Scenario 4: drop + reconnect, replay, overflow ----------------
|
||||
const seqBeforeDrop = Math.max(...daemon.frames.map((f) => f.seq));
|
||||
welcomeLastSeq = seqBeforeDrop; // daemon has everything up to the drop
|
||||
|
||||
Reference in New Issue
Block a user