fix(#411): remediate frontend review findings — wire fetchWithRetry, fix error handling

- Wire fetchWithRetry into login page config fetch (was dead code)
- Remove duplicate ERROR_CODE_MESSAGES, use parseAuthError from auth-errors.ts
- Fix OAuth sign-in fire-and-forget: add .catch() with PDA error + loading reset
- Fix credential login catch: use parseAuthError for better error messages
- Add user feedback when auth config fetch fails (was silent degradation)
- Fix sign-out failure: use logAuthError and set authError state
- Enable fetchWithRetry production logging for retry visibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-16 12:33:25 -06:00
parent 7ead8b1076
commit 9696e45265
5 changed files with 89 additions and 49 deletions

View File

@@ -40,7 +40,6 @@ function mockResponse(status: number, ok?: boolean): Response {
describe("fetchWithRetry", (): void => {
const originalFetch = global.fetch;
const originalEnv = process.env.NODE_ENV;
const sleepMock = vi.mocked(sleep);
beforeEach((): void => {
@@ -52,7 +51,6 @@ describe("fetchWithRetry", (): void => {
afterEach((): void => {
vi.restoreAllMocks();
global.fetch = originalFetch;
process.env.NODE_ENV = originalEnv;
});
it("should succeed on first attempt without retrying", async (): Promise<void> => {
@@ -203,8 +201,7 @@ describe("fetchWithRetry", (): void => {
expect(recordedDelays).toEqual([1000, 2000, 4000]);
});
it("should log retry attempts in development mode", async (): Promise<void> => {
process.env.NODE_ENV = "development";
it("should log retry attempts in all environments", async (): Promise<void> => {
const warnSpy = vi.spyOn(console, "warn").mockImplementation((): void => {});
const okResponse = mockResponse(200);
@@ -222,18 +219,22 @@ describe("fetchWithRetry", (): void => {
warnSpy.mockRestore();
});
it("should NOT log retry attempts in production mode", async (): Promise<void> => {
process.env.NODE_ENV = "production";
it("should log retry attempts for HTTP errors", async (): Promise<void> => {
const warnSpy = vi.spyOn(console, "warn").mockImplementation((): void => {});
const serverError = mockResponse(500);
const okResponse = mockResponse(200);
vi.mocked(global.fetch)
.mockRejectedValueOnce(new TypeError("Failed to fetch"))
.mockResolvedValueOnce(serverError)
.mockResolvedValueOnce(okResponse);
await fetchWithRetry("https://api.example.com/auth/config");
expect(warnSpy).not.toHaveBeenCalled();
expect(warnSpy).toHaveBeenCalledTimes(1);
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining("[Auth] Retry 1/3 after HTTP 500"),
);
warnSpy.mockRestore();
});