mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 16:45:01 +00:00
chore: Fix types in tests 6/N.
This commit is contained in:
@@ -7,12 +7,23 @@ const loadConfig = vi.fn(() => ({
|
|||||||
auth: { token: "ltok" },
|
auth: { token: "ltok" },
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
const resolveGatewayPort = vi.fn(() => 18789);
|
const resolveGatewayPort = vi.fn((_cfg?: unknown) => 18789);
|
||||||
const discoverGatewayBeacons = vi.fn(async () => []);
|
const discoverGatewayBeacons = vi.fn(
|
||||||
|
async (_opts?: unknown): Promise<Array<{ tailnetDns: string }>> => [],
|
||||||
|
);
|
||||||
const pickPrimaryTailnetIPv4 = vi.fn(() => "100.64.0.10");
|
const pickPrimaryTailnetIPv4 = vi.fn(() => "100.64.0.10");
|
||||||
const sshStop = vi.fn(async () => {});
|
const sshStop = vi.fn(async () => {});
|
||||||
const resolveSshConfig = vi.fn(async () => null);
|
const resolveSshConfig = vi.fn(
|
||||||
const startSshPortForward = vi.fn(async () => ({
|
async (
|
||||||
|
_opts?: unknown,
|
||||||
|
): Promise<{
|
||||||
|
user: string;
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
identityFiles: string[];
|
||||||
|
} | null> => null,
|
||||||
|
);
|
||||||
|
const startSshPortForward = vi.fn(async (_opts?: unknown) => ({
|
||||||
parsedTarget: { user: "me", host: "studio", port: 22 },
|
parsedTarget: { user: "me", host: "studio", port: 22 },
|
||||||
localPort: 18789,
|
localPort: 18789,
|
||||||
remotePort: 18789,
|
remotePort: 18789,
|
||||||
@@ -20,7 +31,8 @@ const startSshPortForward = vi.fn(async () => ({
|
|||||||
stderr: [],
|
stderr: [],
|
||||||
stop: sshStop,
|
stop: sshStop,
|
||||||
}));
|
}));
|
||||||
const probeGateway = vi.fn(async ({ url }: { url: string }) => {
|
const probeGateway = vi.fn(async (opts: { url: string }) => {
|
||||||
|
const { url } = opts;
|
||||||
if (url.includes("127.0.0.1")) {
|
if (url.includes("127.0.0.1")) {
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
@@ -80,32 +92,32 @@ const probeGateway = vi.fn(async ({ url }: { url: string }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
vi.mock("../config/config.js", () => ({
|
vi.mock("../config/config.js", () => ({
|
||||||
loadConfig: () => loadConfig(),
|
loadConfig,
|
||||||
resolveGatewayPort: (cfg: unknown) => resolveGatewayPort(cfg),
|
resolveGatewayPort,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../infra/bonjour-discovery.js", () => ({
|
vi.mock("../infra/bonjour-discovery.js", () => ({
|
||||||
discoverGatewayBeacons: (opts: unknown) => discoverGatewayBeacons(opts),
|
discoverGatewayBeacons,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../infra/tailnet.js", () => ({
|
vi.mock("../infra/tailnet.js", () => ({
|
||||||
pickPrimaryTailnetIPv4: () => pickPrimaryTailnetIPv4(),
|
pickPrimaryTailnetIPv4,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../infra/ssh-tunnel.js", async (importOriginal) => {
|
vi.mock("../infra/ssh-tunnel.js", async (importOriginal) => {
|
||||||
const actual = await importOriginal<typeof import("../infra/ssh-tunnel.js")>();
|
const actual = await importOriginal<typeof import("../infra/ssh-tunnel.js")>();
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
startSshPortForward: (opts: unknown) => startSshPortForward(opts),
|
startSshPortForward,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
vi.mock("../infra/ssh-config.js", () => ({
|
vi.mock("../infra/ssh-config.js", () => ({
|
||||||
resolveSshConfig: (opts: unknown) => resolveSshConfig(opts),
|
resolveSshConfig,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock("../gateway/probe.js", () => ({
|
vi.mock("../gateway/probe.js", () => ({
|
||||||
probeGateway: (opts: unknown) => probeGateway(opts),
|
probeGateway,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
function createRuntimeCapture() {
|
function createRuntimeCapture() {
|
||||||
@@ -198,7 +210,8 @@ describe("gateway-status command", () => {
|
|||||||
loadConfig.mockReturnValueOnce({
|
loadConfig.mockReturnValueOnce({
|
||||||
gateway: {
|
gateway: {
|
||||||
mode: "remote",
|
mode: "remote",
|
||||||
remote: {},
|
remote: { url: "", token: "" },
|
||||||
|
auth: { token: "ltok" },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
discoverGatewayBeacons.mockResolvedValueOnce([
|
discoverGatewayBeacons.mockResolvedValueOnce([
|
||||||
@@ -226,6 +239,7 @@ describe("gateway-status command", () => {
|
|||||||
gateway: {
|
gateway: {
|
||||||
mode: "remote",
|
mode: "remote",
|
||||||
remote: { url: "ws://peters-mac-studio-1.sheep-coho.ts.net:18789", token: "rtok" },
|
remote: { url: "ws://peters-mac-studio-1.sheep-coho.ts.net:18789", token: "rtok" },
|
||||||
|
auth: { token: "ltok" },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
resolveSshConfig.mockResolvedValueOnce({
|
resolveSshConfig.mockResolvedValueOnce({
|
||||||
@@ -259,6 +273,7 @@ describe("gateway-status command", () => {
|
|||||||
gateway: {
|
gateway: {
|
||||||
mode: "remote",
|
mode: "remote",
|
||||||
remote: { url: "ws://studio.example:18789", token: "rtok" },
|
remote: { url: "ws://studio.example:18789", token: "rtok" },
|
||||||
|
auth: { token: "ltok" },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
resolveSshConfig.mockResolvedValueOnce(null);
|
resolveSshConfig.mockResolvedValueOnce(null);
|
||||||
@@ -284,6 +299,7 @@ describe("gateway-status command", () => {
|
|||||||
gateway: {
|
gateway: {
|
||||||
mode: "remote",
|
mode: "remote",
|
||||||
remote: { url: "ws://studio.example:18789", token: "rtok" },
|
remote: { url: "ws://studio.example:18789", token: "rtok" },
|
||||||
|
auth: { token: "ltok" },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
resolveSshConfig.mockResolvedValueOnce({
|
resolveSshConfig.mockResolvedValueOnce({
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
import { afterAll, beforeAll, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { Mock } from "vitest";
|
||||||
import { captureEnv } from "../test-utils/env.js";
|
import { captureEnv } from "../test-utils/env.js";
|
||||||
|
|
||||||
let envSnapshot: ReturnType<typeof captureEnv>;
|
let envSnapshot: ReturnType<typeof captureEnv>;
|
||||||
@@ -323,10 +324,12 @@ const runtime = {
|
|||||||
exit: vi.fn(),
|
exit: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const runtimeLogMock = runtime.log as Mock<(...args: unknown[]) => void>;
|
||||||
|
|
||||||
describe("statusCommand", () => {
|
describe("statusCommand", () => {
|
||||||
it("prints JSON when requested", async () => {
|
it("prints JSON when requested", async () => {
|
||||||
await statusCommand({ json: true }, runtime as never);
|
await statusCommand({ json: true }, runtime as never);
|
||||||
const payload = JSON.parse((runtime.log as vi.Mock).mock.calls[0][0]);
|
const payload = JSON.parse(String(runtimeLogMock.mock.calls[0]?.[0]));
|
||||||
expect(payload.linkChannel.linked).toBe(true);
|
expect(payload.linkChannel.linked).toBe(true);
|
||||||
expect(payload.memory.agentId).toBe("main");
|
expect(payload.memory.agentId).toBe("main");
|
||||||
expect(payload.memoryPlugin.enabled).toBe(true);
|
expect(payload.memoryPlugin.enabled).toBe(true);
|
||||||
@@ -348,9 +351,9 @@ describe("statusCommand", () => {
|
|||||||
|
|
||||||
it("surfaces unknown usage when totalTokens is missing", async () => {
|
it("surfaces unknown usage when totalTokens is missing", async () => {
|
||||||
await withUnknownUsageStore(async () => {
|
await withUnknownUsageStore(async () => {
|
||||||
(runtime.log as vi.Mock).mockClear();
|
runtimeLogMock.mockClear();
|
||||||
await statusCommand({ json: true }, runtime as never);
|
await statusCommand({ json: true }, runtime as never);
|
||||||
const payload = JSON.parse((runtime.log as vi.Mock).mock.calls.at(-1)?.[0]);
|
const payload = JSON.parse(String(runtimeLogMock.mock.calls.at(-1)?.[0]));
|
||||||
expect(payload.sessions.recent[0].totalTokens).toBeNull();
|
expect(payload.sessions.recent[0].totalTokens).toBeNull();
|
||||||
expect(payload.sessions.recent[0].totalTokensFresh).toBe(false);
|
expect(payload.sessions.recent[0].totalTokensFresh).toBe(false);
|
||||||
expect(payload.sessions.recent[0].percentUsed).toBeNull();
|
expect(payload.sessions.recent[0].percentUsed).toBeNull();
|
||||||
@@ -360,37 +363,37 @@ describe("statusCommand", () => {
|
|||||||
|
|
||||||
it("prints unknown usage in formatted output when totalTokens is missing", async () => {
|
it("prints unknown usage in formatted output when totalTokens is missing", async () => {
|
||||||
await withUnknownUsageStore(async () => {
|
await withUnknownUsageStore(async () => {
|
||||||
(runtime.log as vi.Mock).mockClear();
|
runtimeLogMock.mockClear();
|
||||||
await statusCommand({}, runtime as never);
|
await statusCommand({}, runtime as never);
|
||||||
const logs = (runtime.log as vi.Mock).mock.calls.map((c) => String(c[0]));
|
const logs = runtimeLogMock.mock.calls.map((c: unknown[]) => String(c[0]));
|
||||||
expect(logs.some((line) => line.includes("unknown/") && line.includes("(?%)"))).toBe(true);
|
expect(logs.some((line) => line.includes("unknown/") && line.includes("(?%)"))).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("prints formatted lines otherwise", async () => {
|
it("prints formatted lines otherwise", async () => {
|
||||||
(runtime.log as vi.Mock).mockClear();
|
runtimeLogMock.mockClear();
|
||||||
await statusCommand({}, runtime as never);
|
await statusCommand({}, runtime as never);
|
||||||
const logs = (runtime.log as vi.Mock).mock.calls.map((c) => String(c[0]));
|
const logs = runtimeLogMock.mock.calls.map((c: unknown[]) => String(c[0]));
|
||||||
expect(logs.some((l) => l.includes("OpenClaw status"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("OpenClaw status"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("Overview"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("Overview"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("Security audit"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("Security audit"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("Summary:"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("Summary:"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("CRITICAL"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("CRITICAL"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("Dashboard"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("Dashboard"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("macos 14.0 (arm64)"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("macos 14.0 (arm64)"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("Memory"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("Memory"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("Channels"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("Channels"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("WhatsApp"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("WhatsApp"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("Sessions"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("Sessions"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("+1000"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("+1000"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("50%"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("50%"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("LaunchAgent"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("LaunchAgent"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("FAQ:"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("FAQ:"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("Troubleshooting:"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("Troubleshooting:"))).toBe(true);
|
||||||
expect(logs.some((l) => l.includes("Next steps:"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("Next steps:"))).toBe(true);
|
||||||
expect(
|
expect(
|
||||||
logs.some(
|
logs.some(
|
||||||
(l) =>
|
(l: string) =>
|
||||||
l.includes("openclaw status --all") ||
|
l.includes("openclaw status --all") ||
|
||||||
l.includes("openclaw --profile isolated status --all") ||
|
l.includes("openclaw --profile isolated status --all") ||
|
||||||
l.includes("openclaw status --all") ||
|
l.includes("openclaw status --all") ||
|
||||||
@@ -414,10 +417,10 @@ describe("statusCommand", () => {
|
|||||||
presence: [],
|
presence: [],
|
||||||
configSnapshot: null,
|
configSnapshot: null,
|
||||||
});
|
});
|
||||||
(runtime.log as vi.Mock).mockClear();
|
runtimeLogMock.mockClear();
|
||||||
await statusCommand({}, runtime as never);
|
await statusCommand({}, runtime as never);
|
||||||
const logs = (runtime.log as vi.Mock).mock.calls.map((c) => String(c[0]));
|
const logs = runtimeLogMock.mock.calls.map((c: unknown[]) => String(c[0]));
|
||||||
expect(logs.some((l) => l.includes("auth token"))).toBe(true);
|
expect(logs.some((l: string) => l.includes("auth token"))).toBe(true);
|
||||||
} finally {
|
} finally {
|
||||||
if (prevToken === undefined) {
|
if (prevToken === undefined) {
|
||||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||||
@@ -462,9 +465,9 @@ describe("statusCommand", () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
(runtime.log as vi.Mock).mockClear();
|
runtimeLogMock.mockClear();
|
||||||
await statusCommand({}, runtime as never);
|
await statusCommand({}, runtime as never);
|
||||||
const logs = (runtime.log as vi.Mock).mock.calls.map((c) => String(c[0]));
|
const logs = runtimeLogMock.mock.calls.map((c: unknown[]) => String(c[0]));
|
||||||
expect(logs.join("\n")).toMatch(/Signal/i);
|
expect(logs.join("\n")).toMatch(/Signal/i);
|
||||||
expect(logs.join("\n")).toMatch(/iMessage/i);
|
expect(logs.join("\n")).toMatch(/iMessage/i);
|
||||||
expect(logs.join("\n")).toMatch(/gateway:/i);
|
expect(logs.join("\n")).toMatch(/gateway:/i);
|
||||||
@@ -507,7 +510,7 @@ describe("statusCommand", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await statusCommand({ json: true }, runtime as never);
|
await statusCommand({ json: true }, runtime as never);
|
||||||
const payload = JSON.parse((runtime.log as vi.Mock).mock.calls.at(-1)?.[0]);
|
const payload = JSON.parse(String(runtimeLogMock.mock.calls.at(-1)?.[0]));
|
||||||
expect(payload.sessions.count).toBe(2);
|
expect(payload.sessions.count).toBe(2);
|
||||||
expect(payload.sessions.paths.length).toBe(2);
|
expect(payload.sessions.paths.length).toBe(2);
|
||||||
expect(
|
expect(
|
||||||
|
|||||||
@@ -19,11 +19,18 @@ vi.mock("./node-llama.js", () => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
const createFetchMock = () =>
|
const createFetchMock = () =>
|
||||||
vi.fn(async () => ({
|
vi.fn(async (_input?: unknown, _init?: unknown) => ({
|
||||||
ok: true,
|
ok: true,
|
||||||
status: 200,
|
status: 200,
|
||||||
json: async () => ({ data: [{ embedding: [1, 2, 3] }] }),
|
json: async () => ({ data: [{ embedding: [1, 2, 3] }] }),
|
||||||
})) as unknown as typeof fetch;
|
}));
|
||||||
|
|
||||||
|
function requireProvider(result: Awaited<ReturnType<typeof createEmbeddingProvider>>) {
|
||||||
|
if (!result.provider) {
|
||||||
|
throw new Error("Expected embedding provider");
|
||||||
|
}
|
||||||
|
return result.provider;
|
||||||
|
}
|
||||||
|
|
||||||
describe("embedding provider remote overrides", () => {
|
describe("embedding provider remote overrides", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -69,10 +76,12 @@ describe("embedding provider remote overrides", () => {
|
|||||||
fallback: "openai",
|
fallback: "openai",
|
||||||
});
|
});
|
||||||
|
|
||||||
await result.provider.embedQuery("hello");
|
const provider = requireProvider(result);
|
||||||
|
await provider.embedQuery("hello");
|
||||||
|
|
||||||
expect(authModule.resolveApiKeyForProvider).not.toHaveBeenCalled();
|
expect(authModule.resolveApiKeyForProvider).not.toHaveBeenCalled();
|
||||||
const [url, init] = fetchMock.mock.calls[0] ?? [];
|
const url = fetchMock.mock.calls[0]?.[0];
|
||||||
|
const init = fetchMock.mock.calls[0]?.[1] as RequestInit | undefined;
|
||||||
expect(url).toBe("https://remote.example/v1/embeddings");
|
expect(url).toBe("https://remote.example/v1/embeddings");
|
||||||
const headers = (init?.headers ?? {}) as Record<string, string>;
|
const headers = (init?.headers ?? {}) as Record<string, string>;
|
||||||
expect(headers.Authorization).toBe("Bearer remote-key");
|
expect(headers.Authorization).toBe("Bearer remote-key");
|
||||||
@@ -112,19 +121,21 @@ describe("embedding provider remote overrides", () => {
|
|||||||
fallback: "openai",
|
fallback: "openai",
|
||||||
});
|
});
|
||||||
|
|
||||||
await result.provider.embedQuery("hello");
|
const provider = requireProvider(result);
|
||||||
|
await provider.embedQuery("hello");
|
||||||
|
|
||||||
expect(authModule.resolveApiKeyForProvider).toHaveBeenCalledTimes(1);
|
expect(authModule.resolveApiKeyForProvider).toHaveBeenCalledTimes(1);
|
||||||
const headers = (fetchMock.mock.calls[0]?.[1]?.headers as Record<string, string>) ?? {};
|
const init = fetchMock.mock.calls[0]?.[1] as RequestInit | undefined;
|
||||||
|
const headers = (init?.headers as Record<string, string>) ?? {};
|
||||||
expect(headers.Authorization).toBe("Bearer provider-key");
|
expect(headers.Authorization).toBe("Bearer provider-key");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("builds Gemini embeddings requests with api key header", async () => {
|
it("builds Gemini embeddings requests with api key header", async () => {
|
||||||
const fetchMock = vi.fn(async () => ({
|
const fetchMock = vi.fn(async (_input?: unknown, _init?: unknown) => ({
|
||||||
ok: true,
|
ok: true,
|
||||||
status: 200,
|
status: 200,
|
||||||
json: async () => ({ embedding: { values: [1, 2, 3] } }),
|
json: async () => ({ embedding: { values: [1, 2, 3] } }),
|
||||||
})) as unknown as typeof fetch;
|
}));
|
||||||
vi.stubGlobal("fetch", fetchMock);
|
vi.stubGlobal("fetch", fetchMock);
|
||||||
vi.mocked(authModule.resolveApiKeyForProvider).mockResolvedValue({
|
vi.mocked(authModule.resolveApiKeyForProvider).mockResolvedValue({
|
||||||
apiKey: "provider-key",
|
apiKey: "provider-key",
|
||||||
@@ -152,9 +163,11 @@ describe("embedding provider remote overrides", () => {
|
|||||||
fallback: "openai",
|
fallback: "openai",
|
||||||
});
|
});
|
||||||
|
|
||||||
await result.provider.embedQuery("hello");
|
const provider = requireProvider(result);
|
||||||
|
await provider.embedQuery("hello");
|
||||||
|
|
||||||
const [url, init] = fetchMock.mock.calls[0] ?? [];
|
const url = fetchMock.mock.calls[0]?.[0];
|
||||||
|
const init = fetchMock.mock.calls[0]?.[1] as RequestInit | undefined;
|
||||||
expect(url).toBe(
|
expect(url).toBe(
|
||||||
"https://generativelanguage.googleapis.com/v1beta/models/text-embedding-004:embedContent",
|
"https://generativelanguage.googleapis.com/v1beta/models/text-embedding-004:embedContent",
|
||||||
);
|
);
|
||||||
@@ -186,15 +199,16 @@ describe("embedding provider auto selection", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result.requestedProvider).toBe("auto");
|
expect(result.requestedProvider).toBe("auto");
|
||||||
expect(result.provider.id).toBe("openai");
|
const provider = requireProvider(result);
|
||||||
|
expect(provider.id).toBe("openai");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses gemini when openai is missing", async () => {
|
it("uses gemini when openai is missing", async () => {
|
||||||
const fetchMock = vi.fn(async () => ({
|
const fetchMock = vi.fn(async (_input?: unknown, _init?: unknown) => ({
|
||||||
ok: true,
|
ok: true,
|
||||||
status: 200,
|
status: 200,
|
||||||
json: async () => ({ embedding: { values: [1, 2, 3] } }),
|
json: async () => ({ embedding: { values: [1, 2, 3] } }),
|
||||||
})) as unknown as typeof fetch;
|
}));
|
||||||
vi.stubGlobal("fetch", fetchMock);
|
vi.stubGlobal("fetch", fetchMock);
|
||||||
vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => {
|
vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => {
|
||||||
if (provider === "openai") {
|
if (provider === "openai") {
|
||||||
@@ -214,8 +228,9 @@ describe("embedding provider auto selection", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result.requestedProvider).toBe("auto");
|
expect(result.requestedProvider).toBe("auto");
|
||||||
expect(result.provider.id).toBe("gemini");
|
const provider = requireProvider(result);
|
||||||
await result.provider.embedQuery("hello");
|
expect(provider.id).toBe("gemini");
|
||||||
|
await provider.embedQuery("hello");
|
||||||
const [url] = fetchMock.mock.calls[0] ?? [];
|
const [url] = fetchMock.mock.calls[0] ?? [];
|
||||||
expect(url).toBe(
|
expect(url).toBe(
|
||||||
`https://generativelanguage.googleapis.com/v1beta/models/${DEFAULT_GEMINI_EMBEDDING_MODEL}:embedContent`,
|
`https://generativelanguage.googleapis.com/v1beta/models/${DEFAULT_GEMINI_EMBEDDING_MODEL}:embedContent`,
|
||||||
@@ -223,11 +238,11 @@ describe("embedding provider auto selection", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("keeps explicit model when openai is selected", async () => {
|
it("keeps explicit model when openai is selected", async () => {
|
||||||
const fetchMock = vi.fn(async () => ({
|
const fetchMock = vi.fn(async (_input?: unknown, _init?: unknown) => ({
|
||||||
ok: true,
|
ok: true,
|
||||||
status: 200,
|
status: 200,
|
||||||
json: async () => ({ data: [{ embedding: [1, 2, 3] }] }),
|
json: async () => ({ data: [{ embedding: [1, 2, 3] }] }),
|
||||||
})) as unknown as typeof fetch;
|
}));
|
||||||
vi.stubGlobal("fetch", fetchMock);
|
vi.stubGlobal("fetch", fetchMock);
|
||||||
vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => {
|
vi.mocked(authModule.resolveApiKeyForProvider).mockImplementation(async ({ provider }) => {
|
||||||
if (provider === "openai") {
|
if (provider === "openai") {
|
||||||
@@ -244,11 +259,13 @@ describe("embedding provider auto selection", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result.requestedProvider).toBe("auto");
|
expect(result.requestedProvider).toBe("auto");
|
||||||
expect(result.provider.id).toBe("openai");
|
const provider = requireProvider(result);
|
||||||
await result.provider.embedQuery("hello");
|
expect(provider.id).toBe("openai");
|
||||||
const [url, init] = fetchMock.mock.calls[0] ?? [];
|
await provider.embedQuery("hello");
|
||||||
|
const url = fetchMock.mock.calls[0]?.[0];
|
||||||
|
const init = fetchMock.mock.calls[0]?.[1] as RequestInit | undefined;
|
||||||
expect(url).toBe("https://api.openai.com/v1/embeddings");
|
expect(url).toBe("https://api.openai.com/v1/embeddings");
|
||||||
const payload = JSON.parse(String(init?.body ?? "{}")) as { model?: string };
|
const payload = JSON.parse(init?.body as string) as { model?: string };
|
||||||
expect(payload.model).toBe("text-embedding-3-small");
|
expect(payload.model).toBe("text-embedding-3-small");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -282,7 +299,8 @@ describe("embedding provider local fallback", () => {
|
|||||||
fallback: "openai",
|
fallback: "openai",
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.provider.id).toBe("openai");
|
const provider = requireProvider(result);
|
||||||
|
expect(provider.id).toBe("openai");
|
||||||
expect(result.fallbackFrom).toBe("local");
|
expect(result.fallbackFrom).toBe("local");
|
||||||
expect(result.fallbackReason).toContain("node-llama-cpp");
|
expect(result.fallbackReason).toContain("node-llama-cpp");
|
||||||
});
|
});
|
||||||
@@ -365,7 +383,8 @@ describe("local embedding normalization", () => {
|
|||||||
|
|
||||||
const result = await createLocalProviderForTest();
|
const result = await createLocalProviderForTest();
|
||||||
|
|
||||||
const embedding = await result.provider.embedQuery("test query");
|
const provider = requireProvider(result);
|
||||||
|
const embedding = await provider.embedQuery("test query");
|
||||||
|
|
||||||
const magnitude = Math.sqrt(embedding.reduce((sum, x) => sum + x * x, 0));
|
const magnitude = Math.sqrt(embedding.reduce((sum, x) => sum + x * x, 0));
|
||||||
|
|
||||||
@@ -380,7 +399,8 @@ describe("local embedding normalization", () => {
|
|||||||
|
|
||||||
const result = await createLocalProviderForTest();
|
const result = await createLocalProviderForTest();
|
||||||
|
|
||||||
const embedding = await result.provider.embedQuery("test");
|
const provider = requireProvider(result);
|
||||||
|
const embedding = await provider.embedQuery("test");
|
||||||
|
|
||||||
expect(embedding).toEqual([0, 0, 0, 0]);
|
expect(embedding).toEqual([0, 0, 0, 0]);
|
||||||
expect(embedding.every((value) => Number.isFinite(value))).toBe(true);
|
expect(embedding.every((value) => Number.isFinite(value))).toBe(true);
|
||||||
@@ -393,7 +413,8 @@ describe("local embedding normalization", () => {
|
|||||||
|
|
||||||
const result = await createLocalProviderForTest();
|
const result = await createLocalProviderForTest();
|
||||||
|
|
||||||
const embedding = await result.provider.embedQuery("test");
|
const provider = requireProvider(result);
|
||||||
|
const embedding = await provider.embedQuery("test");
|
||||||
|
|
||||||
expect(embedding).toEqual([1, 0, 0, 0]);
|
expect(embedding).toEqual([1, 0, 0, 0]);
|
||||||
expect(embedding.every((value) => Number.isFinite(value))).toBe(true);
|
expect(embedding.every((value) => Number.isFinite(value))).toBe(true);
|
||||||
@@ -424,7 +445,8 @@ describe("local embedding normalization", () => {
|
|||||||
|
|
||||||
const result = await createLocalProviderForTest();
|
const result = await createLocalProviderForTest();
|
||||||
|
|
||||||
const embeddings = await result.provider.embedBatch(["text1", "text2", "text3"]);
|
const provider = requireProvider(result);
|
||||||
|
const embeddings = await provider.embedBatch(["text1", "text2", "text3"]);
|
||||||
|
|
||||||
for (const embedding of embeddings) {
|
for (const embedding of embeddings) {
|
||||||
const magnitude = Math.sqrt(embedding.reduce((sum, x) => sum + x * x, 0));
|
const magnitude = Math.sqrt(embedding.reduce((sum, x) => sum + x * x, 0));
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import fs from "node:fs/promises";
|
|||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import type { Mock } from "vitest";
|
||||||
|
|
||||||
const { logWarnMock, logDebugMock, logInfoMock } = vi.hoisted(() => ({
|
const { logWarnMock, logDebugMock, logInfoMock } = vi.hoisted(() => ({
|
||||||
logWarnMock: vi.fn(),
|
logWarnMock: vi.fn(),
|
||||||
@@ -68,7 +69,7 @@ vi.mock("../logging/subsystem.js", () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
vi.mock(import("node:child_process"), async (importOriginal) => {
|
vi.mock("node:child_process", async (importOriginal) => {
|
||||||
const actual = await importOriginal<typeof import("node:child_process")>();
|
const actual = await importOriginal<typeof import("node:child_process")>();
|
||||||
return {
|
return {
|
||||||
...actual,
|
...actual,
|
||||||
@@ -81,7 +82,7 @@ import type { OpenClawConfig } from "../config/config.js";
|
|||||||
import { resolveMemoryBackendConfig } from "./backend-config.js";
|
import { resolveMemoryBackendConfig } from "./backend-config.js";
|
||||||
import { QmdMemoryManager } from "./qmd-manager.js";
|
import { QmdMemoryManager } from "./qmd-manager.js";
|
||||||
|
|
||||||
const spawnMock = mockedSpawn as unknown as vi.Mock;
|
const spawnMock = mockedSpawn as unknown as Mock;
|
||||||
|
|
||||||
describe("QmdMemoryManager", () => {
|
describe("QmdMemoryManager", () => {
|
||||||
let fixtureRoot: string;
|
let fixtureRoot: string;
|
||||||
@@ -195,7 +196,7 @@ describe("QmdMemoryManager", () => {
|
|||||||
|
|
||||||
const { manager } = await createManager({ mode: "full" });
|
const { manager } = await createManager({ mode: "full" });
|
||||||
expect(releaseUpdate).not.toBeNull();
|
expect(releaseUpdate).not.toBeNull();
|
||||||
releaseUpdate?.();
|
(releaseUpdate as (() => void) | null)?.();
|
||||||
await manager?.close();
|
await manager?.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -256,7 +257,7 @@ describe("QmdMemoryManager", () => {
|
|||||||
});
|
});
|
||||||
await new Promise<void>((resolve) => setImmediate(resolve));
|
await new Promise<void>((resolve) => setImmediate(resolve));
|
||||||
expect(created).toBe(false);
|
expect(created).toBe(false);
|
||||||
releaseUpdate?.();
|
(releaseUpdate as (() => void) | null)?.();
|
||||||
const manager = await createPromise;
|
const manager = await createPromise;
|
||||||
await manager?.close();
|
await manager?.close();
|
||||||
});
|
});
|
||||||
@@ -340,7 +341,7 @@ describe("QmdMemoryManager", () => {
|
|||||||
expect(manager).toBeTruthy();
|
expect(manager).toBeTruthy();
|
||||||
await manager?.close();
|
await manager?.close();
|
||||||
|
|
||||||
const commands = spawnMock.mock.calls.map((call) => call[1] as string[]);
|
const commands = spawnMock.mock.calls.map((call: unknown[]) => call[1] as string[]);
|
||||||
const removeSessions = commands.find(
|
const removeSessions = commands.find(
|
||||||
(args) =>
|
(args) =>
|
||||||
args[0] === "collection" && args[1] === "remove" && args[2] === sessionCollectionName,
|
args[0] === "collection" && args[1] === "remove" && args[2] === sessionCollectionName,
|
||||||
@@ -389,7 +390,7 @@ describe("QmdMemoryManager", () => {
|
|||||||
const { manager } = await createManager({ mode: "full" });
|
const { manager } = await createManager({ mode: "full" });
|
||||||
await manager.close();
|
await manager.close();
|
||||||
|
|
||||||
const commands = spawnMock.mock.calls.map((call) => call[1] as string[]);
|
const commands = spawnMock.mock.calls.map((call: unknown[]) => call[1] as string[]);
|
||||||
const removeSessions = commands.find(
|
const removeSessions = commands.find(
|
||||||
(args) =>
|
(args) =>
|
||||||
args[0] === "collection" && args[1] === "remove" && args[2] === sessionCollectionName,
|
args[0] === "collection" && args[1] === "remove" && args[2] === sessionCollectionName,
|
||||||
@@ -485,12 +486,12 @@ describe("QmdMemoryManager", () => {
|
|||||||
await expect(manager.sync({ reason: "manual" })).resolves.toBeUndefined();
|
await expect(manager.sync({ reason: "manual" })).resolves.toBeUndefined();
|
||||||
|
|
||||||
const removeCalls = spawnMock.mock.calls
|
const removeCalls = spawnMock.mock.calls
|
||||||
.map((call) => call[1] as string[])
|
.map((call: unknown[]) => call[1] as string[])
|
||||||
.filter((args) => args[0] === "collection" && args[1] === "remove")
|
.filter((args: string[]) => args[0] === "collection" && args[1] === "remove")
|
||||||
.map((args) => args[2]);
|
.map((args) => args[2]);
|
||||||
const addCalls = spawnMock.mock.calls
|
const addCalls = spawnMock.mock.calls
|
||||||
.map((call) => call[1] as string[])
|
.map((call: unknown[]) => call[1] as string[])
|
||||||
.filter((args) => args[0] === "collection" && args[1] === "add")
|
.filter((args: string[]) => args[0] === "collection" && args[1] === "add")
|
||||||
.map((args) => args[args.indexOf("--name") + 1]);
|
.map((args) => args[args.indexOf("--name") + 1]);
|
||||||
|
|
||||||
expect(updateCalls).toBe(2);
|
expect(updateCalls).toBe(2);
|
||||||
@@ -536,8 +537,8 @@ describe("QmdMemoryManager", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const removeCalls = spawnMock.mock.calls
|
const removeCalls = spawnMock.mock.calls
|
||||||
.map((call) => call[1] as string[])
|
.map((call: unknown[]) => call[1] as string[])
|
||||||
.filter((args) => args[0] === "collection" && args[1] === "remove");
|
.filter((args: string[]) => args[0] === "collection" && args[1] === "remove");
|
||||||
expect(removeCalls).toHaveLength(0);
|
expect(removeCalls).toHaveLength(0);
|
||||||
|
|
||||||
await manager.close();
|
await manager.close();
|
||||||
@@ -575,7 +576,9 @@ describe("QmdMemoryManager", () => {
|
|||||||
manager.search("test", { sessionKey: "agent:main:slack:dm:u123" }),
|
manager.search("test", { sessionKey: "agent:main:slack:dm:u123" }),
|
||||||
).resolves.toEqual([]);
|
).resolves.toEqual([]);
|
||||||
|
|
||||||
const searchCall = spawnMock.mock.calls.find((call) => call[1]?.[0] === "search");
|
const searchCall = spawnMock.mock.calls.find(
|
||||||
|
(call: unknown[]) => (call[1] as string[])?.[0] === "search",
|
||||||
|
);
|
||||||
expect(searchCall?.[1]).toEqual([
|
expect(searchCall?.[1]).toEqual([
|
||||||
"search",
|
"search",
|
||||||
"test",
|
"test",
|
||||||
@@ -585,7 +588,9 @@ describe("QmdMemoryManager", () => {
|
|||||||
"-c",
|
"-c",
|
||||||
"workspace-main",
|
"workspace-main",
|
||||||
]);
|
]);
|
||||||
expect(spawnMock.mock.calls.some((call) => call[1]?.[0] === "query")).toBe(false);
|
expect(
|
||||||
|
spawnMock.mock.calls.some((call: unknown[]) => (call[1] as string[])?.[0] === "query"),
|
||||||
|
).toBe(false);
|
||||||
expect(maxResults).toBeGreaterThan(0);
|
expect(maxResults).toBeGreaterThan(0);
|
||||||
await manager.close();
|
await manager.close();
|
||||||
});
|
});
|
||||||
@@ -628,7 +633,7 @@ describe("QmdMemoryManager", () => {
|
|||||||
).resolves.toEqual([]);
|
).resolves.toEqual([]);
|
||||||
|
|
||||||
const searchAndQueryCalls = spawnMock.mock.calls
|
const searchAndQueryCalls = spawnMock.mock.calls
|
||||||
.map((call) => call[1])
|
.map((call: unknown[]) => call[1])
|
||||||
.filter(
|
.filter(
|
||||||
(args): args is string[] => Array.isArray(args) && ["search", "query"].includes(args[0]),
|
(args): args is string[] => Array.isArray(args) && ["search", "query"].includes(args[0]),
|
||||||
);
|
);
|
||||||
@@ -684,7 +689,7 @@ describe("QmdMemoryManager", () => {
|
|||||||
if (!releaseFirstUpdate) {
|
if (!releaseFirstUpdate) {
|
||||||
throw new Error("first update release missing");
|
throw new Error("first update release missing");
|
||||||
}
|
}
|
||||||
releaseFirstUpdate();
|
(releaseFirstUpdate as () => void)();
|
||||||
|
|
||||||
await Promise.all([inFlight, forced]);
|
await Promise.all([inFlight, forced]);
|
||||||
expect(updateCalls).toBe(2);
|
expect(updateCalls).toBe(2);
|
||||||
@@ -744,7 +749,7 @@ describe("QmdMemoryManager", () => {
|
|||||||
if (!releaseFirstUpdate) {
|
if (!releaseFirstUpdate) {
|
||||||
throw new Error("first update release missing");
|
throw new Error("first update release missing");
|
||||||
}
|
}
|
||||||
releaseFirstUpdate();
|
(releaseFirstUpdate as () => void)();
|
||||||
|
|
||||||
await secondUpdateSpawned.promise;
|
await secondUpdateSpawned.promise;
|
||||||
const forcedTwo = manager.sync({ reason: "manual-again", force: true });
|
const forcedTwo = manager.sync({ reason: "manual-again", force: true });
|
||||||
@@ -752,7 +757,7 @@ describe("QmdMemoryManager", () => {
|
|||||||
if (!releaseSecondUpdate) {
|
if (!releaseSecondUpdate) {
|
||||||
throw new Error("second update release missing");
|
throw new Error("second update release missing");
|
||||||
}
|
}
|
||||||
releaseSecondUpdate();
|
(releaseSecondUpdate as () => void)();
|
||||||
|
|
||||||
await Promise.all([inFlight, forcedOne, forcedTwo]);
|
await Promise.all([inFlight, forcedOne, forcedTwo]);
|
||||||
expect(updateCalls).toBe(3);
|
expect(updateCalls).toBe(3);
|
||||||
@@ -787,7 +792,9 @@ describe("QmdMemoryManager", () => {
|
|||||||
const { manager, resolved } = await createManager();
|
const { manager, resolved } = await createManager();
|
||||||
|
|
||||||
await manager.search("test", { sessionKey: "agent:main:slack:dm:u123" });
|
await manager.search("test", { sessionKey: "agent:main:slack:dm:u123" });
|
||||||
const searchCall = spawnMock.mock.calls.find((call) => call[1]?.[0] === "search");
|
const searchCall = spawnMock.mock.calls.find(
|
||||||
|
(call: unknown[]) => (call[1] as string[])?.[0] === "search",
|
||||||
|
);
|
||||||
const maxResults = resolved.qmd?.limits.maxResults;
|
const maxResults = resolved.qmd?.limits.maxResults;
|
||||||
if (!maxResults) {
|
if (!maxResults) {
|
||||||
throw new Error("qmd maxResults missing");
|
throw new Error("qmd maxResults missing");
|
||||||
@@ -843,8 +850,8 @@ describe("QmdMemoryManager", () => {
|
|||||||
).resolves.toEqual([]);
|
).resolves.toEqual([]);
|
||||||
|
|
||||||
const queryCalls = spawnMock.mock.calls
|
const queryCalls = spawnMock.mock.calls
|
||||||
.map((call) => call[1] as string[])
|
.map((call: unknown[]) => call[1] as string[])
|
||||||
.filter((args) => args[0] === "query");
|
.filter((args: string[]) => args[0] === "query");
|
||||||
expect(queryCalls).toEqual([
|
expect(queryCalls).toEqual([
|
||||||
["query", "test", "--json", "-n", String(maxResults), "-c", "workspace-main"],
|
["query", "test", "--json", "-n", String(maxResults), "-c", "workspace-main"],
|
||||||
["query", "test", "--json", "-n", String(maxResults), "-c", "notes-main"],
|
["query", "test", "--json", "-n", String(maxResults), "-c", "notes-main"],
|
||||||
@@ -894,8 +901,8 @@ describe("QmdMemoryManager", () => {
|
|||||||
).resolves.toEqual([]);
|
).resolves.toEqual([]);
|
||||||
|
|
||||||
const searchAndQueryCalls = spawnMock.mock.calls
|
const searchAndQueryCalls = spawnMock.mock.calls
|
||||||
.map((call) => call[1] as string[])
|
.map((call: unknown[]) => call[1] as string[])
|
||||||
.filter((args) => args[0] === "search" || args[0] === "query");
|
.filter((args: string[]) => args[0] === "search" || args[0] === "query");
|
||||||
expect(searchAndQueryCalls).toEqual([
|
expect(searchAndQueryCalls).toEqual([
|
||||||
[
|
[
|
||||||
"search",
|
"search",
|
||||||
@@ -931,7 +938,9 @@ describe("QmdMemoryManager", () => {
|
|||||||
|
|
||||||
const results = await manager.search("test", { sessionKey: "agent:main:slack:dm:u123" });
|
const results = await manager.search("test", { sessionKey: "agent:main:slack:dm:u123" });
|
||||||
expect(results).toEqual([]);
|
expect(results).toEqual([]);
|
||||||
expect(spawnMock.mock.calls.some((call) => call[1]?.[0] === "query")).toBe(false);
|
expect(
|
||||||
|
spawnMock.mock.calls.some((call: unknown[]) => (call[1] as string[])?.[0] === "query"),
|
||||||
|
).toBe(false);
|
||||||
await manager.close();
|
await manager.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1081,12 +1090,13 @@ describe("QmdMemoryManager", () => {
|
|||||||
"utf-8",
|
"utf-8",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const currentMemory = cfg.memory;
|
||||||
cfg = {
|
cfg = {
|
||||||
...cfg,
|
...cfg,
|
||||||
memory: {
|
memory: {
|
||||||
...cfg.memory,
|
...currentMemory,
|
||||||
qmd: {
|
qmd: {
|
||||||
...cfg.memory.qmd,
|
...currentMemory?.qmd,
|
||||||
sessions: {
|
sessions: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,19 +1,26 @@
|
|||||||
import type { TUI } from "@mariozechner/pi-tui";
|
|
||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import type { ChatLog } from "./components/chat-log.js";
|
|
||||||
import { createEventHandlers } from "./tui-event-handlers.js";
|
import { createEventHandlers } from "./tui-event-handlers.js";
|
||||||
import type { AgentEvent, ChatEvent, TuiStateAccess } from "./tui-types.js";
|
import type { AgentEvent, ChatEvent, TuiStateAccess } from "./tui-types.js";
|
||||||
|
|
||||||
type MockChatLog = Pick<
|
type MockFn = ReturnType<typeof vi.fn>;
|
||||||
ChatLog,
|
type HandlerChatLog = {
|
||||||
| "startTool"
|
startTool: (...args: unknown[]) => void;
|
||||||
| "updateToolResult"
|
updateToolResult: (...args: unknown[]) => void;
|
||||||
| "addSystem"
|
addSystem: (...args: unknown[]) => void;
|
||||||
| "updateAssistant"
|
updateAssistant: (...args: unknown[]) => void;
|
||||||
| "finalizeAssistant"
|
finalizeAssistant: (...args: unknown[]) => void;
|
||||||
| "dropAssistant"
|
dropAssistant: (...args: unknown[]) => void;
|
||||||
>;
|
};
|
||||||
type MockTui = Pick<TUI, "requestRender">;
|
type HandlerTui = { requestRender: (...args: unknown[]) => void };
|
||||||
|
type MockChatLog = {
|
||||||
|
startTool: MockFn;
|
||||||
|
updateToolResult: MockFn;
|
||||||
|
addSystem: MockFn;
|
||||||
|
updateAssistant: MockFn;
|
||||||
|
finalizeAssistant: MockFn;
|
||||||
|
dropAssistant: MockFn;
|
||||||
|
};
|
||||||
|
type MockTui = { requestRender: MockFn };
|
||||||
|
|
||||||
describe("tui-event-handlers: handleAgentEvent", () => {
|
describe("tui-event-handlers: handleAgentEvent", () => {
|
||||||
const makeState = (overrides?: Partial<TuiStateAccess>): TuiStateAccess => ({
|
const makeState = (overrides?: Partial<TuiStateAccess>): TuiStateAccess => ({
|
||||||
@@ -40,15 +47,15 @@ describe("tui-event-handlers: handleAgentEvent", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const makeContext = (state: TuiStateAccess) => {
|
const makeContext = (state: TuiStateAccess) => {
|
||||||
const chatLog: MockChatLog = {
|
const chatLog = {
|
||||||
startTool: vi.fn(),
|
startTool: vi.fn(),
|
||||||
updateToolResult: vi.fn(),
|
updateToolResult: vi.fn(),
|
||||||
addSystem: vi.fn(),
|
addSystem: vi.fn(),
|
||||||
updateAssistant: vi.fn(),
|
updateAssistant: vi.fn(),
|
||||||
finalizeAssistant: vi.fn(),
|
finalizeAssistant: vi.fn(),
|
||||||
dropAssistant: vi.fn(),
|
dropAssistant: vi.fn(),
|
||||||
};
|
} as unknown as MockChatLog & HandlerChatLog;
|
||||||
const tui: MockTui = { requestRender: vi.fn() };
|
const tui = { requestRender: vi.fn() } as unknown as MockTui & HandlerTui;
|
||||||
const setActivityStatus = vi.fn();
|
const setActivityStatus = vi.fn();
|
||||||
const loadHistory = vi.fn();
|
const loadHistory = vi.fn();
|
||||||
const localRunIds = new Set<string>();
|
const localRunIds = new Set<string>();
|
||||||
@@ -136,7 +143,8 @@ describe("tui-event-handlers: handleAgentEvent", () => {
|
|||||||
addSystem: vi.fn(),
|
addSystem: vi.fn(),
|
||||||
updateAssistant: vi.fn(),
|
updateAssistant: vi.fn(),
|
||||||
finalizeAssistant: vi.fn(),
|
finalizeAssistant: vi.fn(),
|
||||||
},
|
dropAssistant: vi.fn(),
|
||||||
|
} as unknown as HandlerChatLog,
|
||||||
tui,
|
tui,
|
||||||
state,
|
state,
|
||||||
setActivityStatus,
|
setActivityStatus,
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import type { TUI } from "@mariozechner/pi-tui";
|
|
||||||
import type { ChatLog } from "./components/chat-log.js";
|
|
||||||
import { asString, extractTextFromMessage, isCommandMessage } from "./tui-formatters.js";
|
import { asString, extractTextFromMessage, isCommandMessage } from "./tui-formatters.js";
|
||||||
import { TuiStreamAssembler } from "./tui-stream-assembler.js";
|
import { TuiStreamAssembler } from "./tui-stream-assembler.js";
|
||||||
import type { AgentEvent, ChatEvent, TuiStateAccess } from "./tui-types.js";
|
import type { AgentEvent, ChatEvent, TuiStateAccess } from "./tui-types.js";
|
||||||
|
|
||||||
type EventHandlerContext = {
|
type EventHandlerContext = {
|
||||||
chatLog: ChatLog;
|
chatLog: {
|
||||||
tui: TUI;
|
startTool: (...args: unknown[]) => void;
|
||||||
|
updateToolResult: (...args: unknown[]) => void;
|
||||||
|
addSystem: (...args: unknown[]) => void;
|
||||||
|
updateAssistant: (...args: unknown[]) => void;
|
||||||
|
finalizeAssistant: (...args: unknown[]) => void;
|
||||||
|
dropAssistant: (...args: unknown[]) => void;
|
||||||
|
};
|
||||||
|
tui: { requestRender: (...args: unknown[]) => void };
|
||||||
state: TuiStateAccess;
|
state: TuiStateAccess;
|
||||||
setActivityStatus: (text: string) => void;
|
setActivityStatus: (text: string) => void;
|
||||||
refreshSessionInfo?: () => Promise<void>;
|
refreshSessionInfo?: () => Promise<void>;
|
||||||
|
|||||||
Reference in New Issue
Block a user