test(FN-2733): expand memory dreams regression coverage
- Add ProjectEngine memory dreams wiring tests for startup ordering, settings-change resync, and unrelated-setting no-op behavior - Cover degraded-mode startup and settings-update failure paths to ensure sync errors are logged without stopping the engine - Extend MemoryView Dream Now tests for success, failure toast handling, and disabled loading-state behavior
This commit is contained in:
@@ -202,7 +202,7 @@ describe("MemoryView", () => {
|
||||
expect(screen.getByRole("button", { name: "Dream Now" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("triggers dream processing and refreshes memory files", async () => {
|
||||
it("clicking Dream Now triggers processing API once and refreshes memory files", async () => {
|
||||
const triggerDreamNow = vi.fn().mockResolvedValue({});
|
||||
const reloadMemoryFiles = vi.fn().mockResolvedValue(undefined);
|
||||
const addToast = vi.fn();
|
||||
@@ -232,6 +232,52 @@ describe("MemoryView", () => {
|
||||
expect(addToast).toHaveBeenCalledWith("Dream processing completed", "success");
|
||||
});
|
||||
|
||||
it("shows error toast when Dream Now processing fails", async () => {
|
||||
const triggerDreamNow = vi.fn().mockRejectedValue(new Error("dream failed"));
|
||||
const addToast = vi.fn();
|
||||
mockUseMemoryData.mockReturnValue(
|
||||
createMemoryData({
|
||||
triggerDreamNow,
|
||||
memorySettings: {
|
||||
memoryEnabled: true,
|
||||
memoryAutoSummarizeEnabled: false,
|
||||
memoryAutoSummarizeThresholdChars: 50000,
|
||||
memoryAutoSummarizeSchedule: "0 3 * * *",
|
||||
memoryDreamsEnabled: true,
|
||||
memoryDreamsSchedule: "0 4 * * *",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
render(<MemoryView addToast={addToast} />);
|
||||
|
||||
await userEvent.click(screen.getByRole("button", { name: "Dream Now" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(addToast).toHaveBeenCalledWith("dream failed", "error");
|
||||
});
|
||||
});
|
||||
|
||||
it("shows Dreaming loading state while processing is running", () => {
|
||||
mockUseMemoryData.mockReturnValue(
|
||||
createMemoryData({
|
||||
dreamRunning: true,
|
||||
memorySettings: {
|
||||
memoryEnabled: true,
|
||||
memoryAutoSummarizeEnabled: false,
|
||||
memoryAutoSummarizeThresholdChars: 50000,
|
||||
memoryAutoSummarizeSchedule: "0 3 * * *",
|
||||
memoryDreamsEnabled: true,
|
||||
memoryDreamsSchedule: "0 4 * * *",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
render(<MemoryView addToast={vi.fn()} />);
|
||||
|
||||
const button = screen.getByRole("button", { name: /Dreaming…/i });
|
||||
expect(button).toBeDisabled();
|
||||
});
|
||||
it("hides Dream Now button when dreams are disabled", () => {
|
||||
render(<MemoryView addToast={vi.fn()} />);
|
||||
|
||||
|
||||
@@ -316,6 +316,143 @@ describe("ProjectEngine auto-summarize wiring", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("ProjectEngine memory dreams wiring", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
const mockStore = createMockStore(baseSettings);
|
||||
mocks.currentStore = mockStore.store;
|
||||
});
|
||||
|
||||
it("starts cron after memory dreams startup sync", async () => {
|
||||
const engine = createEngine();
|
||||
|
||||
await engine.start();
|
||||
|
||||
const cronRunnerStartOrder = mocks.cronRunnerStart.mock.invocationCallOrder[0];
|
||||
expect(mocks.syncMemoryDreamsAutomation).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.syncMemoryDreamsAutomation.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
cronRunnerStartOrder,
|
||||
);
|
||||
|
||||
await engine.stop();
|
||||
});
|
||||
|
||||
it("re-syncs memory dreams automation when memoryDreamsEnabled changes", async () => {
|
||||
const mockStore = createMockStore(baseSettings);
|
||||
mocks.currentStore = mockStore.store;
|
||||
const engine = createEngine();
|
||||
|
||||
await engine.start();
|
||||
mocks.syncMemoryDreamsAutomation.mockClear();
|
||||
|
||||
const previous = { ...baseSettings };
|
||||
const next = {
|
||||
...previous,
|
||||
memoryDreamsEnabled: true,
|
||||
};
|
||||
|
||||
await mockStore.emitSettingsUpdated(next, previous);
|
||||
|
||||
expect(mocks.syncMemoryDreamsAutomation).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.syncMemoryDreamsAutomation).toHaveBeenCalledWith(expect.anything(), next);
|
||||
|
||||
await engine.stop();
|
||||
});
|
||||
|
||||
it("re-syncs memory dreams automation when memoryDreamsSchedule changes", async () => {
|
||||
const mockStore = createMockStore(baseSettings);
|
||||
mocks.currentStore = mockStore.store;
|
||||
const engine = createEngine();
|
||||
|
||||
await engine.start();
|
||||
mocks.syncMemoryDreamsAutomation.mockClear();
|
||||
|
||||
const previous = { ...baseSettings };
|
||||
const next = {
|
||||
...previous,
|
||||
memoryDreamsSchedule: "0 */8 * * *",
|
||||
};
|
||||
|
||||
await mockStore.emitSettingsUpdated(next, previous);
|
||||
|
||||
expect(mocks.syncMemoryDreamsAutomation).toHaveBeenCalledTimes(1);
|
||||
|
||||
await engine.stop();
|
||||
});
|
||||
|
||||
it("does not re-sync memory dreams automation on unrelated settings changes", async () => {
|
||||
const mockStore = createMockStore(baseSettings);
|
||||
mocks.currentStore = mockStore.store;
|
||||
const engine = createEngine();
|
||||
|
||||
await engine.start();
|
||||
mocks.syncMemoryDreamsAutomation.mockClear();
|
||||
|
||||
const previous = { ...baseSettings };
|
||||
const next = {
|
||||
...previous,
|
||||
pollIntervalMs: 30_000,
|
||||
};
|
||||
|
||||
await mockStore.emitSettingsUpdated(next, previous);
|
||||
|
||||
expect(mocks.syncMemoryDreamsAutomation).not.toHaveBeenCalled();
|
||||
|
||||
await engine.stop();
|
||||
});
|
||||
|
||||
it("logs warning and continues startup when memory dreams startup sync fails", async () => {
|
||||
const warnSpy = vi.spyOn(runtimeLog, "warn").mockImplementation(() => {});
|
||||
mocks.syncMemoryDreamsAutomation.mockRejectedValueOnce(new Error("dream startup sync failed"));
|
||||
|
||||
const engine = createEngine();
|
||||
|
||||
await expect(engine.start()).resolves.toBeUndefined();
|
||||
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Memory dreams automation startup sync failed"),
|
||||
);
|
||||
expect(engine.getAutomationSubsystemHealth()).toMatchObject({
|
||||
status: "degraded",
|
||||
});
|
||||
expect(engine.getCronRunner()).toBeDefined();
|
||||
|
||||
await engine.stop();
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("catches and logs memory dreams sync failures on settings changes", async () => {
|
||||
const warnSpy = vi.spyOn(runtimeLog, "warn").mockImplementation(() => {});
|
||||
const mockStore = createMockStore(baseSettings);
|
||||
mocks.currentStore = mockStore.store;
|
||||
const engine = createEngine();
|
||||
|
||||
await engine.start();
|
||||
warnSpy.mockClear();
|
||||
mocks.syncMemoryDreamsAutomation.mockRejectedValueOnce(new Error("dream settings sync failed"));
|
||||
|
||||
await expect(
|
||||
mockStore.emitSettingsUpdated(
|
||||
{
|
||||
...baseSettings,
|
||||
memoryDreamsEnabled: true,
|
||||
},
|
||||
{
|
||||
...baseSettings,
|
||||
memoryDreamsEnabled: false,
|
||||
},
|
||||
),
|
||||
).resolves.toBeUndefined();
|
||||
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
expect.stringContaining("Failed to sync memory maintenance automation"),
|
||||
);
|
||||
|
||||
await engine.stop();
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe("ProjectEngine remote tunnel manager wiring", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
|
||||
Reference in New Issue
Block a user