Spracherkennung für: .ts vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
captureHttpExchange,
finalizeDebugProxyCapture,
initializeDebugProxyCapture,
} from "./runtime.js";
const storeState = vi.hoisted(() => {
const events: Record<string, unknown>[] = [];
const store = {
upsertSession: vi.fn(),
endSession: vi.fn(),
recordEvent: vi.fn((event: Record<string, unknown>) => {
events.push(event);
}),
};
return {
events,
store,
closeDebugProxyCaptureStore: vi.fn(),
};
});
vi.mock("./store.sqlite.js", () => ({
closeDebugProxyCaptureStore: storeState.closeDebugProxyCaptureStore,
getDebugProxyCaptureStore: () => storeState.store,
persistEventPayload: (
_store: unknown,
payload: { data?: Buffer | string | null; contentType?: string },
) => ({
contentType: payload.contentType,
...(typeof payload.data === "string" ? { dataText: payload.data } : {}),
}),
safeJsonString: (value: unknown) => (value == null ? undefined : JSON.stringify(value)),
}));
describe("debug proxy runtime", () => {
const envKeys = [
"OPENCLAW_DEBUG_PROXY_ENABLED",
"OPENCLAW_DEBUG_PROXY_DB_PATH",
"OPENCLAW_DEBUG_PROXY_BLOB_DIR",
"OPENCLAW_DEBUG_PROXY_SESSION_ID",
"OPENCLAW_DEBUG_PROXY_SOURCE_PROCESS",
] as const;
const savedEnv = Object.fromEntries(envKeys.map((key) => [key, process.env[key]]));
const originalFetch = globalThis.fetch;
beforeEach(() => {
storeState.events.length = 0;
storeState.store.upsertSession.mockClear();
storeState.store.endSession.mockClear();
storeState.store.recordEvent.mockClear();
storeState.closeDebugProxyCaptureStore.mockClear();
process.env.OPENCLAW_DEBUG_PROXY_ENABLED = "1";
process.env.OPENCLAW_DEBUG_PROXY_DB_PATH = "/tmp/openclaw-proxy-runtime-test.sqlit
e";
process.env.OPENCLAW_DEBUG_PROXY_BLOB_DIR = "/tmp/openclaw-proxy-runtime-test-blobs";
process.env.OPENCLAW_DEBUG_PROXY_SESSION_ID = "runtime-test-session";
process.env.OPENCLAW_DEBUG_PROXY_SOURCE_PROCESS = "runtime-test";
});
afterEach(() => {
globalThis.fetch = originalFetch;
for (const key of envKeys) {
const value = savedEnv[key];
if (value == null) {
delete process.env[key];
} else {
process.env[key] = value;
}
}
});
it("captures ambient global fetch calls when debug proxy mode is enabled", async () => {
globalThis.fetch = vi.fn(async () => ({ status: 200 }) as Response) as typeof fetch;
initializeDebugProxyCapture("test");
await globalThis.fetch("https://api.minimax.io/anthropic/messages", {
method: "POST",
headers: { "content-type": "application/json" },
body: '{"input":"hello"}',
});
finalizeDebugProxyCapture();
const events = storeState.events.filter((event) => event.sessionId === "runtime-test-session");
expect(events.some((event) => event.host === "api.minimax.io")).toBe(true);
expect(events.some((event) => event.kind === "request")).toBe(true);
expect(events.some((event) => event.kind === "response")).toBe(true);
});
it("redacts sensitive request and response headers before persistence", async () => {
initializeDebugProxyCapture("test");
captureHttpExchange({
url: "https://discord.com/api/v10/gateway/bot",
method: "GET",
requestHeaders: {
Authorization: "Bot discord-token",
Cookie: "sid=session-token",
"x-api-key": "provider-key",
"content-type": "application/json",
"x-safe": "visible",
},
response: new Response("{}", {
status: 200,
headers: {
"content-type": "application/json",
"set-cookie": "sid=response-token",
},
}),
});
await new Promise((resolve) => setImmediate(resolve));
finalizeDebugProxyCapture();
const request = storeState.events.find((event) => event.kind === "request");
expect(JSON.parse(String(request?.headersJson))).toMatchObject({
Authorization: "[REDACTED]",
Cookie: "[REDACTED]",
"x-api-key": "[REDACTED]",
"content-type": "application/json",
"x-safe": "visible",
});
const response = storeState.events.find((event) => event.kind === "response");
expect(JSON.parse(String(response?.headersJson))).toMatchObject({
"content-type": "application/json",
"set-cookie": "[REDACTED]",
});
});
});