fix(19): resolve post-merge wave-4 harness integration gaps
Two issues surfaced only when plans 19-04 (login UI) and 19-05 (Option C bypass + login.spec) were merged together and run against the real stack — neither executor could catch them in isolation: 1. LOCAL_SESSION_SECRET was added to the CI harness (ci.yml) but not to the local dev stack (docker-compose.dev.yml). Without it the real-login success path (POST /api/auth/local/login) 503s when signing the session cookie, so the e2e round-trip failed. Add the same fixed dev-only value to the dev compose override (dev-only target; never a production secret). 2. login.spec test 1 assumed clearing the local-session cookie yields a logged-out state, but under the always-on DEV_AUTH_BYPASS devAuthBypass() injects DEV_USER into /api/me regardless of any cookie — a logged-out state is architecturally unreachable in this bypass-only harness. Reframe the test to drive /login directly (validating the real-browser render of all brand + form surfaces) and move the unauthenticated root->/login redirect-gate coverage to a unit test in App.test.tsx where meQuery.isError is controllable. Result: API 446/446, PWA 265/265 (+2 gate tests), e2e desktop 42 passed / 3 skipped (all login specs green). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
11977fddf4
commit
17a531550a
@@ -237,6 +237,58 @@ describe('App — setup-status gate', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// Phase 19 (AUTH-LOCAL-15): the unauthenticated → /login redirect gate. This lives
|
||||
// here at the unit level because the e2e harness runs DEV_AUTH_BYPASS-only (global-setup
|
||||
// refuses a non-bypass DB), and under the always-on bypass /api/me is authed via DEV_USER
|
||||
// injection regardless of any cookie — so a logged-out state (meQuery.isError) is
|
||||
// architecturally unreachable in the browser harness. The redirect logic is controllable
|
||||
// here by rejecting fetchMe.
|
||||
describe('App — auth gate (Phase 19)', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
window.history.pushState({}, '', '/');
|
||||
mockFetchSetupStatus.mockResolvedValue({ setupComplete: true });
|
||||
_mockFetchAuthMode.mockResolvedValue({ localEnabled: true, oidcEnabled: false });
|
||||
});
|
||||
|
||||
it('redirects to /login when fetchMe errors (unauthenticated) and localEnabled', async () => {
|
||||
mockFetchMe.mockRejectedValue(new Error('401 Unauthorized'));
|
||||
|
||||
const queryClient = makeQueryClient();
|
||||
renderApp(queryClient);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('login-page')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// The authenticated app shell must NOT render for an unauthenticated user.
|
||||
expect(screen.queryByTestId('calendar-shell')).toBeNull();
|
||||
expect(screen.queryByTestId('app-nav')).toBeNull();
|
||||
});
|
||||
|
||||
it('renders the app shell (not /login) when fetchMe succeeds', async () => {
|
||||
mockFetchMe.mockResolvedValue({
|
||||
user: {
|
||||
id: 1,
|
||||
displayName: 'Test User',
|
||||
color: '#4a90d9',
|
||||
isAdmin: false,
|
||||
needsProviderSetup: false,
|
||||
hasLocalCredential: false,
|
||||
},
|
||||
});
|
||||
|
||||
const queryClient = makeQueryClient();
|
||||
renderApp(queryClient);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('calendar-shell')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(screen.queryByTestId('login-page')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('App — setupStatus and route presence', () => {
|
||||
it('App.tsx references setupStatus queryKey', () => {
|
||||
// This test verifies the source-level contract via module inspection.
|
||||
|
||||
Reference in New Issue
Block a user