test(core): accept either fs.watch failure path on Linux and macOS

`store-activity.test.ts > "logs fs.watch setup failures and keeps polling
active"` flakes on Linux CI: it sets `tasksDir` to a non-existent path
and expects `fs.watch` to throw synchronously (routed to
`watch:fs-watch-setup`). macOS Node does throw, but Linux Node returns a
watcher that emits an async `error` event instead (routed to
`watch:fs-watch-error`). The contract this test guards is "log the
failure and keep polling alive" — not which catch arm handled it.

Match either warning message + phase, and use `vi.waitFor` so the async
Linux path isn't raced by the spy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-05-28 10:03:29 -07:00
parent f13e197371
commit 8c0a75f50e

View File

@@ -817,16 +817,32 @@ describe("TaskStore", () => {
try {
await store.watch();
const warningCall = warnSpy.mock.calls.find(
(call) => typeof call[0] === "string" && call[0].includes("[task-store] fs.watch unavailable; falling back to polling-only updates"),
);
expect(warningCall).toBeDefined();
// fs.watch on a missing path is platform-sensitive: macOS Node throws
// ENOENT synchronously (routed to the `watch:fs-watch-setup` warning),
// whereas Linux Node returns a watcher that emits an async `error`
// event (routed to the `watch:fs-watch-error` warning). Either path
// is correct — the contract this test guards is "log the failure and
// keep polling alive," not which catch arm caught it. Use vi.waitFor
// so we observe the async path on Linux without racing the spy.
const findWarning = () =>
warnSpy.mock.calls.find((call) => {
if (typeof call[0] !== "string") return false;
return (
call[0].includes("[task-store] fs.watch unavailable; falling back to polling-only updates")
|| call[0].includes("[task-store] fs.watch emitted an error; polling will continue")
);
});
await vi.waitFor(() => {
expect(findWarning()).toBeDefined();
});
const warningCall = findWarning()!;
const [, context] = warningCall as [string, Record<string, unknown>];
expect(context).toMatchObject({
phase: "watch:fs-watch-setup",
tasksDir: join(rootDir, ".fusion", "missing-tasks-dir"),
});
expect(context.phase).toMatch(/^watch:fs-watch-(setup|error)$/);
expect(typeof context.error).toBe("string");
expect(storeAny.pollInterval).not.toBeNull();
await expect(storeAny.checkForChanges()).resolves.toBeUndefined();