openclaw/src/agents/copilot-credentials.test.ts
2026-01-29 11:30:02 -06:00

141 lines
4.5 KiB
TypeScript

import { describe, expect, it, vi, beforeEach } from "vitest";
import {
isCopilotCliInstalled,
readCopilotAuthStatus,
readCopilotAuthStatusCached,
resetCopilotCredentialCacheForTest,
} from "./copilot-credentials.js";
describe("copilot-credentials", () => {
beforeEach(() => {
resetCopilotCredentialCacheForTest();
});
describe("isCopilotCliInstalled", () => {
it("returns true when copilot --version succeeds", () => {
const execSync = vi.fn().mockReturnValue("0.0.1");
const result = isCopilotCliInstalled({ execSync });
expect(result).toBe(true);
expect(execSync).toHaveBeenCalledWith("copilot --version", expect.any(Object));
});
it("returns false when copilot --version fails", () => {
const execSync = vi.fn().mockImplementation(() => {
throw new Error("ENOENT: not found");
});
const result = isCopilotCliInstalled({ execSync });
expect(result).toBe(false);
});
it("uses custom cliPath when provided", () => {
const execSync = vi.fn().mockReturnValue("0.0.1");
isCopilotCliInstalled({ cliPath: "/usr/local/bin/copilot", execSync });
expect(execSync).toHaveBeenCalledWith("/usr/local/bin/copilot --version", expect.any(Object));
});
});
describe("readCopilotAuthStatus", () => {
it("returns authenticated status when CLI reports authenticated", () => {
const execSync = vi.fn().mockReturnValue(
JSON.stringify({
isAuthenticated: true,
user: {
login: "testuser",
avatarUrl: "https://example.com/avatar.png",
},
}),
);
const result = readCopilotAuthStatus({ execSync });
expect(result).toEqual({
authenticated: true,
login: "testuser",
avatarUrl: "https://example.com/avatar.png",
});
});
it("returns not authenticated when CLI reports not authenticated", () => {
const execSync = vi.fn().mockReturnValue(
JSON.stringify({
isAuthenticated: false,
}),
);
const result = readCopilotAuthStatus({ execSync });
expect(result).toEqual({ authenticated: false });
});
it("returns null when CLI is not installed", () => {
const execSync = vi.fn().mockImplementation(() => {
const error = new Error("ENOENT: not found");
throw error;
});
const result = readCopilotAuthStatus({ execSync });
expect(result).toBe(null);
});
it("returns not authenticated on stderr indicating not logged in", () => {
const execSync = vi.fn().mockImplementation(() => {
const error = new Error("Command failed") as Error & { stderr: string };
error.stderr = "You are not logged in";
throw error;
});
const result = readCopilotAuthStatus({ execSync });
expect(result).toEqual({ authenticated: false });
});
});
describe("readCopilotAuthStatusCached", () => {
it("returns cached value within TTL", () => {
const execSync = vi.fn().mockReturnValue(
JSON.stringify({
isAuthenticated: true,
user: { login: "testuser" },
}),
);
// First call populates cache
const result1 = readCopilotAuthStatusCached({ execSync, ttlMs: 60000 });
expect(result1?.authenticated).toBe(true);
expect(execSync).toHaveBeenCalledTimes(1);
// Second call should use cache
const result2 = readCopilotAuthStatusCached({ execSync, ttlMs: 60000 });
expect(result2?.authenticated).toBe(true);
expect(execSync).toHaveBeenCalledTimes(1); // Not called again
});
it("refreshes cache after TTL expires", async () => {
const execSync = vi.fn().mockReturnValue(
JSON.stringify({
isAuthenticated: true,
user: { login: "testuser" },
}),
);
// First call with very short TTL
readCopilotAuthStatusCached({ execSync, ttlMs: 1 });
expect(execSync).toHaveBeenCalledTimes(1);
// Wait for TTL to expire
await new Promise((r) => setTimeout(r, 5));
// Second call should refresh
readCopilotAuthStatusCached({ execSync, ttlMs: 1 });
expect(execSync).toHaveBeenCalledTimes(2);
});
it("does not cache when ttlMs is 0", () => {
const execSync = vi.fn().mockReturnValue(
JSON.stringify({
isAuthenticated: true,
user: { login: "testuser" },
}),
);
readCopilotAuthStatusCached({ execSync, ttlMs: 0 });
readCopilotAuthStatusCached({ execSync, ttlMs: 0 });
expect(execSync).toHaveBeenCalledTimes(2);
});
});
});