| import fs from "node:fs/promises"; |
| import os from "node:os"; |
| import path from "node:path"; |
|
|
| import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; |
|
|
| import { getMemorySearchManager, type MemoryIndexManager } from "./index.js"; |
|
|
| vi.mock("chokidar", () => ({ |
| default: { |
| watch: vi.fn(() => ({ |
| on: vi.fn(), |
| close: vi.fn(async () => undefined), |
| })), |
| }, |
| })); |
|
|
| vi.mock("./embeddings.js", () => { |
| return { |
| createEmbeddingProvider: async () => ({ |
| requestedProvider: "openai", |
| provider: { |
| id: "mock", |
| model: "mock-embed", |
| embedQuery: async () => [0, 0, 0], |
| embedBatch: async () => { |
| throw new Error("openai embeddings failed: 400 bad request"); |
| }, |
| }, |
| }), |
| }; |
| }); |
|
|
| describe("memory manager sync failures", () => { |
| let workspaceDir: string; |
| let indexPath: string; |
| let manager: MemoryIndexManager | null = null; |
|
|
| beforeEach(async () => { |
| vi.useFakeTimers(); |
| workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-mem-")); |
| indexPath = path.join(workspaceDir, "index.sqlite"); |
| await fs.mkdir(path.join(workspaceDir, "memory")); |
| await fs.writeFile(path.join(workspaceDir, "MEMORY.md"), "Hello"); |
| }); |
|
|
| afterEach(async () => { |
| vi.useRealTimers(); |
| if (manager) { |
| await manager.close(); |
| manager = null; |
| } |
| await fs.rm(workspaceDir, { recursive: true, force: true }); |
| }); |
|
|
| it("does not raise unhandledRejection when watch-triggered sync fails", async () => { |
| const unhandled: unknown[] = []; |
| const handler = (reason: unknown) => { |
| unhandled.push(reason); |
| }; |
| process.on("unhandledRejection", handler); |
|
|
| const cfg = { |
| agents: { |
| defaults: { |
| workspace: workspaceDir, |
| memorySearch: { |
| provider: "openai", |
| model: "mock-embed", |
| store: { path: indexPath }, |
| sync: { watch: true, watchDebounceMs: 1, onSessionStart: false, onSearch: false }, |
| }, |
| }, |
| list: [{ id: "main", default: true }], |
| }, |
| }; |
|
|
| const result = await getMemorySearchManager({ cfg, agentId: "main" }); |
| expect(result.manager).not.toBeNull(); |
| if (!result.manager) { |
| throw new Error("manager missing"); |
| } |
| manager = result.manager; |
| const syncSpy = vi.spyOn(manager, "sync"); |
|
|
| |
| (manager as unknown as { scheduleWatchSync: () => void }).scheduleWatchSync(); |
|
|
| await vi.runOnlyPendingTimersAsync(); |
| const syncPromise = syncSpy.mock.results[0]?.value as Promise<void> | undefined; |
| vi.useRealTimers(); |
| if (syncPromise) { |
| await syncPromise.catch(() => undefined); |
| } |
|
|
| process.off("unhandledRejection", handler); |
| expect(unhandled).toHaveLength(0); |
| }); |
| }); |
|
|