Files
wehub-resource-sync 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
chore: import upstream snapshot with attribution
2026-07-13 13:03:45 +08:00

358 lines
11 KiB
TypeScript

/**
* Tests for providers.ts — providerToBackend adapter layer.
*
* Verifies that the Backend wrapper correctly delegates to the
* underlying Mem0Provider methods with proper argument mapping.
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
import {
createProvider,
customCategoryMapToList,
providerToBackend,
} from "../providers.ts";
// ---------------------------------------------------------------------------
// Mock provider factory
// ---------------------------------------------------------------------------
function createMockProvider() {
return {
search: vi
.fn()
.mockResolvedValue([{ id: "m1", memory: "found", score: 0.9 }]),
add: vi.fn().mockResolvedValue({
results: [{ id: "m1", event: "ADD", memory: "stored" }],
}),
get: vi
.fn()
.mockResolvedValue({ id: "m1", memory: "test", created_at: "2026-01-01" }),
getAll: vi.fn().mockResolvedValue([{ id: "m1", memory: "listed" }]),
update: vi.fn().mockResolvedValue(undefined),
delete: vi.fn().mockResolvedValue(undefined),
deleteAll: vi.fn().mockResolvedValue(undefined),
history: vi.fn().mockResolvedValue([]),
};
}
const DEFAULT_USER = "test-user";
beforeEach(() => {
vi.resetAllMocks();
});
// ---------------------------------------------------------------------------
// customCategoryMapToList
// ---------------------------------------------------------------------------
describe("customCategoryMapToList", () => {
it("converts category maps to the Mem0 SDK list shape", () => {
expect(
customCategoryMapToList({
preference: "User preferences",
work: "Work context",
}),
).toEqual([
{ preference: "User preferences" },
{ work: "Work context" },
]);
});
it("returns undefined for empty or missing category maps", () => {
expect(customCategoryMapToList()).toBeUndefined();
expect(customCategoryMapToList({})).toBeUndefined();
});
});
// ---------------------------------------------------------------------------
// PlatformProvider
// ---------------------------------------------------------------------------
describe("PlatformProvider", () => {
it("passes custom_categories as a list to the Mem0 SDK", async () => {
let addOptions: Record<string, unknown> | undefined;
vi.doMock("mem0ai", () => ({
default: class MockMemoryClient {
async add(_messages: unknown, opts: Record<string, unknown>) {
addOptions = opts;
return { results: [] };
}
},
}));
const provider = createProvider(
{
mode: "platform",
apiKey: "test-key",
userId: "test-user",
autoCapture: true,
autoRecall: true,
customInstructions: "Store durable facts.",
customCategories: {},
searchThreshold: 0.1,
topK: 5,
},
{ resolvePath: (p: string) => p } as any,
);
await provider.add([{ role: "user", content: "Remember this" }], {
user_id: "test-user",
custom_categories: customCategoryMapToList({
preference: "User preferences",
}),
source: "OPENCLAW",
});
expect(addOptions?.customCategories).toEqual([
{ preference: "User preferences" },
]);
vi.doUnmock("mem0ai");
});
});
// ---------------------------------------------------------------------------
// search
// ---------------------------------------------------------------------------
describe("providerToBackend — search", () => {
// v3.0.0: keyword_search, reranking removed from SDK
it("delegates to provider.search with correct options", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
const results = await backend.search("hello world", {
topK: 10,
threshold: 0.5,
filters: { category: "preference" },
});
expect(provider.search).toHaveBeenCalledWith("hello world", {
user_id: DEFAULT_USER,
top_k: 10,
threshold: 0.5,
filters: { category: "preference" },
source: "OPENCLAW",
});
expect(results).toHaveLength(1);
expect((results[0] as any).id).toBe("m1");
});
it("uses default userId when opts.userId is not provided", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
await backend.search("query");
// v3.0.0: keyword_search, reranking removed
expect(provider.search).toHaveBeenCalledWith("query", {
user_id: DEFAULT_USER,
top_k: undefined,
threshold: undefined,
filters: undefined,
source: "OPENCLAW",
});
});
});
// ---------------------------------------------------------------------------
// add
// ---------------------------------------------------------------------------
describe("providerToBackend — add", () => {
it("delegates to provider.add with content as message", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
const result = await backend.add("Remember this fact");
expect(provider.add).toHaveBeenCalledWith(
[{ role: "user", content: "Remember this fact" }],
expect.objectContaining({ user_id: DEFAULT_USER }),
);
expect(result).toBeDefined();
});
it("passes messages array when provided", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
const messages = [
{ role: "user", content: "Hi" },
{ role: "assistant", content: "Hello" },
];
await backend.add(undefined, messages);
expect(provider.add).toHaveBeenCalledWith(
messages,
expect.objectContaining({ user_id: DEFAULT_USER }),
);
});
// v3.0.0: immutable, expiration_date removed from SDK
it("forwards optional add options", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
await backend.add("fact", undefined, {
runId: "run-1",
metadata: { source: "test" },
infer: false,
});
expect(provider.add).toHaveBeenCalledWith(
[{ role: "user", content: "fact" }],
expect.objectContaining({
user_id: DEFAULT_USER,
run_id: "run-1",
metadata: { source: "test" },
infer: false,
}),
);
});
});
// ---------------------------------------------------------------------------
// get
// ---------------------------------------------------------------------------
describe("providerToBackend — get", () => {
it("delegates to provider.get", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
const result = await backend.get("mem-123");
expect(provider.get).toHaveBeenCalledWith("mem-123");
expect((result as any).id).toBe("m1");
});
});
// ---------------------------------------------------------------------------
// listMemories
// ---------------------------------------------------------------------------
describe("providerToBackend — listMemories", () => {
it("delegates to provider.getAll", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
const results = await backend.listMemories({ pageSize: 50 });
expect(provider.getAll).toHaveBeenCalledWith({
user_id: DEFAULT_USER,
page_size: 50,
source: "OPENCLAW",
});
expect(results).toHaveLength(1);
});
it("uses default userId when opts.userId is not provided", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
await backend.listMemories();
expect(provider.getAll).toHaveBeenCalledWith({
user_id: DEFAULT_USER,
page_size: undefined,
source: "OPENCLAW",
});
});
});
// ---------------------------------------------------------------------------
// update
// ---------------------------------------------------------------------------
describe("providerToBackend — update", () => {
it("calls provider.update with content", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
const result = await backend.update("mem-123", "updated text");
expect(provider.update).toHaveBeenCalledWith("mem-123", "updated text");
expect((result as any).id).toBe("mem-123");
expect((result as any).updated).toBe(true);
});
it("warns and skips when only metadata is provided (no content)", async () => {
const provider = createMockProvider();
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const backend = providerToBackend(provider as any, DEFAULT_USER);
const result = await backend.update("mem-123", undefined, {
tag: "important",
});
expect(provider.update).not.toHaveBeenCalled();
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining("metadata updates are not supported"),
);
expect((result as any).id).toBe("mem-123");
warnSpy.mockRestore();
});
});
// ---------------------------------------------------------------------------
// delete
// ---------------------------------------------------------------------------
describe("providerToBackend — delete", () => {
it("calls provider.delete with memoryId", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
const result = await backend.delete("mem-456");
expect(provider.delete).toHaveBeenCalledWith("mem-456");
expect((result as any).deleted).toBe("mem-456");
});
it("calls provider.deleteAll when opts.all is true", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
const result = await backend.delete(undefined, {
all: true,
userId: "custom-user",
});
expect(provider.deleteAll).toHaveBeenCalledWith("custom-user");
expect((result as any).deleted).toBe("all");
});
});
// ---------------------------------------------------------------------------
// deleteEntities (platform-only)
// ---------------------------------------------------------------------------
describe("providerToBackend — deleteEntities", () => {
it("throws platform-only error", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
await expect(
backend.deleteEntities({ userId: DEFAULT_USER }),
).rejects.toThrow("platform mode");
});
});
// ---------------------------------------------------------------------------
// status
// ---------------------------------------------------------------------------
describe("providerToBackend — status", () => {
it("returns connected: true", async () => {
const provider = createMockProvider();
const backend = providerToBackend(provider as any, DEFAULT_USER);
const result = await backend.status();
expect((result as any).connected).toBe(true);
expect((result as any).backend).toBe("oss");
});
});