chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,449 @@
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest";
|
||||
|
||||
vi.mock("../src/logger.js", () => ({
|
||||
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn() },
|
||||
}));
|
||||
|
||||
import { registerFacetsFunction } from "../src/functions/facets.js";
|
||||
import type { Facet } from "../src/types.js";
|
||||
|
||||
function mockKV() {
|
||||
const store = new Map<string, Map<string, unknown>>();
|
||||
return {
|
||||
get: async <T>(scope: string, key: string): Promise<T | null> => {
|
||||
return (store.get(scope)?.get(key) as T) ?? null;
|
||||
},
|
||||
set: async <T>(scope: string, key: string, data: T): Promise<T> => {
|
||||
if (!store.has(scope)) store.set(scope, new Map());
|
||||
store.get(scope)!.set(key, data);
|
||||
return data;
|
||||
},
|
||||
delete: async (scope: string, key: string): Promise<void> => {
|
||||
store.get(scope)?.delete(key);
|
||||
},
|
||||
list: async <T>(scope: string): Promise<T[]> => {
|
||||
const entries = store.get(scope);
|
||||
return entries ? (Array.from(entries.values()) as T[]) : [];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function mockSdk() {
|
||||
const functions = new Map<string, Function>();
|
||||
return {
|
||||
registerFunction: (idOrOpts: string | { id: string }, handler: Function) => {
|
||||
const id = typeof idOrOpts === "string" ? idOrOpts : idOrOpts.id;
|
||||
functions.set(id, handler);
|
||||
},
|
||||
registerTrigger: () => {},
|
||||
trigger: async (idOrInput: string | { function_id: string; payload: unknown }, data?: unknown) => {
|
||||
const id = typeof idOrInput === "string" ? idOrInput : idOrInput.function_id;
|
||||
const payload = typeof idOrInput === "string" ? data : idOrInput.payload;
|
||||
const fn = functions.get(id);
|
||||
if (!fn) throw new Error(`No function: ${id}`);
|
||||
return fn(payload);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("Facets Functions", () => {
|
||||
let sdk: ReturnType<typeof mockSdk>;
|
||||
let kv: ReturnType<typeof mockKV>;
|
||||
|
||||
beforeEach(() => {
|
||||
sdk = mockSdk();
|
||||
kv = mockKV();
|
||||
registerFacetsFunction(sdk as never, kv as never);
|
||||
});
|
||||
|
||||
describe("mem::facet-tag", () => {
|
||||
it("tags a target with a facet", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
})) as { success: boolean; facet: Facet };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.facet.id).toMatch(/^fct_/);
|
||||
expect(result.facet.targetId).toBe("act_123");
|
||||
expect(result.facet.targetType).toBe("action");
|
||||
expect(result.facet.dimension).toBe("priority");
|
||||
expect(result.facet.value).toBe("high");
|
||||
expect(result.facet.createdAt).toBeDefined();
|
||||
});
|
||||
|
||||
it("returns error when dimension is empty", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "action",
|
||||
dimension: " ",
|
||||
value: "high",
|
||||
})) as { success: boolean; error: string };
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain("dimension is required");
|
||||
});
|
||||
|
||||
it("returns error when value is empty", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "",
|
||||
})) as { success: boolean; error: string };
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain("value is required");
|
||||
});
|
||||
|
||||
it("returns error for invalid targetType", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "invalid",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
})) as { success: boolean; error: string };
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain("targetType must be one of");
|
||||
});
|
||||
|
||||
it("skips duplicate tag", async () => {
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
});
|
||||
|
||||
const result = (await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
})) as { success: boolean; facet: Facet; skipped: boolean };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.skipped).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("mem::facet-untag", () => {
|
||||
it("removes a specific value from a dimension", async () => {
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "urgent",
|
||||
});
|
||||
|
||||
const result = (await sdk.trigger("mem::facet-untag", {
|
||||
targetId: "act_123",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
})) as { success: boolean; removed: number };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.removed).toBe(1);
|
||||
|
||||
const remaining = (await sdk.trigger("mem::facet-get", {
|
||||
targetId: "act_123",
|
||||
})) as { success: boolean; dimensions: Array<{ dimension: string; values: string[] }> };
|
||||
|
||||
expect(remaining.dimensions[0].values).toEqual(["urgent"]);
|
||||
});
|
||||
|
||||
it("removes all values in a dimension when value is omitted", async () => {
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_123",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "urgent",
|
||||
});
|
||||
|
||||
const result = (await sdk.trigger("mem::facet-untag", {
|
||||
targetId: "act_123",
|
||||
dimension: "priority",
|
||||
})) as { success: boolean; removed: number };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.removed).toBe(2);
|
||||
|
||||
const remaining = (await sdk.trigger("mem::facet-get", {
|
||||
targetId: "act_123",
|
||||
})) as { success: boolean; dimensions: Array<{ dimension: string; values: string[] }> };
|
||||
|
||||
expect(remaining.dimensions).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("mem::facet-query", () => {
|
||||
beforeEach(async () => {
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_1",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_1",
|
||||
targetType: "action",
|
||||
dimension: "status",
|
||||
value: "active",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_2",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "low",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_2",
|
||||
targetType: "action",
|
||||
dimension: "status",
|
||||
value: "active",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "mem_1",
|
||||
targetType: "memory",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
});
|
||||
});
|
||||
|
||||
it("queries with matchAll (AND logic)", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-query", {
|
||||
matchAll: ["priority:high", "status:active"],
|
||||
})) as { success: boolean; results: Array<{ targetId: string }> };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.results.length).toBe(1);
|
||||
expect(result.results[0].targetId).toBe("act_1");
|
||||
});
|
||||
|
||||
it("queries with matchAny (OR logic)", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-query", {
|
||||
matchAny: ["priority:high", "priority:low"],
|
||||
})) as { success: boolean; results: Array<{ targetId: string }> };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.results.length).toBe(3);
|
||||
});
|
||||
|
||||
it("queries with both matchAll and matchAny", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-query", {
|
||||
matchAll: ["status:active"],
|
||||
matchAny: ["priority:high"],
|
||||
})) as { success: boolean; results: Array<{ targetId: string; matchedFacets: string[] }> };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.results.length).toBe(1);
|
||||
expect(result.results[0].targetId).toBe("act_1");
|
||||
expect(result.results[0].matchedFacets).toContain("status:active");
|
||||
expect(result.results[0].matchedFacets).toContain("priority:high");
|
||||
});
|
||||
|
||||
it("returns error when neither matchAll nor matchAny provided", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-query", {})) as {
|
||||
success: boolean;
|
||||
error: string;
|
||||
};
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain("at least one of matchAll or matchAny is required");
|
||||
});
|
||||
|
||||
it("filters by targetType", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-query", {
|
||||
matchAny: ["priority:high"],
|
||||
targetType: "memory",
|
||||
})) as { success: boolean; results: Array<{ targetId: string; targetType: string }> };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.results.length).toBe(1);
|
||||
expect(result.results[0].targetId).toBe("mem_1");
|
||||
expect(result.results[0].targetType).toBe("memory");
|
||||
});
|
||||
|
||||
it("respects limit", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-query", {
|
||||
matchAny: ["priority:high", "priority:low"],
|
||||
limit: 1,
|
||||
})) as { success: boolean; results: Array<{ targetId: string }> };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.results.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("mem::facet-get", () => {
|
||||
it("returns facets grouped by dimension", async () => {
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_1",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_1",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "urgent",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_1",
|
||||
targetType: "action",
|
||||
dimension: "status",
|
||||
value: "active",
|
||||
});
|
||||
|
||||
const result = (await sdk.trigger("mem::facet-get", {
|
||||
targetId: "act_1",
|
||||
})) as { success: boolean; dimensions: Array<{ dimension: string; values: string[] }> };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.dimensions.length).toBe(2);
|
||||
|
||||
const priorityDim = result.dimensions.find((d) => d.dimension === "priority");
|
||||
expect(priorityDim).toBeDefined();
|
||||
expect(priorityDim!.values).toEqual(["high", "urgent"]);
|
||||
|
||||
const statusDim = result.dimensions.find((d) => d.dimension === "status");
|
||||
expect(statusDim).toBeDefined();
|
||||
expect(statusDim!.values).toEqual(["active"]);
|
||||
});
|
||||
|
||||
it("returns empty dimensions for target with no facets", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-get", {
|
||||
targetId: "nonexistent",
|
||||
})) as { success: boolean; dimensions: Array<{ dimension: string; values: string[] }> };
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.dimensions).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("mem::facet-stats", () => {
|
||||
beforeEach(async () => {
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_1",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_2",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_3",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "low",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "mem_1",
|
||||
targetType: "memory",
|
||||
dimension: "category",
|
||||
value: "bugfix",
|
||||
});
|
||||
});
|
||||
|
||||
it("returns dimensions with value counts", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-stats", {})) as {
|
||||
success: boolean;
|
||||
dimensions: Array<{
|
||||
dimension: string;
|
||||
values: Array<{ value: string; count: number }>;
|
||||
}>;
|
||||
totalFacets: number;
|
||||
};
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.totalFacets).toBe(4);
|
||||
expect(result.dimensions.length).toBe(2);
|
||||
|
||||
const priorityDim = result.dimensions.find((d) => d.dimension === "priority");
|
||||
expect(priorityDim).toBeDefined();
|
||||
const highVal = priorityDim!.values.find((v) => v.value === "high");
|
||||
expect(highVal!.count).toBe(2);
|
||||
const lowVal = priorityDim!.values.find((v) => v.value === "low");
|
||||
expect(lowVal!.count).toBe(1);
|
||||
});
|
||||
|
||||
it("filters by targetType", async () => {
|
||||
const result = (await sdk.trigger("mem::facet-stats", {
|
||||
targetType: "memory",
|
||||
})) as {
|
||||
success: boolean;
|
||||
dimensions: Array<{
|
||||
dimension: string;
|
||||
values: Array<{ value: string; count: number }>;
|
||||
}>;
|
||||
totalFacets: number;
|
||||
};
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.totalFacets).toBe(1);
|
||||
expect(result.dimensions.length).toBe(1);
|
||||
expect(result.dimensions[0].dimension).toBe("category");
|
||||
expect(result.dimensions[0].values[0].value).toBe("bugfix");
|
||||
expect(result.dimensions[0].values[0].count).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("mem::facet-dimensions", () => {
|
||||
it("returns unique dimension names with counts", async () => {
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_1",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "high",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_2",
|
||||
targetType: "action",
|
||||
dimension: "priority",
|
||||
value: "low",
|
||||
});
|
||||
await sdk.trigger("mem::facet-tag", {
|
||||
targetId: "act_1",
|
||||
targetType: "action",
|
||||
dimension: "status",
|
||||
value: "active",
|
||||
});
|
||||
|
||||
const result = (await sdk.trigger("mem::facet-dimensions", {})) as {
|
||||
success: boolean;
|
||||
dimensions: Array<{ dimension: string; count: number }>;
|
||||
};
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.dimensions.length).toBe(2);
|
||||
|
||||
const priorityDim = result.dimensions.find((d) => d.dimension === "priority");
|
||||
expect(priorityDim).toBeDefined();
|
||||
expect(priorityDim!.count).toBe(2);
|
||||
|
||||
const statusDim = result.dimensions.find((d) => d.dimension === "status");
|
||||
expect(statusDim).toBeDefined();
|
||||
expect(statusDim!.count).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user