diff --git a/src/infra/gemini-auth.test.ts b/src/infra/gemini-auth.test.ts new file mode 100644 index 00000000000..6e496f75ec2 --- /dev/null +++ b/src/infra/gemini-auth.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; +import { parseGeminiAuth } from "./gemini-auth.js"; + +describe("parseGeminiAuth", () => { + it("returns bearer auth for OAuth JSON tokens", () => { + expect(parseGeminiAuth('{"token":"oauth-token","projectId":"demo"}')).toEqual({ + headers: { + Authorization: "Bearer oauth-token", + "Content-Type": "application/json", + }, + }); + }); + + it("falls back to API key auth for invalid or unusable OAuth payloads", () => { + expect(parseGeminiAuth('{"token":"","projectId":"demo"}')).toEqual({ + headers: { + "x-goog-api-key": '{"token":"","projectId":"demo"}', + "Content-Type": "application/json", + }, + }); + expect(parseGeminiAuth("{not-json}")).toEqual({ + headers: { + "x-goog-api-key": "{not-json}", + "Content-Type": "application/json", + }, + }); + expect(parseGeminiAuth(' {"token":"oauth-token"}')).toEqual({ + headers: { + "x-goog-api-key": ' {"token":"oauth-token"}', + "Content-Type": "application/json", + }, + }); + }); +}); diff --git a/src/infra/pairing-files.test.ts b/src/infra/pairing-files.test.ts new file mode 100644 index 00000000000..8f891036956 --- /dev/null +++ b/src/infra/pairing-files.test.ts @@ -0,0 +1,85 @@ +import { describe, expect, it, vi } from "vitest"; +import { + pruneExpiredPending, + resolvePairingPaths, + upsertPendingPairingRequest, +} from "./pairing-files.js"; + +describe("pairing file helpers", () => { + it("resolves pairing file paths from explicit base dirs", () => { + expect(resolvePairingPaths("/tmp/openclaw-state", "devices")).toEqual({ + dir: "/tmp/openclaw-state/devices", + pendingPath: "/tmp/openclaw-state/devices/pending.json", + pairedPath: "/tmp/openclaw-state/devices/paired.json", + }); + }); + + it("prunes only entries older than the ttl", () => { + const pendingById = { + stale: { ts: 10, requestId: "stale" }, + edge: { ts: 50, requestId: "edge" }, + fresh: { ts: 70, requestId: "fresh" }, + }; + + pruneExpiredPending(pendingById, 100, 50); + + expect(pendingById).toEqual({ + edge: { ts: 50, requestId: "edge" }, + fresh: { ts: 70, requestId: "fresh" }, + }); + }); + + it("reuses existing pending requests without persisting again", async () => { + const persist = vi.fn(async () => undefined); + const existing = { requestId: "req-1", deviceId: "device-1", ts: 1 }; + const pendingById = { "req-1": existing }; + + await expect( + upsertPendingPairingRequest({ + pendingById, + isExisting: (pending) => pending.deviceId === "device-1", + createRequest: vi.fn(() => ({ requestId: "req-2", deviceId: "device-1", ts: 2 })), + isRepair: false, + persist, + }), + ).resolves.toEqual({ + status: "pending", + request: existing, + created: false, + }); + expect(persist).not.toHaveBeenCalled(); + }); + + it("creates and persists new pending requests with the repair flag", async () => { + const persist = vi.fn(async () => undefined); + const createRequest = vi.fn((isRepair: boolean) => ({ + requestId: "req-2", + deviceId: "device-2", + ts: 2, + isRepair, + })); + const pendingById: Record< + string, + { requestId: string; deviceId: string; ts: number; isRepair: boolean } + > = {}; + + await expect( + upsertPendingPairingRequest({ + pendingById, + isExisting: (pending) => pending.deviceId === "device-2", + createRequest, + isRepair: true, + persist, + }), + ).resolves.toEqual({ + status: "pending", + request: { requestId: "req-2", deviceId: "device-2", ts: 2, isRepair: true }, + created: true, + }); + expect(createRequest).toHaveBeenCalledWith(true); + expect(persist).toHaveBeenCalledOnce(); + expect(pendingById).toEqual({ + "req-2": { requestId: "req-2", deviceId: "device-2", ts: 2, isRepair: true }, + }); + }); +});