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:
@@ -129,7 +129,8 @@ export function AuthProvider({ children }: { children: ReactNode }): React.JSX.E
|
||||
try {
|
||||
await apiPost("/auth/sign-out");
|
||||
} catch (error) {
|
||||
console.error("Sign out error:", error);
|
||||
logAuthError("Sign out request did not complete", error);
|
||||
setAuthError("network");
|
||||
} finally {
|
||||
setUser(null);
|
||||
expiresAtRef.current = null;
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -80,9 +80,7 @@ export async function fetchWithRetry(
|
||||
lastResponse = response;
|
||||
const delay = computeDelay(attempt, baseDelayMs, backoffFactor);
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.warn(`[Auth] Retry ${attempt + 1}/${maxRetries} after HTTP ${response.status}, waiting ${delay}ms...`);
|
||||
}
|
||||
console.warn(`[Auth] Retry ${attempt + 1}/${maxRetries} after HTTP ${response.status}, waiting ${delay}ms...`);
|
||||
|
||||
await sleep(delay);
|
||||
} catch (error: unknown) {
|
||||
@@ -96,9 +94,7 @@ export async function fetchWithRetry(
|
||||
lastError = error;
|
||||
const delay = computeDelay(attempt, baseDelayMs, backoffFactor);
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
console.warn(`[Auth] Retry ${attempt + 1}/${maxRetries} after ${parsed.code}, waiting ${delay}ms...`);
|
||||
}
|
||||
console.warn(`[Auth] Retry ${attempt + 1}/${maxRetries} after ${parsed.code}, waiting ${delay}ms...`);
|
||||
|
||||
await sleep(delay);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user