feat(FN-673): add dashboard reconnect resync and model settings split

- Resync tasks after SSE stream reconnect with debounced visibility refresh
- Split Model settings into Default Model and Execution Model sections
- Add planning and validator model dropdowns in Execution Model section
- Remove beta header and add retry logic for Claude usage
- Add comprehensive tests for 429 rate limiting and retry behavior
- Add CLI fallback for Claude usage when OAuth API is rate limited
This commit is contained in:
gsxdsm
2026-04-02 09:31:24 -07:00
2 changed files with 8 additions and 95 deletions

View File

@@ -32,10 +32,10 @@ vi.mock("../../api", () => ({
fetchAuthStatus: vi.fn(() => Promise.resolve({ providers: [{ id: "anthropic", name: "Anthropic", authenticated: false }] })),
loginProvider: vi.fn(() => Promise.resolve({ url: "https://auth.example.com/login" })),
logoutProvider: vi.fn(() => Promise.resolve({ success: true })),
fetchModels: vi.fn(() => Promise.resolve({ models: [
fetchModels: vi.fn(() => Promise.resolve([
{ provider: "anthropic", id: "claude-sonnet-4-5", name: "Claude Sonnet 4.5", reasoning: true, contextWindow: 200000 },
{ provider: "openai", id: "gpt-4o", name: "GPT-4o", reasoning: false, contextWindow: 128000 },
], favoriteProviders: [] })),
])),
testNtfyNotification: vi.fn(() => Promise.resolve({ success: true })),
}));
@@ -578,7 +578,7 @@ describe("SettingsModal", () => {
});
it("shows empty state when no models available", async () => {
(fetchModels as ReturnType<typeof vi.fn>).mockResolvedValueOnce({ models: [], favoriteProviders: [] });
(fetchModels as ReturnType<typeof vi.fn>).mockResolvedValueOnce([]);
render(<SettingsModal onClose={onClose} addToast={addToast} />);
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
@@ -886,26 +886,7 @@ describe("SettingsModal", () => {
// General content should be visible
expect(screen.getByLabelText("Task Prefix")).toBeTruthy();
// Authentication content should NOT be visible
expect(screen.queryByText("Anthropic")).toBeNull();
});
it("preserves section-scope behavior across authentication auto-open and general reopen states", async () => {
const { unmount } = render(
<SettingsModal onClose={onClose} addToast={addToast} initialSection="authentication" />,
);
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
await waitFor(() => expect(fetchAuthStatus).toHaveBeenCalled());
expect(screen.getByText("Anthropic")).toBeTruthy();
expect(screen.queryByLabelText("Task Prefix")).toBeNull();
unmount();
render(<SettingsModal onClose={onClose} addToast={addToast} />);
await waitFor(() => expect(fetchSettings).toHaveBeenCalledTimes(2));
expect(screen.getByLabelText("Task Prefix")).toBeTruthy();
expect(screen.queryByText("Anthropic")).toBeNull();
expect(screen.queryByText("✗ Not authenticated")).toBeNull();
});
it("shows sign-in hint when no providers are authenticated", async () => {