diff --git a/frontend/src/mocks/index.ts b/frontend/src/mocks/index.ts index 74caf63..85e9d10 100644 --- a/frontend/src/mocks/index.ts +++ b/frontend/src/mocks/index.ts @@ -9,7 +9,7 @@ export const handlers = [...authHandlers, ...userHandlers, ...setupHandlers, ... export { authHandlers } from './auth/handlers'; export { userHandlers } from './users/handlers'; -export { setupHandlers, setupUninitializedHandlers, setupConflictHandlers } from './setup/handlers'; +export { setupHandlers, setupUninitializedHandlers, setupConflictHandlers, setupNetworkErrorHandlers } from './setup/handlers'; export { invitationHandlers } from './invitation/handlers'; export { availabilityHandlers } from './availability/handlers'; export * from './auth/fixtures'; diff --git a/frontend/src/mocks/setup/handlers.ts b/frontend/src/mocks/setup/handlers.ts index 4979ace..dcd717f 100644 --- a/frontend/src/mocks/setup/handlers.ts +++ b/frontend/src/mocks/setup/handlers.ts @@ -37,6 +37,11 @@ export const setupHandlers = [ }), ]; +/** Override: simulates network error on setup status — backend unreachable. */ +export const setupNetworkErrorHandlers = [ + http.get(`${API_BASE}/api/v1/Setup/status`, () => HttpResponse.error()), +]; + /** Override: returns not-initialized status — use in tests for InitGuard. */ export const setupUninitializedHandlers = [ http.get(`${API_BASE}/api/v1/Setup/status`, () => diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index b883282..9aa8452 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -8,7 +8,7 @@ import { } from '@tanstack/react-router'; import type { AuthContextValue } from '@/contexts/auth-context'; import type { SetupStatus } from '@/api/types'; -import { api } from '@/lib/api-client'; +import { api, NetworkError } from '@/lib/api-client'; import { AppLayout } from '@/components/layout/AppLayout'; import { LoginPage } from '@/pages/LoginPage'; import { NotFoundPage } from '@/pages/NotFoundPage'; @@ -99,10 +99,13 @@ const rootRoute = createRootRouteWithContext()({ try { status = await fetchSetupStatus(); } catch (err) { - // On network failure, assume not initialized so the user can set up - // the system once the backend is reachable. Logging helps debugging. - console.warn('[InitGuard] Setup status check failed, assuming not initialized:', err); - status = { initialized: false }; + if (err instanceof NetworkError) { + // Backend unreachable: skip setup redirect and show the login page instead. + status = { initialized: true }; + } else { + console.warn('[InitGuard] Setup status check failed, assuming not initialized:', err); + status = { initialized: false }; + } } const onSetup = location.pathname === '/setup'; diff --git a/frontend/src/test/RouteGuard.test.tsx b/frontend/src/test/RouteGuard.test.tsx index 7cedab2..5c1da9a 100644 --- a/frontend/src/test/RouteGuard.test.tsx +++ b/frontend/src/test/RouteGuard.test.tsx @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it } from 'vitest'; import { screen } from '@testing-library/react'; import { renderApp, mockAuthenticated, mockGuest } from '@/test/utils'; import { server } from '@/mocks/server'; -import { setupUninitializedHandlers } from '@/mocks/index'; +import { setupUninitializedHandlers, setupNetworkErrorHandlers } from '@/mocks/index'; import { _resetSetupStatusCache } from '@/router'; beforeEach(() => { @@ -53,6 +53,24 @@ describe('InitGuard (BR-U2-09, BR-U2-10)', () => { expect(await screen.findByTestId('setup-submit-button')).toBeInTheDocument(); }); + it('shows login page (not setup) when backend is unreachable', async () => { + server.use(...setupNetworkErrorHandlers); + mockGuest(); + renderApp('/login'); + + expect(await screen.findByTestId('login-form-submit-button')).toBeInTheDocument(); + expect(screen.queryByTestId('setup-submit-button')).not.toBeInTheDocument(); + }); + + it('redirects /setup to /login when backend is unreachable', async () => { + server.use(...setupNetworkErrorHandlers); + mockGuest(); + renderApp('/setup'); + + expect(await screen.findByTestId('login-form-submit-button')).toBeInTheDocument(); + expect(screen.queryByTestId('setup-submit-button')).not.toBeInTheDocument(); + }); + it('redirects /setup to /login when system is already initialized', async () => { // Default handlers return { initialized: true }. mockGuest();