import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; // Mock the runtime before importing buildUserAgent const mockRuntime = { version: "2026.3.19", }; vi.mock("./runtime.js", () => ({ getMSTeamsRuntime: vi.fn(() => mockRuntime), })); import { fetchGraphJson } from "./graph.js"; import { getMSTeamsRuntime } from "./runtime.js"; import { buildUserAgent, ensureUserAgentHeader, resetUserAgentCache } from "./user-agent.js"; describe("buildUserAgent", () => { beforeEach(() => { resetUserAgentCache(); vi.mocked(getMSTeamsRuntime).mockReturnValue(mockRuntime as never); }); afterEach(() => { vi.restoreAllMocks(); }); it("returns teams.ts[apps]/ OpenClaw/ format", () => { const ua = buildUserAgent(); expect(ua).toMatch(/^teams\.ts\[apps\]\/.+ OpenClaw\/2026\.3\.19$/); }); it("reflects the runtime version", () => { vi.mocked(getMSTeamsRuntime).mockReturnValue({ version: "1.2.3" } as never); const ua = buildUserAgent(); expect(ua).toMatch(/OpenClaw\/1\.2\.3$/); }); it("returns OpenClaw/unknown when runtime is not initialized", () => { vi.mocked(getMSTeamsRuntime).mockImplementation(() => { throw new Error("MSTeams runtime not initialized"); }); const ua = buildUserAgent(); expect(ua).toMatch(/OpenClaw\/unknown$/); // SDK version should still be present expect(ua).toMatch(/^teams\.ts\[apps\]\//); }); it("sends the generated User-Agent in Graph requests by default", async () => { const mockFetch = vi.fn().mockResolvedValueOnce({ ok: true, json: async () => ({ value: [] }), }); vi.stubGlobal("fetch", mockFetch); await fetchGraphJson({ token: "test-token", path: "/groups" }); expect(mockFetch).toHaveBeenCalledOnce(); const [, init] = mockFetch.mock.calls[0]; expect(init.headers["User-Agent"]).toMatch(/^teams\.ts\[apps\]\/.+ OpenClaw\/2026\.3\.19$/); expect(init.headers).toHaveProperty("Authorization", "Bearer test-token"); }); it("lets caller headers override the default Graph User-Agent", async () => { const mockFetch = vi.fn().mockResolvedValueOnce({ ok: true, json: async () => ({ value: [] }), }); vi.stubGlobal("fetch", mockFetch); await fetchGraphJson({ token: "test-token", path: "/groups", headers: { "User-Agent": "custom-agent/1.0" }, }); const [, init] = mockFetch.mock.calls[0]; expect(init.headers["User-Agent"]).toBe("custom-agent/1.0"); }); it("adds the generated User-Agent to Headers instances without overwriting callers", () => { const generated = ensureUserAgentHeader(); expect(generated.get("User-Agent")).toMatch(/^teams\.ts\[apps\]\/.+ OpenClaw\/2026\.3\.19$/); const custom = ensureUserAgentHeader({ "User-Agent": "custom-agent/2.0" }); expect(custom.get("User-Agent")).toBe("custom-agent/2.0"); }); });