From 2b700115e87258f27f5547b83cbcf78645d3525b Mon Sep 17 00:00:00 2001 From: Knacky Date: Thu, 28 May 2026 06:43:33 +0200 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20sprint=205=20=E2=80=94=20corre?= =?UTF-8?q?ct=20API=20path=20/simulation-templates=20=E2=86=92=20/template?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- frontend/src/api/templates.ts | 10 +++++----- frontend/tests/SimulationList.test.tsx | 2 +- frontend/tests/TemplateFormPage.test.tsx | 18 +++++++++--------- frontend/tests/TemplatePickerModal.test.tsx | 12 ++++++------ frontend/tests/TemplatesListPage.test.tsx | 18 +++++++++--------- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/frontend/src/api/templates.ts b/frontend/src/api/templates.ts index 0bd3349..7175af6 100644 --- a/frontend/src/api/templates.ts +++ b/frontend/src/api/templates.ts @@ -6,19 +6,19 @@ import type { } from './types'; export async function listTemplates(): Promise { - const { data } = await apiClient.get('/simulation-templates'); + const { data } = await apiClient.get('/templates'); return data; } export async function getTemplate(id: number): Promise { - const { data } = await apiClient.get(`/simulation-templates/${id}`); + const { data } = await apiClient.get(`/templates/${id}`); return data; } export async function createTemplate( input: SimulationTemplateCreateInput, ): Promise { - const { data } = await apiClient.post('/simulation-templates', input); + const { data } = await apiClient.post('/templates', input); return data; } @@ -26,10 +26,10 @@ export async function updateTemplate( id: number, patch: SimulationTemplatePatchInput, ): Promise { - const { data } = await apiClient.patch(`/simulation-templates/${id}`, patch); + const { data } = await apiClient.patch(`/templates/${id}`, patch); return data; } export async function deleteTemplate(id: number): Promise { - await apiClient.delete(`/simulation-templates/${id}`); + await apiClient.delete(`/templates/${id}`); } diff --git a/frontend/tests/SimulationList.test.tsx b/frontend/tests/SimulationList.test.tsx index 20178c4..fbc763e 100644 --- a/frontend/tests/SimulationList.test.tsx +++ b/frontend/tests/SimulationList.test.tsx @@ -129,7 +129,7 @@ describe('SimulationList — admin/redteam', () => { it('opens TemplatePickerModal when "From template…" is clicked', async () => { mock.onGet('/engagements/42/simulations').reply(200, SIMULATIONS); - mock.onGet('/simulation-templates').reply(200, []); + mock.onGet('/templates').reply(200, []); const user = userEvent.setup(); renderWithProviders(); await waitFor(() => { diff --git a/frontend/tests/TemplateFormPage.test.tsx b/frontend/tests/TemplateFormPage.test.tsx index 32e8142..09cb5dc 100644 --- a/frontend/tests/TemplateFormPage.test.tsx +++ b/frontend/tests/TemplateFormPage.test.tsx @@ -112,7 +112,7 @@ describe('TemplateFormPage — new mode', () => { }); it('submits POST when name is filled', async () => { - mock.onPost('/simulation-templates').reply(201, { ...TEMPLATE, id: 99 }); + mock.onPost('/templates').reply(201, { ...TEMPLATE, id: 99 }); const user = userEvent.setup(); renderNew(); await user.type(screen.getByLabelText(/Name/i), 'My Template'); @@ -125,7 +125,7 @@ describe('TemplateFormPage — new mode', () => { }); it('shows backend error on name conflict (409)', async () => { - mock.onPost('/simulation-templates').reply(409, { error: 'template name already exists' }); + mock.onPost('/templates').reply(409, { error: 'template name already exists' }); const user = userEvent.setup(); renderNew(); await user.type(screen.getByLabelText(/Name/i), 'Duplicate'); @@ -153,7 +153,7 @@ describe('TemplateFormPage — edit mode', () => { }); it('loads existing template data into form', async () => { - mock.onGet('/simulation-templates/5').reply(200, TEMPLATE); + mock.onGet('/templates/5').reply(200, TEMPLATE); renderEdit(5); await waitFor(() => { expect(screen.getByDisplayValue('Mimikatz LSASS Dump')).toBeInTheDocument(); @@ -162,7 +162,7 @@ describe('TemplateFormPage — edit mode', () => { }); it('shows technique and tactic chips from existing template', async () => { - mock.onGet('/simulation-templates/5').reply(200, TEMPLATE); + mock.onGet('/templates/5').reply(200, TEMPLATE); renderEdit(5); await waitFor(() => { expect(screen.getByTitle('T1003 — OS Credential Dumping')).toBeInTheDocument(); @@ -171,7 +171,7 @@ describe('TemplateFormPage — edit mode', () => { }); it('shows Delete button in edit mode', async () => { - mock.onGet('/simulation-templates/5').reply(200, TEMPLATE); + mock.onGet('/templates/5').reply(200, TEMPLATE); renderEdit(5); await waitFor(() => { expect(screen.getByDisplayValue('Mimikatz LSASS Dump')).toBeInTheDocument(); @@ -180,8 +180,8 @@ describe('TemplateFormPage — edit mode', () => { }); it('submits PATCH on save', async () => { - mock.onGet('/simulation-templates/5').reply(200, TEMPLATE); - mock.onPatch('/simulation-templates/5').reply(200, { ...TEMPLATE, name: 'Updated' }); + mock.onGet('/templates/5').reply(200, TEMPLATE); + mock.onPatch('/templates/5').reply(200, { ...TEMPLATE, name: 'Updated' }); const user = userEvent.setup(); renderEdit(5); await waitFor(() => { @@ -196,8 +196,8 @@ describe('TemplateFormPage — edit mode', () => { }); it('opens delete confirm dialog and calls DELETE on confirm', async () => { - mock.onGet('/simulation-templates/5').reply(200, TEMPLATE); - mock.onDelete('/simulation-templates/5').reply(204); + mock.onGet('/templates/5').reply(200, TEMPLATE); + mock.onDelete('/templates/5').reply(204); const user = userEvent.setup(); renderEdit(5); await waitFor(() => { diff --git a/frontend/tests/TemplatePickerModal.test.tsx b/frontend/tests/TemplatePickerModal.test.tsx index efd67ee..f067200 100644 --- a/frontend/tests/TemplatePickerModal.test.tsx +++ b/frontend/tests/TemplatePickerModal.test.tsx @@ -51,7 +51,7 @@ describe('TemplatePickerModal', () => { }); it('shows loading state while fetching', () => { - mock.onGet('/simulation-templates').reply(() => new Promise(() => {})); + mock.onGet('/templates').reply(() => new Promise(() => {})); renderWithProviders( { }); it('shows empty state when no templates', async () => { - mock.onGet('/simulation-templates').reply(200, []); + mock.onGet('/templates').reply(200, []); renderWithProviders( { }); it('lists templates with name and MITRE count', async () => { - mock.onGet('/simulation-templates').reply(200, TEMPLATES); + mock.onGet('/templates').reply(200, TEMPLATES); renderWithProviders( { }); it('calls onSelectTemplate when a template row is clicked', async () => { - mock.onGet('/simulation-templates').reply(200, TEMPLATES); + mock.onGet('/templates').reply(200, TEMPLATES); const user = userEvent.setup(); renderWithProviders( { }); it('calls onClose when Cancel is clicked', async () => { - mock.onGet('/simulation-templates').reply(200, TEMPLATES); + mock.onGet('/templates').reply(200, TEMPLATES); const user = userEvent.setup(); renderWithProviders( { }); it('shows error state on fetch failure', async () => { - mock.onGet('/simulation-templates').reply(500, { error: 'Server error' }); + mock.onGet('/templates').reply(500, { error: 'Server error' }); renderWithProviders( { }); it('shows loading state initially', () => { - mock.onGet('/simulation-templates').reply(() => new Promise(() => {})); + mock.onGet('/templates').reply(() => new Promise(() => {})); renderWithProviders(); expect(screen.getByTestId('loading-state')).toBeInTheDocument(); }); it('shows error state on failure', async () => { - mock.onGet('/simulation-templates').reply(500, { error: 'Server error' }); + mock.onGet('/templates').reply(500, { error: 'Server error' }); renderWithProviders(); await waitFor(() => { expect(screen.getByTestId('error-state')).toBeInTheDocument(); @@ -73,7 +73,7 @@ describe('TemplatesListPage', () => { }); it('shows empty state when no templates', async () => { - mock.onGet('/simulation-templates').reply(200, []); + mock.onGet('/templates').reply(200, []); renderWithProviders(); await waitFor(() => { expect(screen.getByTestId('empty-state')).toBeInTheDocument(); @@ -81,7 +81,7 @@ describe('TemplatesListPage', () => { }); it('renders template list with name, MITRE count, created by', async () => { - mock.onGet('/simulation-templates').reply(200, TEMPLATES); + mock.onGet('/templates').reply(200, TEMPLATES); renderWithProviders(); await waitFor(() => { expect(screen.getByText('Mimikatz LSASS Dump')).toBeInTheDocument(); @@ -95,7 +95,7 @@ describe('TemplatesListPage', () => { }); it('shows New button', async () => { - mock.onGet('/simulation-templates').reply(200, TEMPLATES); + mock.onGet('/templates').reply(200, TEMPLATES); renderWithProviders(); await waitFor(() => { expect(screen.getByText('Mimikatz LSASS Dump')).toBeInTheDocument(); @@ -104,7 +104,7 @@ describe('TemplatesListPage', () => { }); it('shows Edit and Delete actions', async () => { - mock.onGet('/simulation-templates').reply(200, TEMPLATES); + mock.onGet('/templates').reply(200, TEMPLATES); renderWithProviders(); await waitFor(() => { expect(screen.getByText('Mimikatz LSASS Dump')).toBeInTheDocument(); @@ -114,10 +114,10 @@ describe('TemplatesListPage', () => { }); it('calls delete endpoint on confirm', async () => { - mock.onGet('/simulation-templates').reply(200, TEMPLATES); - mock.onDelete('/simulation-templates/1').reply(204); + mock.onGet('/templates').reply(200, TEMPLATES); + mock.onDelete('/templates/1').reply(204); // After delete, refetch returns updated list - mock.onGet('/simulation-templates').reply(200, [TEMPLATES[1]]); + mock.onGet('/templates').reply(200, [TEMPLATES[1]]); const user = userEvent.setup(); const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(true);