mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 19:33:41 +00:00
test: add hook tests for useDisclosures, useAuditLog, useUsers
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6ede0d93ed
commit
e67fe73da7
3 changed files with 430 additions and 0 deletions
116
frontend/src/hooks/__tests__/useAuditLog.test.ts
Normal file
116
frontend/src/hooks/__tests__/useAuditLog.test.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import { waitFor } from '@testing-library/react'
|
||||
import { http, HttpResponse } from 'msw'
|
||||
import { server } from '@/test/mocks/server'
|
||||
import { renderHookWithProviders } from '@/test/utils'
|
||||
import { mockAuditLogEntry } from '@/test/mocks/data'
|
||||
import { useAuditLog } from '@/hooks/useAuditLog'
|
||||
import type { AuditLogFilters } from '@/hooks/useAuditLog'
|
||||
|
||||
describe('useAuditLog', () => {
|
||||
const defaultFilters: AuditLogFilters = { skip: 0, limit: 50 }
|
||||
|
||||
it('fetches audit log entries (success)', async () => {
|
||||
const { result } = renderHookWithProviders(() => useAuditLog(defaultFilters))
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(result.current.data).toEqual([mockAuditLogEntry])
|
||||
expect(result.current.data).toHaveLength(1)
|
||||
expect(result.current.data![0].action).toBe('case.update')
|
||||
})
|
||||
|
||||
it('passes filter parameters (user_id, action, date_from, date_to, skip, limit)', async () => {
|
||||
const capturedParams: Record<string, string | null> = {}
|
||||
|
||||
server.use(
|
||||
http.get('/api/admin/audit-log', ({ request }) => {
|
||||
const url = new URL(request.url)
|
||||
capturedParams.skip = url.searchParams.get('skip')
|
||||
capturedParams.limit = url.searchParams.get('limit')
|
||||
capturedParams.user_id = url.searchParams.get('user_id')
|
||||
capturedParams.action = url.searchParams.get('action')
|
||||
capturedParams.date_from = url.searchParams.get('date_from')
|
||||
capturedParams.date_to = url.searchParams.get('date_to')
|
||||
return HttpResponse.json([mockAuditLogEntry])
|
||||
}),
|
||||
)
|
||||
|
||||
const filters: AuditLogFilters = {
|
||||
skip: 10,
|
||||
limit: 25,
|
||||
user_id: '1',
|
||||
action: 'case.update',
|
||||
date_from: '2026-02-01',
|
||||
date_to: '2026-02-28',
|
||||
}
|
||||
|
||||
const { result } = renderHookWithProviders(() => useAuditLog(filters))
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(capturedParams.skip).toBe('10')
|
||||
expect(capturedParams.limit).toBe('25')
|
||||
expect(capturedParams.user_id).toBe('1')
|
||||
expect(capturedParams.action).toBe('case.update')
|
||||
expect(capturedParams.date_from).toBe('2026-02-01')
|
||||
expect(capturedParams.date_to).toBe('2026-02-28')
|
||||
})
|
||||
|
||||
it('does not send optional params when they are undefined', async () => {
|
||||
const capturedParams: Record<string, string | null> = {}
|
||||
|
||||
server.use(
|
||||
http.get('/api/admin/audit-log', ({ request }) => {
|
||||
const url = new URL(request.url)
|
||||
capturedParams.skip = url.searchParams.get('skip')
|
||||
capturedParams.limit = url.searchParams.get('limit')
|
||||
capturedParams.user_id = url.searchParams.get('user_id')
|
||||
capturedParams.action = url.searchParams.get('action')
|
||||
capturedParams.date_from = url.searchParams.get('date_from')
|
||||
capturedParams.date_to = url.searchParams.get('date_to')
|
||||
return HttpResponse.json([])
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useAuditLog(defaultFilters))
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(capturedParams.skip).toBe('0')
|
||||
expect(capturedParams.limit).toBe('50')
|
||||
expect(capturedParams.user_id).toBeNull()
|
||||
expect(capturedParams.action).toBeNull()
|
||||
expect(capturedParams.date_from).toBeNull()
|
||||
expect(capturedParams.date_to).toBeNull()
|
||||
})
|
||||
|
||||
it('handles empty response', async () => {
|
||||
server.use(
|
||||
http.get('/api/admin/audit-log', () => {
|
||||
return HttpResponse.json([])
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useAuditLog(defaultFilters))
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(result.current.data).toEqual([])
|
||||
expect(result.current.data).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('handles server error (500)', async () => {
|
||||
server.use(
|
||||
http.get('/api/admin/audit-log', () => {
|
||||
return new HttpResponse(null, { status: 500 })
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useAuditLog(defaultFilters))
|
||||
|
||||
await waitFor(() => expect(result.current.isError).toBe(true))
|
||||
|
||||
expect(result.current.data).toBeUndefined()
|
||||
})
|
||||
})
|
||||
146
frontend/src/hooks/__tests__/useDisclosures.test.ts
Normal file
146
frontend/src/hooks/__tests__/useDisclosures.test.ts
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import { waitFor, act } from '@testing-library/react'
|
||||
import { http, HttpResponse } from 'msw'
|
||||
import { server } from '@/test/mocks/server'
|
||||
import { renderHookWithProviders } from '@/test/utils'
|
||||
import {
|
||||
mockDisclosureRequest,
|
||||
mockDisclosureApproved,
|
||||
} from '@/test/mocks/data'
|
||||
import { useDisclosures, useReviewDisclosure } from '@/hooks/useDisclosures'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// useDisclosures
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('useDisclosures', () => {
|
||||
it('fetches disclosures with status filter (success)', async () => {
|
||||
let capturedStatus: string | null = null
|
||||
|
||||
server.use(
|
||||
http.get('/api/admin/disclosure-requests', ({ request }) => {
|
||||
const url = new URL(request.url)
|
||||
capturedStatus = url.searchParams.get('status')
|
||||
return HttpResponse.json([mockDisclosureRequest])
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useDisclosures('pending'))
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(capturedStatus).toBe('pending')
|
||||
expect(result.current.data).toEqual([mockDisclosureRequest])
|
||||
expect(result.current.data).toHaveLength(1)
|
||||
expect(result.current.data![0].status).toBe('pending')
|
||||
})
|
||||
|
||||
it('handles empty list', async () => {
|
||||
server.use(
|
||||
http.get('/api/admin/disclosure-requests', () => {
|
||||
return HttpResponse.json([])
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useDisclosures('pending'))
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(result.current.data).toEqual([])
|
||||
expect(result.current.data).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('works without status parameter (uses default "pending")', async () => {
|
||||
let capturedStatus: string | null = null
|
||||
|
||||
server.use(
|
||||
http.get('/api/admin/disclosure-requests', ({ request }) => {
|
||||
const url = new URL(request.url)
|
||||
capturedStatus = url.searchParams.get('status')
|
||||
return HttpResponse.json([mockDisclosureRequest])
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useDisclosures())
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(capturedStatus).toBe('pending')
|
||||
expect(result.current.data).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// useReviewDisclosure
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('useReviewDisclosure', () => {
|
||||
it('approves a disclosure (mutation success)', async () => {
|
||||
server.use(
|
||||
http.put('/api/admin/disclosure-requests/:id', () => {
|
||||
return HttpResponse.json(mockDisclosureApproved)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useReviewDisclosure())
|
||||
|
||||
let response: unknown
|
||||
await act(async () => {
|
||||
response = await result.current.mutateAsync({ id: 1, status: 'approved' })
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect((response as { status: string }).status).toBe('approved')
|
||||
expect((response as { reviewed_by: number }).reviewed_by).toBe(1)
|
||||
})
|
||||
|
||||
it('rejects a disclosure', async () => {
|
||||
const rejectedDisclosure = {
|
||||
...mockDisclosureRequest,
|
||||
status: 'rejected' as const,
|
||||
reviewed_by: 1,
|
||||
reviewed_at: '2026-02-26T09:30:00Z',
|
||||
}
|
||||
|
||||
server.use(
|
||||
http.put('/api/admin/disclosure-requests/:id', () => {
|
||||
return HttpResponse.json(rejectedDisclosure)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useReviewDisclosure())
|
||||
|
||||
let response: unknown
|
||||
await act(async () => {
|
||||
response = await result.current.mutateAsync({ id: 1, status: 'rejected' })
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect((response as { status: string }).status).toBe('rejected')
|
||||
})
|
||||
|
||||
it('handles error', async () => {
|
||||
server.use(
|
||||
http.put('/api/admin/disclosure-requests/:id', () => {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Not found' },
|
||||
{ status: 404 },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useReviewDisclosure())
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({ id: 999, status: 'approved' })
|
||||
} catch {
|
||||
// expected
|
||||
}
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isError).toBe(true))
|
||||
})
|
||||
})
|
||||
168
frontend/src/hooks/__tests__/useUsers.test.ts
Normal file
168
frontend/src/hooks/__tests__/useUsers.test.ts
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import { waitFor, act } from '@testing-library/react'
|
||||
import { http, HttpResponse } from 'msw'
|
||||
import { server } from '@/test/mocks/server'
|
||||
import { renderHookWithProviders } from '@/test/utils'
|
||||
import { mockUserResponse } from '@/test/mocks/data'
|
||||
import { useUsers, useCreateUser, useUpdateUser } from '@/hooks/useUsers'
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// useUsers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('useUsers', () => {
|
||||
it('fetches user list (success)', async () => {
|
||||
const { result } = renderHookWithProviders(() => useUsers())
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(result.current.data).toEqual([mockUserResponse])
|
||||
expect(result.current.data).toHaveLength(1)
|
||||
expect(result.current.data![0].username).toBe('admin_user')
|
||||
expect(result.current.data![0].role).toBe('admin')
|
||||
})
|
||||
|
||||
it('handles empty list', async () => {
|
||||
server.use(
|
||||
http.get('/api/admin/users', () => {
|
||||
return HttpResponse.json([])
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useUsers())
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect(result.current.data).toEqual([])
|
||||
expect(result.current.data).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// useCreateUser
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('useCreateUser', () => {
|
||||
it('creates user (success)', async () => {
|
||||
const newUser = {
|
||||
...mockUserResponse,
|
||||
id: 3,
|
||||
username: 'new_user',
|
||||
email: 'new@dak.de',
|
||||
role: 'dak_mitarbeiter' as const,
|
||||
}
|
||||
|
||||
server.use(
|
||||
http.post('/api/admin/users', () => {
|
||||
return HttpResponse.json(newUser)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useCreateUser())
|
||||
|
||||
let response: unknown
|
||||
await act(async () => {
|
||||
response = await result.current.mutateAsync({
|
||||
username: 'new_user',
|
||||
email: 'new@dak.de',
|
||||
password: 'SecurePass123!',
|
||||
role: 'dak_mitarbeiter',
|
||||
})
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect((response as { id: number }).id).toBe(3)
|
||||
expect((response as { username: string }).username).toBe('new_user')
|
||||
expect((response as { role: string }).role).toBe('dak_mitarbeiter')
|
||||
})
|
||||
|
||||
it('handles validation error (422)', async () => {
|
||||
server.use(
|
||||
http.post('/api/admin/users', () => {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'Username already exists' },
|
||||
{ status: 422 },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useCreateUser())
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
username: 'admin_user',
|
||||
email: 'duplicate@dak.de',
|
||||
password: 'SecurePass123!',
|
||||
role: 'admin',
|
||||
})
|
||||
} catch {
|
||||
// expected
|
||||
}
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isError).toBe(true))
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// useUpdateUser
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('useUpdateUser', () => {
|
||||
it('updates user (success)', async () => {
|
||||
const updatedUser = {
|
||||
...mockUserResponse,
|
||||
role: 'dak_mitarbeiter' as const,
|
||||
is_active: false,
|
||||
}
|
||||
|
||||
server.use(
|
||||
http.put('/api/admin/users/:id', () => {
|
||||
return HttpResponse.json(updatedUser)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useUpdateUser())
|
||||
|
||||
let response: unknown
|
||||
await act(async () => {
|
||||
response = await result.current.mutateAsync({
|
||||
id: 1,
|
||||
payload: { role: 'dak_mitarbeiter', is_active: false },
|
||||
})
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
||||
|
||||
expect((response as { role: string }).role).toBe('dak_mitarbeiter')
|
||||
expect((response as { is_active: boolean }).is_active).toBe(false)
|
||||
})
|
||||
|
||||
it('handles error (404 Not Found)', async () => {
|
||||
server.use(
|
||||
http.put('/api/admin/users/:id', () => {
|
||||
return HttpResponse.json(
|
||||
{ detail: 'User not found' },
|
||||
{ status: 404 },
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
const { result } = renderHookWithProviders(() => useUpdateUser())
|
||||
|
||||
await act(async () => {
|
||||
try {
|
||||
await result.current.mutateAsync({
|
||||
id: 999,
|
||||
payload: { is_active: false },
|
||||
})
|
||||
} catch {
|
||||
// expected
|
||||
}
|
||||
})
|
||||
|
||||
await waitFor(() => expect(result.current.isError).toBe(true))
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue