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();
|
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 triggerDreamNow = vi.fn().mockResolvedValue({});
|
||||||
const reloadMemoryFiles = vi.fn().mockResolvedValue(undefined);
|
const reloadMemoryFiles = vi.fn().mockResolvedValue(undefined);
|
||||||
const addToast = vi.fn();
|
const addToast = vi.fn();
|
||||||
@@ -232,6 +232,52 @@ describe("MemoryView", () => {
|
|||||||
expect(addToast).toHaveBeenCalledWith("Dream processing completed", "success");
|
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", () => {
|
it("hides Dream Now button when dreams are disabled", () => {
|
||||||
render(<MemoryView addToast={vi.fn()} />);
|
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", () => {
|
describe("ProjectEngine remote tunnel manager wiring", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
|
|||||||
Reference in New Issue
Block a user