even more typescript fixes
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 25m5s

This commit is contained in:
2026-02-17 17:20:54 -08:00
parent 4f80baf466
commit 174b637a0a
27 changed files with 1858 additions and 630 deletions

View File

@@ -505,3 +505,115 @@ expect(element.className).toMatch(/dark:bg-teal-\d+/);
```
See [ADR-057](../adr/0057-test-remediation-post-api-versioning.md) for lessons learned from the test remediation effort.
## TypeScript Type Safety in Tests (ADR-060)
Tests must be fully type-safe. Common patterns for handling API response types and mock casting are documented below.
### Response Type Narrowing
API responses use discriminated unions (`ApiSuccessResponse<T> | ApiErrorResponse`). Access `.data` only after type narrowing.
**Utility Functions** (`src/tests/utils/testHelpers.ts`):
```typescript
import { asSuccessResponse, asErrorResponse } from '@/tests/utils/testHelpers';
// Success response access
const response = await request.get('/api/v1/users/1');
const body = asSuccessResponse<User>(response.body);
expect(body.data.id).toBe(1);
// Error response access
const errorResponse = await request.post('/api/v1/users').send({});
expect(errorResponse.status).toBe(400);
const errorBody = asErrorResponse(errorResponse.body);
expect(errorBody.error.code).toBe('VALIDATION_ERROR');
```
### Mock Object Type Casting
Use appropriate casting based on type compatibility:
```typescript
// Level 1: Type assertion for compatible shapes
const mock = createMockUser() as User;
// Level 2: Unknown bridge for incompatible shapes
const mock = partialMock as unknown as User;
// Level 3: Partial with required overrides
const mock: User = { ...createPartialUser(), id: 1, email: 'test@test.com' };
```
### Mock Function Casting
```typescript
import { asMock } from '@/tests/utils/testHelpers';
// Cast vi.fn() to specific function type
const mockFn = vi.fn();
someService.register(asMock<UserService['create']>(mockFn));
// vi.fn() with explicit type parameters
const mockFn = vi.fn<[string], Promise<User>>().mockResolvedValue(mockUser);
// vi.mocked() for mocked modules
vi.mock('@/services/userService');
const mockedService = vi.mocked(userService);
mockedService.create.mockResolvedValue(mockUser);
```
### Mock Logger for Controller Tests
Controllers require a Pino logger on `req.log`. Use the shared mock logger utility:
```typescript
import { createMockLogger } from '@/tests/utils/testHelpers';
function createMockRequest(overrides = {}): ExpressRequest {
return {
body: {},
cookies: {},
log: createMockLogger(),
res: { cookie: vi.fn() } as unknown as ExpressResponse,
...overrides,
} as unknown as ExpressRequest;
}
```
The `createMockLogger()` function returns a complete Pino logger mock with all methods (`info`, `debug`, `error`, `warn`, `fatal`, `trace`, `silent`, `child`) as `vi.fn()` mocks.
### MSW Handler Typing
Ensure MSW handlers return properly typed API responses:
```typescript
import { ApiSuccessResponse } from '@/types/api';
import { Flyer } from '@/types/flyer';
http.get('/api/v1/flyers', () => {
const response: ApiSuccessResponse<Flyer[]> = {
success: true,
data: [mockFlyer],
};
return HttpResponse.json(response);
});
```
### Generic Type Parameters
Provide explicit generics when TypeScript cannot infer:
```typescript
// Factory function generic
const mock = createMockPaginatedResponse<Flyer>({ data: [mockFlyer] });
// Assertion generic
expect(result).toEqual<ApiSuccessResponse<User>>({
success: true,
data: mockUser,
});
```
See [ADR-060](../adr/0060-typescript-test-error-remediation.md) for comprehensive patterns and remediation strategies.