555e282cc4
pi-agent-plugin checks / lint (push) Has been cancelled
pi-agent-plugin checks / test (20) (push) Has been cancelled
pi-agent-plugin checks / test (22) (push) Has been cancelled
pi-agent-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / check_changes (push) Has been cancelled
TypeScript SDK CI / changelog_check (push) Has been cancelled
ci / changelog_check (push) Has been cancelled
ci / check_changes (push) Has been cancelled
ci / build_mem0 (3.10) (push) Has been cancelled
ci / build_mem0 (3.11) (push) Has been cancelled
ci / build_mem0 (3.12) (push) Has been cancelled
CLI Node CI / lint (push) Has been cancelled
CLI Node CI / test (20) (push) Has been cancelled
CLI Node CI / test (22) (push) Has been cancelled
CLI Node CI / build (push) Has been cancelled
CLI Python CI / lint (push) Has been cancelled
CLI Python CI / test (3.10) (push) Has been cancelled
CLI Python CI / test (3.11) (push) Has been cancelled
CLI Python CI / test (3.12) (push) Has been cancelled
CLI Python CI / build (push) Has been cancelled
openclaw checks / lint (push) Has been cancelled
openclaw checks / test (20) (push) Has been cancelled
openclaw checks / test (22) (push) Has been cancelled
openclaw checks / build (push) Has been cancelled
opencode-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (22) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (22) (push) Has been cancelled
240 lines
6.7 KiB
TypeScript
240 lines
6.7 KiB
TypeScript
/**
|
|
* Tests for cli/config-file.ts — file-based config helpers.
|
|
*
|
|
* All filesystem operations are mocked via fs-safe.ts so tests never
|
|
* touch the real disk.
|
|
*/
|
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
|
vi.mock("../fs-safe.ts", () => ({
|
|
readText: vi.fn(),
|
|
exists: vi.fn(),
|
|
writeText: vi.fn(),
|
|
mkdirp: vi.fn(),
|
|
unlink: vi.fn(),
|
|
}));
|
|
|
|
import { readText, exists, writeText, mkdirp } from "../fs-safe.ts";
|
|
import {
|
|
readPluginAuth,
|
|
writePluginAuth,
|
|
getBaseUrl,
|
|
DEFAULT_BASE_URL,
|
|
} from "../cli/config-file.ts";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
const mockExists = exists as ReturnType<typeof vi.fn>;
|
|
const mockReadText = readText as ReturnType<typeof vi.fn>;
|
|
const mockWriteText = writeText as ReturnType<typeof vi.fn>;
|
|
const mockMkdirp = mkdirp as ReturnType<typeof vi.fn>;
|
|
|
|
function setConfigFile(obj: Record<string, unknown>): void {
|
|
mockExists.mockReturnValue(true);
|
|
mockReadText.mockReturnValue(JSON.stringify(obj));
|
|
}
|
|
|
|
function setNoFile(): void {
|
|
mockExists.mockReturnValue(false);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.resetAllMocks();
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// readPluginAuth
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe("readPluginAuth", () => {
|
|
it("returns empty object when config file does not exist", () => {
|
|
setNoFile();
|
|
expect(readPluginAuth()).toEqual({});
|
|
});
|
|
|
|
it("returns empty object when config has no plugins section", () => {
|
|
setConfigFile({ someOtherKey: true });
|
|
expect(readPluginAuth()).toEqual({});
|
|
});
|
|
|
|
it("reads all fields correctly from nested config", () => {
|
|
setConfigFile({
|
|
plugins: {
|
|
entries: {
|
|
"openclaw-mem0": {
|
|
enabled: true,
|
|
config: {
|
|
apiKey: "sk-test-123",
|
|
baseUrl: "https://custom.api.com",
|
|
userId: "user-1",
|
|
mode: "platform",
|
|
autoRecall: true,
|
|
autoCapture: false,
|
|
topK: 10,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const auth = readPluginAuth();
|
|
expect(auth).toEqual({
|
|
apiKey: "sk-test-123",
|
|
baseUrl: "https://custom.api.com",
|
|
userId: "user-1",
|
|
mode: "platform",
|
|
autoRecall: true,
|
|
autoCapture: false,
|
|
topK: 10,
|
|
});
|
|
});
|
|
|
|
it("handles snake_case aliases (api_key, base_url, user_id)", () => {
|
|
setConfigFile({
|
|
plugins: {
|
|
entries: {
|
|
"openclaw-mem0": {
|
|
enabled: true,
|
|
config: {
|
|
api_key: "sk-snake",
|
|
base_url: "https://snake.api.com",
|
|
user_id: "user-snake",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const auth = readPluginAuth();
|
|
expect(auth.apiKey).toBe("sk-snake");
|
|
expect(auth.baseUrl).toBe("https://snake.api.com");
|
|
expect(auth.userId).toBe("user-snake");
|
|
});
|
|
|
|
it("throws error when JSON is invalid (prevents config destruction)", () => {
|
|
mockExists.mockReturnValue(true);
|
|
mockReadText.mockReturnValue("not valid json {{{");
|
|
expect(() => readPluginAuth()).toThrow(/Failed to parse[\s\S]*Fix the JSON syntax error/);
|
|
});
|
|
|
|
it("returns empty object when config file is empty", () => {
|
|
mockExists.mockReturnValue(true);
|
|
mockReadText.mockReturnValue(" ");
|
|
expect(readPluginAuth()).toEqual({});
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// writePluginAuth
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe("writePluginAuth", () => {
|
|
it("creates nested structure from scratch when no config exists", () => {
|
|
setNoFile();
|
|
// exists returns false for both the file (readFullConfig) and the dir (writeFullConfig)
|
|
mockExists.mockReturnValue(false);
|
|
|
|
writePluginAuth({ apiKey: "sk-new", userId: "u1" });
|
|
|
|
expect(mockMkdirp).toHaveBeenCalled();
|
|
expect(mockWriteText).toHaveBeenCalledOnce();
|
|
|
|
const written = JSON.parse(mockWriteText.mock.calls[0][1]);
|
|
expect(written.plugins.entries["openclaw-mem0"].enabled).toBe(true);
|
|
expect(written.plugins.entries["openclaw-mem0"].config.apiKey).toBe(
|
|
"sk-new",
|
|
);
|
|
expect(written.plugins.entries["openclaw-mem0"].config.userId).toBe("u1");
|
|
});
|
|
|
|
it("merges into existing config preserving other data", () => {
|
|
setConfigFile({
|
|
otherSetting: "keep-me",
|
|
plugins: {
|
|
entries: {
|
|
"openclaw-mem0": {
|
|
enabled: true,
|
|
config: {
|
|
apiKey: "sk-old",
|
|
mode: "platform",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
writePluginAuth({ baseUrl: "https://new.api.com" });
|
|
|
|
const written = JSON.parse(mockWriteText.mock.calls[0][1]);
|
|
// Existing fields preserved
|
|
expect(written.otherSetting).toBe("keep-me");
|
|
expect(written.plugins.entries["openclaw-mem0"].config.apiKey).toBe(
|
|
"sk-old",
|
|
);
|
|
expect(written.plugins.entries["openclaw-mem0"].config.mode).toBe(
|
|
"platform",
|
|
);
|
|
// New field added
|
|
expect(written.plugins.entries["openclaw-mem0"].config.baseUrl).toBe(
|
|
"https://new.api.com",
|
|
);
|
|
});
|
|
|
|
it("creates directory if missing", () => {
|
|
// File doesn't exist (readFullConfig returns {}), dir doesn't exist
|
|
mockExists.mockReturnValue(false);
|
|
|
|
writePluginAuth({ apiKey: "sk-test" });
|
|
|
|
expect(mockMkdirp).toHaveBeenCalledWith(
|
|
expect.stringContaining(".openclaw"),
|
|
0o700,
|
|
);
|
|
});
|
|
|
|
it("skips undefined values", () => {
|
|
setNoFile();
|
|
mockExists.mockReturnValue(false);
|
|
|
|
writePluginAuth({
|
|
apiKey: "sk-set",
|
|
baseUrl: undefined,
|
|
userId: undefined,
|
|
});
|
|
|
|
const written = JSON.parse(mockWriteText.mock.calls[0][1]);
|
|
const cfg = written.plugins.entries["openclaw-mem0"].config;
|
|
expect(cfg.apiKey).toBe("sk-set");
|
|
expect(cfg).not.toHaveProperty("baseUrl");
|
|
expect(cfg).not.toHaveProperty("userId");
|
|
});
|
|
});
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// getBaseUrl
|
|
// ---------------------------------------------------------------------------
|
|
|
|
describe("getBaseUrl", () => {
|
|
it("returns configured URL when baseUrl is set", () => {
|
|
setConfigFile({
|
|
plugins: {
|
|
entries: {
|
|
"openclaw-mem0": {
|
|
enabled: true,
|
|
config: { baseUrl: "https://custom.example.com" },
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
expect(getBaseUrl()).toBe("https://custom.example.com");
|
|
});
|
|
|
|
it("returns default URL when baseUrl is not configured", () => {
|
|
setNoFile();
|
|
expect(getBaseUrl()).toBe(DEFAULT_BASE_URL);
|
|
});
|
|
});
|