Files
fusion/packages/engine/src/__tests__/fallback-model-observer.test.ts
gsxdsm cb57093d03 refactor: domain folder layout (types, API, core, engine) (#2398)
## Summary

Wave 17 organizes Fusion into **domain folders** (stacks on #2397).

### Layout
- **core/types/** — board, task, agents, settings, merge, workflow,
mesh, …
- **core/src/** — agents, ai, async-stores, workflows, tasks, config,
db, …
- **dashboard/app/api/** — client, tasks, agents, git, missions,
planning, …
- **engine/src/** — agents, auth, execution, merge, missions, overseer,
worktree, …

Root keepers retained for large entrypoints (`store.ts`, `executor.ts`,
`merger.ts`, …).

Public barrels (`@fusion/core`, `@fusion/engine`, `app/api.ts` → legacy)
stay stable.

## Test plan
- [x] `@fusion/core` typecheck
- [x] `@fusion/engine` typecheck (pre-existing playwright-core noise
only)
- [ ] CI merge gate

**Stack:** #2394 → #2397 → **this PR**
2026-08-03 00:20:53 -07:00

118 lines
3.8 KiB
TypeScript

import { describe, expect, it, vi, beforeEach } from "vitest";
import { createFallbackModelObserver } from "../auth/fallback-model-observer.js";
import { notifyFallbackUsed } from "../util/notifier.js";
vi.mock("../util/notifier.js", () => ({
notifyFallbackUsed: vi.fn().mockResolvedValue(undefined),
}));
describe("createFallbackModelObserver", () => {
beforeEach(() => {
vi.clearAllMocks();
});
it("logs fallback activity, appends agent log, and dispatches a notification", async () => {
const store = {
logEntry: vi.fn().mockResolvedValue(undefined),
appendAgentLog: vi.fn().mockResolvedValue(undefined),
};
const observer = createFallbackModelObserver({
agent: "Executor Agent",
label: "executor",
store,
taskId: "FN-123",
taskTitle: "Fix Codex auth",
});
await observer({
primaryModel: "openai-codex/gpt-5.3-codex",
fallbackModel: "zai/glm-5.1",
triggerPoint: "prompt-time",
failureCategory: "authentication",
timestamp: "2026-05-03T22:00:00.000Z",
});
const expectedMessage =
"[fallback] executor switched from openai-codex/gpt-5.3-codex to zai/glm-5.1 (prompt-time; primary provider authentication failed)";
expect(store.logEntry).toHaveBeenCalledWith("FN-123", expectedMessage);
expect(store.appendAgentLog).toHaveBeenCalledWith(
"FN-123",
expectedMessage,
"status",
undefined,
"Executor Agent",
);
expect(notifyFallbackUsed).toHaveBeenCalledWith({
primaryModel: "openai-codex/gpt-5.3-codex",
fallbackModel: "zai/glm-5.1",
triggerPoint: "prompt-time",
taskId: "FN-123",
taskTitle: "Fix Codex auth",
timestamp: "2026-05-03T22:00:00.000Z",
});
});
it("writes fallback events as non-empty rows with readable delimiters", async () => {
const store = {
logEntry: vi.fn().mockResolvedValue(undefined),
appendAgentLog: vi.fn().mockResolvedValue(undefined),
};
const observer = createFallbackModelObserver({
agent: "triage",
label: "triage",
store,
taskId: "FN-7437",
});
await observer({
primaryModel: "openai/gpt-4o",
fallbackModel: "anthropic/claude-3-5-haiku-20241022",
triggerPoint: "prompt-time",
});
await observer({
primaryModel: "openai/gpt-4o",
fallbackModel: "anthropic/claude-3-5-haiku-20241022",
triggerPoint: "prompt-time",
});
const rows = store.appendAgentLog.mock.calls.map((call) => call[1]);
expect(rows).toEqual([
"[fallback] triage switched from openai/gpt-4o to anthropic/claude-3-5-haiku-20241022 (prompt-time)",
"[fallback] triage switched from openai/gpt-4o to anthropic/claude-3-5-haiku-20241022 (prompt-time)",
]);
expect(rows.every((row) => row.trim() === row && row.includes(" switched from ") && row.includes(" to "))).toBe(true);
});
it("swallows logging failures and still dispatches a notification", async () => {
const store = {
logEntry: vi.fn().mockRejectedValue(new Error("log failed")),
appendAgentLog: vi.fn().mockRejectedValue(new Error("append failed")),
};
const observer = createFallbackModelObserver({
agent: "Merger Agent",
label: "merge verification",
store,
});
await expect(observer({
primaryModel: "openai-codex/gpt-5.3-codex",
fallbackModel: "zai/glm-5.1",
triggerPoint: "session-creation",
taskId: "FN-456",
taskTitle: "Merge verification",
})).resolves.toBeUndefined();
expect(notifyFallbackUsed).toHaveBeenCalledWith({
primaryModel: "openai-codex/gpt-5.3-codex",
fallbackModel: "zai/glm-5.1",
triggerPoint: "session-creation",
taskId: "FN-456",
taskTitle: "Merge verification",
timestamp: undefined,
});
});
});