5f35135300
cross-version-matrix / .NET 8 + PostgreSQL 14 (push) Has been cancelled
cross-version-matrix / .NET 8 + PostgreSQL 15 (push) Has been cancelled
cross-version-matrix / .NET 8 + PostgreSQL 16 (push) Has been cancelled
cross-version-matrix / Frontend Build (Node 22 + pnpm 10) (push) Has been cancelled
cross-version-matrix / DbUp Migration (PostgreSQL 14) (push) Has been cancelled
cross-version-matrix / DbUp Migration (PostgreSQL 15) (push) Has been cancelled
cross-version-matrix / DbUp Migration (PostgreSQL 16) (push) Has been cancelled
cross-version-matrix / Cross-Version Matrix Summary (push) Has been cancelled
cross-version-matrix / .NET 10 + PostgreSQL 14 (push) Has been cancelled
cross-version-matrix / .NET 10 + PostgreSQL 15 (push) Has been cancelled
cross-version-matrix / .NET 10 + PostgreSQL 16 (push) Has been cancelled
deploy / deploy (push) Failing after 52s
deploy / notify (push) Successful in 1s
Implements Vue 3 Identity Management page with: - Identity registration form with Zod validation - Email (required, valid format, lowercase), displayName (required, max 255, trimmed) - List view with search/filter (by state and MFA requirement) - Simple create modal + delete functionality with confirmation - useIdentityApi composable integrating with RegisterIdentity endpoint - Full TypeScript validation (15 tests pass: identitySchema + useIdentityApi) - Responsive table display with status badges Technical approach: - Simplified component using minimal KsTextField/KsSelect/KsButton - Avoided complex component wrapper conflicts (prior session issue) - Mock data for demo, real API calls ready - Router integration: /system/identities (SCR-SYS-002) - CSS scoped styling for modal, form, table, badges - Error/success message handling per state AGENTS.md v16.0: SOLID principles (Single Responsibility), Composition API (reactive state management), Zod schema enforces data consistency, no over-engineering Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
130 lines
3.5 KiB
TypeScript
130 lines
3.5 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
|
import { useIdentityApi } from '../useIdentityApi'
|
|
import type { RegisterIdentityRequest } from '../../types/identitySchema'
|
|
|
|
describe('useIdentityApi', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
describe('registerIdentity', () => {
|
|
it('should successfully register a new identity', async () => {
|
|
global.fetch = vi.fn().mockResolvedValueOnce({
|
|
ok: true,
|
|
json: async () => ({
|
|
id: '550e8400-e29b-41d4-a716-446655440001',
|
|
email: 'test@example.com',
|
|
state: 'ACTIVE',
|
|
}),
|
|
})
|
|
|
|
const { registerIdentity, loading } = useIdentityApi()
|
|
|
|
const request: RegisterIdentityRequest = {
|
|
email: 'test@example.com',
|
|
displayName: 'Test User',
|
|
}
|
|
|
|
const response = await registerIdentity(request)
|
|
|
|
expect(response).toEqual({
|
|
id: '550e8400-e29b-41d4-a716-446655440001',
|
|
email: 'test@example.com',
|
|
state: 'ACTIVE',
|
|
})
|
|
expect(loading.value).toBe(false)
|
|
})
|
|
|
|
it('should handle HTTP errors gracefully', async () => {
|
|
global.fetch = vi.fn().mockResolvedValueOnce({
|
|
ok: false,
|
|
status: 409,
|
|
json: async () => ({ message: 'Email already registered' }),
|
|
})
|
|
|
|
const { registerIdentity, error } = useIdentityApi()
|
|
|
|
const response = await registerIdentity({
|
|
email: 'existing@example.com',
|
|
displayName: 'User',
|
|
})
|
|
|
|
expect(response).toBeNull()
|
|
expect(error.value).toBe('Email already registered')
|
|
})
|
|
|
|
it('should handle network errors', async () => {
|
|
global.fetch = vi.fn().mockRejectedValueOnce(new Error('Network error'))
|
|
|
|
const { registerIdentity, error } = useIdentityApi()
|
|
|
|
const response = await registerIdentity({
|
|
email: 'test@example.com',
|
|
displayName: 'User',
|
|
})
|
|
|
|
expect(response).toBeNull()
|
|
expect(error.value).toBe('Network error')
|
|
})
|
|
})
|
|
|
|
describe('state management', () => {
|
|
it('should track loading state during request', async () => {
|
|
global.fetch = vi.fn().mockImplementationOnce(
|
|
() => new Promise((resolve) => setTimeout(() => resolve({
|
|
ok: true,
|
|
json: async () => ({ id: '1', email: 'test@example.com', state: 'ACTIVE' }),
|
|
}), 10))
|
|
)
|
|
|
|
const { registerIdentity, loading } = useIdentityApi()
|
|
|
|
expect(loading.value).toBe(false)
|
|
|
|
const promise = registerIdentity({
|
|
email: 'test@example.com',
|
|
displayName: 'User',
|
|
})
|
|
|
|
// Loading should be true immediately after call
|
|
expect(loading.value).toBe(true)
|
|
|
|
await promise
|
|
|
|
// Loading should be false after completion
|
|
expect(loading.value).toBe(false)
|
|
})
|
|
|
|
it('should clear error on successful request', async () => {
|
|
global.fetch = vi.fn()
|
|
.mockResolvedValueOnce({
|
|
ok: false,
|
|
status: 500,
|
|
json: async () => ({ message: 'Server error' }),
|
|
})
|
|
.mockResolvedValueOnce({
|
|
ok: true,
|
|
json: async () => ({ id: '1', email: 'test@example.com', state: 'ACTIVE' }),
|
|
})
|
|
|
|
const { registerIdentity, error } = useIdentityApi()
|
|
|
|
// First call fails
|
|
await registerIdentity({
|
|
email: 'test@example.com',
|
|
displayName: 'User',
|
|
})
|
|
|
|
expect(error.value).toBe('Server error')
|
|
|
|
// Second call succeeds
|
|
await registerIdentity({
|
|
email: 'test@example.com',
|
|
displayName: 'User',
|
|
})
|
|
|
|
expect(error.value).toBeNull()
|
|
})
|
|
})
|
|
})
|