fix(unit-2-auth): Fix API endpoint paths and JSON casing for real backend
- Add /api/v1/ prefix to all Unit 2 API calls (Setup/status, Setup/owner,
Invitation/validate, Invitation/complete) to match backend ApiPrefixConvention
- Fix setup status endpoint to POST /api/v1/Setup/owner (not /Setup)
- Handle PascalCase 'Initialized' response from .NET backend without camelCase policy
- Update all MSW mock handlers to match corrected /api/v1/ URL patterns
- Remove unused imports from RouteGuard.test.tsx
Root cause: backend uses ApiPrefixConvention('api/v1') but frontend calls
were missing the prefix, and .NET defaults to PascalCase JSON serialization.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,7 +35,7 @@ export function useValidateInvitation(token: string | null) {
|
||||
setIsPending(true);
|
||||
setError(null);
|
||||
try {
|
||||
const url = `/Invitation/validate?token=${encodeURIComponent(token)}`;
|
||||
const url = `/api/v1/Invitation/validate?token=${encodeURIComponent(token)}`;
|
||||
const response = await api.get(url);
|
||||
setData(response as InvitationValidationResponse);
|
||||
} catch (err: unknown) {
|
||||
@@ -64,7 +64,7 @@ export function useCompleteInvitation() {
|
||||
setIsPending(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await api.post('/Invitation/complete', data);
|
||||
const response = await api.post('/api/v1/Invitation/complete', data);
|
||||
return response as InvitationCompleteResponse;
|
||||
} catch (err: unknown) {
|
||||
const error = err instanceof Error ? err : new Error(String(err));
|
||||
|
||||
@@ -26,7 +26,7 @@ export function useSetup() {
|
||||
setIsPending(true);
|
||||
setError(null);
|
||||
try {
|
||||
const response = await api.post('/Setup', data);
|
||||
const response = await api.post('/api/v1/Setup/owner', data);
|
||||
return response as SetupResponse;
|
||||
} catch (err) {
|
||||
const error = err instanceof Error ? err : new Error(String(err));
|
||||
|
||||
@@ -2,7 +2,7 @@ import { http, HttpResponse } from 'msw';
|
||||
import { API_BASE } from '../auth/fixtures';
|
||||
|
||||
export const invitationHandlers = [
|
||||
http.get(`${API_BASE}/Invitation/validate`, ({ request }) => {
|
||||
http.get(`${API_BASE}/api/v1/Invitation/validate`, ({ request }) => {
|
||||
const token = new URL(request.url).searchParams.get('token');
|
||||
|
||||
if (token === 'valid-token') {
|
||||
@@ -25,7 +25,7 @@ export const invitationHandlers = [
|
||||
);
|
||||
}),
|
||||
|
||||
http.post(`${API_BASE}/Invitation/complete`, async ({ request }) => {
|
||||
http.post(`${API_BASE}/api/v1/Invitation/complete`, async ({ request }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const body = await request.json() as any;
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@ import { API_BASE } from '../auth/fixtures';
|
||||
|
||||
/** Setup status mock (used by public bootstrap guards). */
|
||||
export const setupHandlers = [
|
||||
http.get(`${API_BASE}/Setup/status`, () =>
|
||||
http.get(`${API_BASE}/api/v1/Setup/status`, () =>
|
||||
HttpResponse.json<SetupStatus>({ initialized: true }),
|
||||
),
|
||||
|
||||
http.post(`${API_BASE}/Setup`, async ({ request }) => {
|
||||
http.post(`${API_BASE}/api/v1/Setup/owner`, async ({ request }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const body = await request.json() as any;
|
||||
if (!body.email || !body.password || !body.name) {
|
||||
@@ -39,15 +39,15 @@ export const setupHandlers = [
|
||||
|
||||
/** Override: returns not-initialized status — use in tests for InitGuard. */
|
||||
export const setupUninitializedHandlers = [
|
||||
http.get(`${API_BASE}/Setup/status`, () =>
|
||||
http.get(`${API_BASE}/api/v1/Setup/status`, () =>
|
||||
HttpResponse.json<SetupStatus>({ initialized: false }),
|
||||
),
|
||||
];
|
||||
|
||||
/** Override: POST /Setup returns 409 — system already initialized. */
|
||||
/** Override: POST /Setup/owner returns 409 — system already initialized. */
|
||||
export const setupConflictHandlers = [
|
||||
...setupUninitializedHandlers,
|
||||
http.post(`${API_BASE}/Setup`, () =>
|
||||
http.post(`${API_BASE}/api/v1/Setup/owner`, () =>
|
||||
HttpResponse.json(
|
||||
{ title: 'Conflict', detail: 'System has already been initialized.', status: 409 },
|
||||
{ status: 409 },
|
||||
|
||||
@@ -29,8 +29,10 @@ async function fetchSetupStatus(): Promise<SetupStatus> {
|
||||
if (_setupStatusCache !== null) return _setupStatusCache;
|
||||
if (_setupStatusPromise === null) {
|
||||
_setupStatusPromise = api
|
||||
.get<SetupStatus>('/Setup/status')
|
||||
.then((s) => {
|
||||
.get<{ initialized?: boolean; Initialized?: boolean }>('/api/v1/Setup/status')
|
||||
.then((raw) => {
|
||||
// Backend may return PascalCase (Initialized) if no camelCase policy is set.
|
||||
const s: SetupStatus = { initialized: raw.initialized ?? raw.Initialized ?? false };
|
||||
_setupStatusCache = s;
|
||||
return s;
|
||||
})
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { renderApp, renderWithProviders, mockAuthenticated, mockGuest } from '@/test/utils';
|
||||
import { renderApp, mockAuthenticated, mockGuest } from '@/test/utils';
|
||||
import { server } from '@/mocks/server';
|
||||
import { setupUninitializedHandlers } from '@/mocks/index';
|
||||
import { _resetSetupStatusCache } from '@/router';
|
||||
import { RoleGuard } from '@/components/auth/RoleGuard';
|
||||
|
||||
beforeEach(() => {
|
||||
_resetSetupStatusCache();
|
||||
|
||||
Reference in New Issue
Block a user