aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorFuwn <[email protected]>2026-02-03 22:04:13 -0800
committerFuwn <[email protected]>2026-02-03 22:04:13 -0800
commit3119964df660e9fc58c5003793711b05f217e01a (patch)
tree73738aad2484a7dcea2c2bdf34ea29ef67ed142f /packages
parenttest(mcp): Add comprehensive server tests (diff)
downloadarchived-imemio-3119964df660e9fc58c5003793711b05f217e01a.tar.xz
archived-imemio-3119964df660e9fc58c5003793711b05f217e01a.zip
test(sdk): Add Supabase store integration tests
Diffstat (limited to 'packages')
-rw-r--r--packages/sdk/src/supabase-store.test.ts825
1 files changed, 825 insertions, 0 deletions
diff --git a/packages/sdk/src/supabase-store.test.ts b/packages/sdk/src/supabase-store.test.ts
new file mode 100644
index 0000000..32e1add
--- /dev/null
+++ b/packages/sdk/src/supabase-store.test.ts
@@ -0,0 +1,825 @@
+import { describe, it, expect, beforeEach, vi } from "vitest";
+import type { SupabaseClient } from "@supabase/supabase-js";
+import { SupabaseStore } from "./supabase-store.js";
+import type { Tag } from "./types.js";
+
+function createMockSupabaseClient() {
+ const mockSingle = vi.fn();
+ const mockSelect = vi.fn(() => ({ single: mockSingle }));
+ const mockEq = vi.fn(() => ({
+ eq: mockEq,
+ select: mockSelect,
+ single: mockSingle,
+ }));
+ const mockContains = vi.fn(() => ({ data: [], error: null }));
+ const mockDelete = vi.fn(() => ({ eq: mockEq }));
+ const mockUpdate = vi.fn(() => ({ eq: mockEq }));
+ const mockInsert = vi.fn(() => ({ select: mockSelect }));
+ const mockFrom = vi.fn(() => ({
+ insert: mockInsert,
+ select: mockSelect,
+ update: mockUpdate,
+ delete: mockDelete,
+ eq: mockEq,
+ contains: mockContains,
+ }));
+ const mockRpc = vi.fn();
+
+ return {
+ from: mockFrom,
+ rpc: mockRpc,
+ _mocks: {
+ from: mockFrom,
+ insert: mockInsert,
+ select: mockSelect,
+ update: mockUpdate,
+ delete: mockDelete,
+ eq: mockEq,
+ contains: mockContains,
+ single: mockSingle,
+ rpc: mockRpc,
+ },
+ };
+}
+
+describe("SupabaseStore", () => {
+ const testUserId = "user-123";
+ let mockClient: ReturnType<typeof createMockSupabaseClient>;
+ let store: SupabaseStore;
+
+ beforeEach(() => {
+ vi.clearAllMocks();
+ mockClient = createMockSupabaseClient();
+ store = new SupabaseStore(
+ mockClient as unknown as SupabaseClient,
+ testUserId,
+ );
+ });
+ describe("create", () => {
+ it("builds correct insert query with required fields", async () => {
+ const mockMemoryRow = {
+ id: "memory-1",
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: null,
+ content: "Test content",
+ tags: [],
+ metadata: {},
+ embedding: null,
+ embedding_dimensions: null,
+ created_at: "2024-01-01T00:00:00Z",
+ updated_at: "2024-01-01T00:00:00Z",
+ };
+
+ mockClient._mocks.single.mockResolvedValue({
+ data: mockMemoryRow,
+ error: null,
+ });
+ await store.create({
+ content: "Test content",
+ projectId: "project-1",
+ });
+ expect(mockClient.from).toHaveBeenCalledWith("memories");
+ expect(mockClient._mocks.insert).toHaveBeenCalledWith({
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: null,
+ content: "Test content",
+ tags: [],
+ metadata: {},
+ });
+ });
+ it("builds correct insert query with all optional fields", async () => {
+ const testTags: Tag[] = [{ id: "tag-1", name: "important" }];
+ const testMetadata = { source: "test" };
+ const testEmbedding = [0.1, 0.2, 0.3];
+ const mockMemoryRow = {
+ id: "memory-1",
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: "folder-1",
+ content: "Test content",
+ tags: testTags,
+ metadata: testMetadata,
+ embedding: testEmbedding,
+ embedding_dimensions: 3,
+ created_at: "2024-01-01T00:00:00Z",
+ updated_at: "2024-01-01T00:00:00Z",
+ };
+
+ mockClient._mocks.single.mockResolvedValue({
+ data: mockMemoryRow,
+ error: null,
+ });
+ await store.create({
+ content: "Test content",
+ projectId: "project-1",
+ folderId: "folder-1",
+ tags: testTags,
+ metadata: testMetadata,
+ embedding: testEmbedding,
+ });
+ expect(mockClient._mocks.insert).toHaveBeenCalledWith({
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: "folder-1",
+ content: "Test content",
+ tags: testTags,
+ metadata: testMetadata,
+ embedding: testEmbedding,
+ embedding_dimensions: 3,
+ });
+ });
+ it("uses custom embedding dimensions when provided", async () => {
+ const testEmbedding = [0.1, 0.2, 0.3];
+ const mockMemoryRow = {
+ id: "memory-1",
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: null,
+ content: "Test content",
+ tags: [],
+ metadata: {},
+ embedding: testEmbedding,
+ embedding_dimensions: 768,
+ created_at: "2024-01-01T00:00:00Z",
+ updated_at: "2024-01-01T00:00:00Z",
+ };
+
+ mockClient._mocks.single.mockResolvedValue({
+ data: mockMemoryRow,
+ error: null,
+ });
+ await store.create({
+ content: "Test content",
+ projectId: "project-1",
+ embedding: testEmbedding,
+ embeddingDimensions: 768,
+ });
+ expect(mockClient._mocks.insert).toHaveBeenCalledWith({
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: null,
+ content: "Test content",
+ tags: [],
+ metadata: {},
+ embedding: testEmbedding,
+ embedding_dimensions: 768,
+ });
+ });
+ it("returns the created memory with correct field mapping", async () => {
+ const mockMemoryRow = {
+ id: "memory-1",
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: "folder-1",
+ content: "Test content",
+ tags: [{ id: "tag-1", name: "important" }],
+ metadata: { source: "test" },
+ embedding: null,
+ embedding_dimensions: null,
+ created_at: "2024-01-01T00:00:00Z",
+ updated_at: "2024-01-02T00:00:00Z",
+ };
+
+ mockClient._mocks.single.mockResolvedValue({
+ data: mockMemoryRow,
+ error: null,
+ });
+
+ const result = await store.create({
+ content: "Test content",
+ projectId: "project-1",
+ });
+
+ expect(result.id).toBe("memory-1");
+ expect(result.content).toBe("Test content");
+ expect(result.projectId).toBe("project-1");
+ expect(result.folderId).toBe("folder-1");
+ expect(result.tags).toEqual([{ id: "tag-1", name: "important" }]);
+ expect(result.metadata).toEqual({ source: "test" });
+ expect(result.createdAt).toBeInstanceOf(Date);
+ expect(result.updatedAt).toBeInstanceOf(Date);
+ });
+ it("throws error when insert fails", async () => {
+ mockClient._mocks.single.mockResolvedValue({
+ data: null,
+ error: { message: "Database error" },
+ });
+ await expect(
+ store.create({
+ content: "Test content",
+ projectId: "project-1",
+ }),
+ ).rejects.toThrow("Failed to create memory: Database error");
+ });
+ });
+ describe("read", () => {
+ it("queries by id and user_id", async () => {
+ const mockMemoryRow = {
+ id: "memory-1",
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: null,
+ content: "Test content",
+ tags: [],
+ metadata: {},
+ embedding: null,
+ embedding_dimensions: null,
+ created_at: "2024-01-01T00:00:00Z",
+ updated_at: "2024-01-01T00:00:00Z",
+ };
+ const mockSingleResult = vi.fn().mockResolvedValue({
+ data: mockMemoryRow,
+ error: null,
+ });
+ const mockSelectResult = vi.fn().mockReturnValue({
+ single: mockSingleResult,
+ });
+ const mockEqUserId = vi.fn().mockReturnValue({
+ single: mockSingleResult,
+ select: mockSelectResult,
+ });
+ const mockEqId = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+ const mockSelect = vi.fn().mockReturnValue({
+ eq: mockEqId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ select: mockSelect,
+ });
+
+ const result = await store.read("memory-1");
+
+ expect(mockClient.from).toHaveBeenCalledWith("memories");
+ expect(mockSelect).toHaveBeenCalled();
+ expect(mockEqId).toHaveBeenCalledWith("id", "memory-1");
+ expect(mockEqUserId).toHaveBeenCalledWith("user_id", testUserId);
+ expect(result.success).toBe(true);
+
+ if (result.success) {
+ expect(result.value.id).toBe("memory-1");
+ }
+ });
+ it("returns failure when memory not found", async () => {
+ const mockSingleResult = vi.fn().mockResolvedValue({
+ data: null,
+ error: { message: "Not found" },
+ });
+ const mockEqUserId = vi.fn().mockReturnValue({
+ single: mockSingleResult,
+ });
+ const mockEqId = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+ const mockSelect = vi.fn().mockReturnValue({
+ eq: mockEqId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ select: mockSelect,
+ });
+
+ const result = await store.read("non-existent-id");
+
+ expect(result.success).toBe(false);
+
+ if (!result.success) {
+ expect(result.error.type).toBe("MEMORY_NOT_FOUND");
+ expect(result.error.memoryId).toBe("non-existent-id");
+ }
+ });
+ });
+ describe("update", () => {
+ it("builds correct update query with content", async () => {
+ const mockMemoryRow = {
+ id: "memory-1",
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: null,
+ content: "Updated content",
+ tags: [],
+ metadata: {},
+ embedding: null,
+ embedding_dimensions: null,
+ created_at: "2024-01-01T00:00:00Z",
+ updated_at: "2024-01-02T00:00:00Z",
+ };
+ const mockSingleResult = vi.fn().mockResolvedValue({
+ data: mockMemoryRow,
+ error: null,
+ });
+ const mockSelectResult = vi.fn().mockReturnValue({
+ single: mockSingleResult,
+ });
+ const mockEqUserId = vi.fn().mockReturnValue({
+ select: mockSelectResult,
+ });
+ const mockEqId = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+ const mockUpdate = vi.fn().mockReturnValue({
+ eq: mockEqId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ update: mockUpdate,
+ });
+ await store.update("memory-1", {
+ content: "Updated content",
+ });
+ expect(mockClient.from).toHaveBeenCalledWith("memories");
+ expect(mockUpdate).toHaveBeenCalledWith({ content: "Updated content" });
+ expect(mockEqId).toHaveBeenCalledWith("id", "memory-1");
+ expect(mockEqUserId).toHaveBeenCalledWith("user_id", testUserId);
+ });
+ it("builds correct update query with all fields", async () => {
+ const testTags: Tag[] = [{ id: "tag-1", name: "updated" }];
+ const testMetadata = { updated: true };
+ const testEmbedding = [0.4, 0.5, 0.6];
+ const mockMemoryRow = {
+ id: "memory-1",
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: "folder-2",
+ content: "Updated content",
+ tags: testTags,
+ metadata: testMetadata,
+ embedding: testEmbedding,
+ embedding_dimensions: 3,
+ created_at: "2024-01-01T00:00:00Z",
+ updated_at: "2024-01-02T00:00:00Z",
+ };
+ const mockSingleResult = vi.fn().mockResolvedValue({
+ data: mockMemoryRow,
+ error: null,
+ });
+ const mockSelectResult = vi.fn().mockReturnValue({
+ single: mockSingleResult,
+ });
+ const mockEqUserId = vi.fn().mockReturnValue({
+ select: mockSelectResult,
+ });
+ const mockEqId = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+ const mockUpdate = vi.fn().mockReturnValue({
+ eq: mockEqId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ update: mockUpdate,
+ });
+ await store.update("memory-1", {
+ content: "Updated content",
+ folderId: "folder-2",
+ tags: testTags,
+ metadata: testMetadata,
+ embedding: testEmbedding,
+ embeddingDimensions: 768,
+ });
+ expect(mockUpdate).toHaveBeenCalledWith({
+ content: "Updated content",
+ folder_id: "folder-2",
+ tags: testTags,
+ metadata: testMetadata,
+ embedding: testEmbedding,
+ embedding_dimensions: 768,
+ });
+ });
+ it("returns failure when memory not found", async () => {
+ const mockSingleResult = vi.fn().mockResolvedValue({
+ data: null,
+ error: { message: "Not found" },
+ });
+ const mockSelectResult = vi.fn().mockReturnValue({
+ single: mockSingleResult,
+ });
+ const mockEqUserId = vi.fn().mockReturnValue({
+ select: mockSelectResult,
+ });
+ const mockEqId = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+ const mockUpdate = vi.fn().mockReturnValue({
+ eq: mockEqId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ update: mockUpdate,
+ });
+
+ const result = await store.update("non-existent-id", {
+ content: "Updated content",
+ });
+
+ expect(result.success).toBe(false);
+
+ if (!result.success) {
+ expect(result.error.type).toBe("MEMORY_NOT_FOUND");
+ expect(result.error.memoryId).toBe("non-existent-id");
+ }
+ });
+ });
+ describe("delete", () => {
+ it("builds correct delete query", async () => {
+ const mockEqUserId = vi.fn().mockResolvedValue({
+ error: null,
+ count: 1,
+ });
+ const mockEqId = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+ const mockDelete = vi.fn().mockReturnValue({
+ eq: mockEqId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ delete: mockDelete,
+ });
+
+ const result = await store.delete("memory-1");
+
+ expect(mockClient.from).toHaveBeenCalledWith("memories");
+ expect(mockDelete).toHaveBeenCalledWith({ count: "exact" });
+ expect(mockEqId).toHaveBeenCalledWith("id", "memory-1");
+ expect(mockEqUserId).toHaveBeenCalledWith("user_id", testUserId);
+ expect(result.success).toBe(true);
+ });
+ it("returns failure when memory not found", async () => {
+ const mockEqUserId = vi.fn().mockResolvedValue({
+ error: null,
+ count: 0,
+ });
+ const mockEqId = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+ const mockDelete = vi.fn().mockReturnValue({
+ eq: mockEqId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ delete: mockDelete,
+ });
+
+ const result = await store.delete("non-existent-id");
+
+ expect(result.success).toBe(false);
+
+ if (!result.success) {
+ expect(result.error.type).toBe("MEMORY_NOT_FOUND");
+ expect(result.error.memoryId).toBe("non-existent-id");
+ }
+ });
+ it("returns failure when delete encounters error", async () => {
+ const mockEqUserId = vi.fn().mockResolvedValue({
+ error: { message: "Database error" },
+ count: null,
+ });
+ const mockEqId = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+ const mockDelete = vi.fn().mockReturnValue({
+ eq: mockEqId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ delete: mockDelete,
+ });
+
+ const result = await store.delete("memory-1");
+
+ expect(result.success).toBe(false);
+
+ if (!result.success) {
+ expect(result.error.type).toBe("MEMORY_NOT_FOUND");
+ }
+ });
+ });
+ describe("list", () => {
+ it("queries with user_id filter only when no filter provided", async () => {
+ const mockQueryResult = Promise.resolve({ data: [], error: null });
+ const mockEqUserId = vi.fn().mockReturnValue(mockQueryResult);
+ const mockSelect = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ select: mockSelect,
+ });
+ await store.list();
+ expect(mockClient.from).toHaveBeenCalledWith("memories");
+ expect(mockSelect).toHaveBeenCalled();
+ expect(mockEqUserId).toHaveBeenCalledWith("user_id", testUserId);
+ });
+ it("adds projectId filter when provided", async () => {
+ const mockQueryResult = Promise.resolve({ data: [], error: null });
+ const mockEqProjectId = vi.fn().mockReturnValue(mockQueryResult);
+ const mockEqUserId = vi.fn().mockReturnValue({
+ eq: mockEqProjectId,
+ });
+ const mockSelect = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ select: mockSelect,
+ });
+ await store.list({ projectId: "project-1" });
+ expect(mockEqProjectId).toHaveBeenCalledWith("project_id", "project-1");
+ });
+ it("adds folderId filter when provided", async () => {
+ const mockQueryResult = Promise.resolve({ data: [], error: null });
+ const mockEqFolderId = vi.fn().mockReturnValue(mockQueryResult);
+ const mockEqUserId = vi.fn().mockReturnValue({
+ eq: mockEqFolderId,
+ });
+ const mockSelect = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ select: mockSelect,
+ });
+ await store.list({ folderId: "folder-1" });
+ expect(mockEqFolderId).toHaveBeenCalledWith("folder_id", "folder-1");
+ });
+ it("adds tags filter with contains when provided", async () => {
+ const mockQueryResult = Promise.resolve({ data: [], error: null });
+ const mockContains = vi.fn().mockReturnValue(mockQueryResult);
+ const mockEqUserId = vi.fn().mockReturnValue({
+ contains: mockContains,
+ });
+ const mockSelect = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ select: mockSelect,
+ });
+ await store.list({ tags: ["tag-1", "tag-2"] });
+ expect(mockContains).toHaveBeenCalledWith("tags", [
+ { id: "tag-1" },
+ { id: "tag-2" },
+ ]);
+ });
+ it("applies multiple filters together", async () => {
+ const mockQueryResult = Promise.resolve({ data: [], error: null });
+ const mockContains = vi.fn().mockReturnValue(mockQueryResult);
+ const mockEqFolderId = vi.fn().mockReturnValue({
+ contains: mockContains,
+ });
+ const mockEqProjectId = vi.fn().mockReturnValue({
+ eq: mockEqFolderId,
+ });
+ const mockEqUserId = vi.fn().mockReturnValue({
+ eq: mockEqProjectId,
+ });
+ const mockSelect = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ select: mockSelect,
+ });
+ await store.list({
+ projectId: "project-1",
+ folderId: "folder-1",
+ tags: ["tag-1"],
+ });
+ expect(mockEqProjectId).toHaveBeenCalledWith("project_id", "project-1");
+ expect(mockEqFolderId).toHaveBeenCalledWith("folder_id", "folder-1");
+ expect(mockContains).toHaveBeenCalledWith("tags", [{ id: "tag-1" }]);
+ });
+ it("returns mapped memories from query result", async () => {
+ const mockMemoryRows = [
+ {
+ id: "memory-1",
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: null,
+ content: "Content 1",
+ tags: [],
+ metadata: {},
+ embedding: null,
+ embedding_dimensions: null,
+ created_at: "2024-01-01T00:00:00Z",
+ updated_at: "2024-01-01T00:00:00Z",
+ },
+ {
+ id: "memory-2",
+ user_id: testUserId,
+ project_id: "project-1",
+ folder_id: "folder-1",
+ content: "Content 2",
+ tags: [{ id: "tag-1", name: "important" }],
+ metadata: { source: "test" },
+ embedding: null,
+ embedding_dimensions: null,
+ created_at: "2024-01-02T00:00:00Z",
+ updated_at: "2024-01-02T00:00:00Z",
+ },
+ ];
+ const mockQueryResult = Promise.resolve({
+ data: mockMemoryRows,
+ error: null,
+ });
+ const mockEqUserId = vi.fn().mockReturnValue(mockQueryResult);
+ const mockSelect = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ select: mockSelect,
+ });
+
+ const result = await store.list();
+
+ expect(result).toHaveLength(2);
+ expect(result[0]?.id).toBe("memory-1");
+ expect(result[0]?.projectId).toBe("project-1");
+ expect(result[1]?.id).toBe("memory-2");
+ expect(result[1]?.folderId).toBe("folder-1");
+ expect(result[1]?.tags).toEqual([{ id: "tag-1", name: "important" }]);
+ });
+ it("throws error when list fails", async () => {
+ const mockQueryResult = Promise.resolve({
+ data: null,
+ error: { message: "Database error" },
+ });
+ const mockEqUserId = vi.fn().mockReturnValue(mockQueryResult);
+ const mockSelect = vi.fn().mockReturnValue({
+ eq: mockEqUserId,
+ });
+
+ mockClient.from = vi.fn().mockReturnValue({
+ select: mockSelect,
+ });
+ await expect(store.list()).rejects.toThrow(
+ "Failed to list memories: Database error",
+ );
+ });
+ });
+ describe("search", () => {
+ it("calls rpc with correct parameters", async () => {
+ const testEmbedding = [0.1, 0.2, 0.3];
+
+ mockClient.rpc.mockResolvedValue({
+ data: [],
+ error: null,
+ });
+ await store.search(testEmbedding);
+ expect(mockClient.rpc).toHaveBeenCalledWith("search_memories", {
+ query_embedding: testEmbedding,
+ match_threshold: 0.7,
+ match_count: 10,
+ filter_project_id: null,
+ filter_folder_id: null,
+ });
+ });
+ it("passes custom threshold when provided", async () => {
+ const testEmbedding = [0.1, 0.2, 0.3];
+
+ mockClient.rpc.mockResolvedValue({
+ data: [],
+ error: null,
+ });
+ await store.search(testEmbedding, { threshold: 0.8 });
+ expect(mockClient.rpc).toHaveBeenCalledWith("search_memories", {
+ query_embedding: testEmbedding,
+ match_threshold: 0.8,
+ match_count: 10,
+ filter_project_id: null,
+ filter_folder_id: null,
+ });
+ });
+ it("passes custom limit when provided", async () => {
+ const testEmbedding = [0.1, 0.2, 0.3];
+
+ mockClient.rpc.mockResolvedValue({
+ data: [],
+ error: null,
+ });
+ await store.search(testEmbedding, { limit: 5 });
+ expect(mockClient.rpc).toHaveBeenCalledWith("search_memories", {
+ query_embedding: testEmbedding,
+ match_threshold: 0.7,
+ match_count: 5,
+ filter_project_id: null,
+ filter_folder_id: null,
+ });
+ });
+ it("passes projectId filter when provided", async () => {
+ const testEmbedding = [0.1, 0.2, 0.3];
+
+ mockClient.rpc.mockResolvedValue({
+ data: [],
+ error: null,
+ });
+ await store.search(testEmbedding, { projectId: "project-1" });
+ expect(mockClient.rpc).toHaveBeenCalledWith("search_memories", {
+ query_embedding: testEmbedding,
+ match_threshold: 0.7,
+ match_count: 10,
+ filter_project_id: "project-1",
+ filter_folder_id: null,
+ });
+ });
+ it("passes folderId filter when provided", async () => {
+ const testEmbedding = [0.1, 0.2, 0.3];
+
+ mockClient.rpc.mockResolvedValue({
+ data: [],
+ error: null,
+ });
+ await store.search(testEmbedding, { folderId: "folder-1" });
+ expect(mockClient.rpc).toHaveBeenCalledWith("search_memories", {
+ query_embedding: testEmbedding,
+ match_threshold: 0.7,
+ match_count: 10,
+ filter_project_id: null,
+ filter_folder_id: "folder-1",
+ });
+ });
+ it("passes all options when provided", async () => {
+ const testEmbedding = [0.1, 0.2, 0.3];
+
+ mockClient.rpc.mockResolvedValue({
+ data: [],
+ error: null,
+ });
+ await store.search(testEmbedding, {
+ threshold: 0.9,
+ limit: 20,
+ projectId: "project-1",
+ folderId: "folder-1",
+ });
+ expect(mockClient.rpc).toHaveBeenCalledWith("search_memories", {
+ query_embedding: testEmbedding,
+ match_threshold: 0.9,
+ match_count: 20,
+ filter_project_id: "project-1",
+ filter_folder_id: "folder-1",
+ });
+ });
+ it("returns mapped search results", async () => {
+ const testEmbedding = [0.1, 0.2, 0.3];
+ const mockSearchResultRows = [
+ {
+ id: "memory-1",
+ content: "Test content 1",
+ project_id: "project-1",
+ folder_id: null,
+ tags: [],
+ metadata: {},
+ similarity: 0.95,
+ created_at: "2024-01-01T00:00:00Z",
+ updated_at: "2024-01-01T00:00:00Z",
+ },
+ {
+ id: "memory-2",
+ content: "Test content 2",
+ project_id: "project-1",
+ folder_id: "folder-1",
+ tags: [{ id: "tag-1", name: "important" }],
+ metadata: { source: "test" },
+ similarity: 0.85,
+ created_at: "2024-01-02T00:00:00Z",
+ updated_at: "2024-01-02T00:00:00Z",
+ },
+ ];
+
+ mockClient.rpc.mockResolvedValue({
+ data: mockSearchResultRows,
+ error: null,
+ });
+
+ const result = await store.search(testEmbedding);
+
+ expect(result).toHaveLength(2);
+ expect(result[0]?.id).toBe("memory-1");
+ expect(result[0]?.content).toBe("Test content 1");
+ expect(result[0]?.similarity).toBe(0.95);
+ expect(result[0]?.projectId).toBe("project-1");
+ expect(result[0]?.folderId).toBeNull();
+ expect(result[0]?.createdAt).toBeInstanceOf(Date);
+ expect(result[1]?.id).toBe("memory-2");
+ expect(result[1]?.similarity).toBe(0.85);
+ expect(result[1]?.folderId).toBe("folder-1");
+ expect(result[1]?.tags).toEqual([{ id: "tag-1", name: "important" }]);
+ });
+ it("throws error when search fails", async () => {
+ const testEmbedding = [0.1, 0.2, 0.3];
+
+ mockClient.rpc.mockResolvedValue({
+ data: null,
+ error: { message: "RPC error" },
+ });
+ await expect(store.search(testEmbedding)).rejects.toThrow(
+ "Failed to search memories: RPC error",
+ );
+ });
+ });
+});