docs: map existing codebase
This commit is contained in:
340
.planning/codebase/TESTING.md
Normal file
340
.planning/codebase/TESTING.md
Normal file
@@ -0,0 +1,340 @@
|
||||
# Testing Patterns
|
||||
|
||||
**Analysis Date:** 2026-04-27
|
||||
|
||||
## Test Framework Status
|
||||
|
||||
**Current State:** **Minimal test coverage - testing infrastructure in place but not actively used**
|
||||
|
||||
### Jest (Server)
|
||||
|
||||
**Runner:**
|
||||
- Jest 30.x
|
||||
- Config: Inline in `server/package.json` (lines 88-104)
|
||||
|
||||
**Run Commands:**
|
||||
```bash
|
||||
cd server
|
||||
npm test # Run all unit tests (seeks *.spec.ts files)
|
||||
npm run test:watch # Watch mode for development
|
||||
npm run test:cov # Generate coverage report
|
||||
npm run test:debug # Debug mode with breakpoints
|
||||
npm run test:e2e # Run end-to-end tests (config: test/jest-e2e.json)
|
||||
```
|
||||
|
||||
**Configuration Details:**
|
||||
```json
|
||||
{
|
||||
"moduleFileExtensions": ["js", "json", "ts"],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": { "^.+\\.(t|j)s$": "ts-jest" },
|
||||
"collectCoverageFrom": ["**/*.(t|j)s"],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
```
|
||||
|
||||
**Testing Libraries:**
|
||||
- Jest 30.x (test runner)
|
||||
- ts-jest (TypeScript support)
|
||||
- @nestjs/testing (NestJS module testing utilities)
|
||||
- supertest (HTTP request testing)
|
||||
|
||||
### Vitest (Client)
|
||||
|
||||
**Status:** Not installed. No vitest config present in `client/package.json`
|
||||
|
||||
**Implications:** Client has zero automated test infrastructure. Any future testing would require adding vitest and configuring it.
|
||||
|
||||
## Test File Organization
|
||||
|
||||
**Server:**
|
||||
- Location: Unit tests alongside source in `src/`, E2E tests in `test/`
|
||||
- Naming: `*.spec.ts` for unit tests, `*.e2e-spec.ts` for integration tests
|
||||
- Example: No unit test files currently exist in `server/src/`
|
||||
|
||||
**Current Test Suite:**
|
||||
- Location: `server/test/app.e2e-spec.ts`
|
||||
- Scope: Single basic integration test for app startup
|
||||
- Status: Placeholder test (checks 'Hello World!' endpoint that doesn't exist)
|
||||
|
||||
**Client:**
|
||||
- No test files present
|
||||
- No test directory structure
|
||||
|
||||
## Test Structure (Server)
|
||||
|
||||
**E2E Test Example** (`server/test/app.e2e-spec.ts`):
|
||||
```typescript
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleFixture.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('/ (GET)', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.expect('Hello World!');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Patterns:**
|
||||
- `describe()` for test suites
|
||||
- `it()` for individual tests (or `test()`)
|
||||
- `beforeEach()` for setup (module initialization)
|
||||
- Async/await for async operations
|
||||
- Chained `.expect()` assertions with supertest
|
||||
|
||||
## Mocking
|
||||
|
||||
### Server (Jest/NestJS)
|
||||
|
||||
**Framework:** @nestjs/testing provides test module builder
|
||||
|
||||
**Pattern Observed:**
|
||||
```typescript
|
||||
const moduleFixture: TestingModule = await Test.createTestingModule({
|
||||
imports: [AppModule], // Or specific module imports
|
||||
}).compile();
|
||||
|
||||
const service = moduleFixture.get<ServiceName>(ServiceName);
|
||||
```
|
||||
|
||||
**What to Mock:**
|
||||
- Database (Prisma) - would use mock provider
|
||||
- External APIs - would use jest.mock()
|
||||
- Services with dependencies - inject via module builder
|
||||
|
||||
**What NOT to Mock:**
|
||||
- NestJS core decorators
|
||||
- Guards/Middleware (test through integration)
|
||||
- Database schema (use test database)
|
||||
|
||||
### Client
|
||||
|
||||
**No mocking patterns established yet** - testing infrastructure not present.
|
||||
|
||||
Would require:
|
||||
- Vitest for runner
|
||||
- `@testing-library/react` for component testing
|
||||
- Mock fetch/axios calls
|
||||
- Mock WebSocket connections
|
||||
|
||||
## Fixtures and Factories
|
||||
|
||||
**Server:**
|
||||
|
||||
**Test Data Approach:** Not fully established, but patterns available:
|
||||
- Prisma seed scripts exist in `server/prisma/`:
|
||||
- `seed.ts` - Basic seed
|
||||
- `seed-equipment.ts` - 5,482 equipment items
|
||||
- `seed-feats.ts` - Feat database
|
||||
- Could be reused for test fixtures
|
||||
|
||||
**Location:** Would be in test directory or integrated with jest setup
|
||||
|
||||
**No factory pattern observed** - tests would need to build objects inline or create helper functions
|
||||
|
||||
**Client:**
|
||||
|
||||
**Not applicable** - no test infrastructure
|
||||
|
||||
## Coverage
|
||||
|
||||
**Requirements:** None enforced (no CI/CD pipeline checking coverage)
|
||||
|
||||
**View Coverage:**
|
||||
```bash
|
||||
cd server
|
||||
npm run test:cov
|
||||
# Generates: server/coverage/
|
||||
```
|
||||
|
||||
**Current Coverage:** Unknown (no tests running)
|
||||
|
||||
**Gaps:** **Entire codebase untested**
|
||||
- Auth module: No unit tests (`server/src/modules/auth/`)
|
||||
- Characters module: No unit tests (`server/src/modules/characters/`)
|
||||
- Battle module: No unit tests (`server/src/modules/battle/`)
|
||||
- WebSocket gateway: No tests
|
||||
- Guards/Decorators: No tests
|
||||
- Client components: No tests
|
||||
- Client hooks: No tests
|
||||
- State management (Zustand): No tests
|
||||
|
||||
## Test Types
|
||||
|
||||
### Unit Tests
|
||||
|
||||
**Not currently used**
|
||||
|
||||
**When implemented, pattern would be:**
|
||||
- Test individual services in isolation
|
||||
- Mock Prisma client
|
||||
- Location: `server/src/modules/[module]/[module].service.spec.ts`
|
||||
- Example test structure (not yet used):
|
||||
```typescript
|
||||
describe('AuthService', () => {
|
||||
let service: AuthService;
|
||||
let prisma: PrismaService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [AuthService, { provide: PrismaService, useValue: mockPrisma }],
|
||||
}).compile();
|
||||
|
||||
service = module.get<AuthService>(AuthService);
|
||||
});
|
||||
|
||||
it('should register user', async () => {
|
||||
// Test implementation
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Integration Tests
|
||||
|
||||
**Single example:** `server/test/app.e2e-spec.ts`
|
||||
|
||||
**Scope:**
|
||||
- Tests full HTTP request/response cycle
|
||||
- Uses real app instance
|
||||
- Uses Test.createTestingModule() to create app
|
||||
|
||||
**Pattern:**
|
||||
```typescript
|
||||
return request(app.getHttpServer())
|
||||
.post('/auth/login')
|
||||
.send({ identifier: 'user', password: 'pass' })
|
||||
.expect(200)
|
||||
.expect((res) => {
|
||||
expect(res.body.token).toBeDefined();
|
||||
});
|
||||
```
|
||||
|
||||
**Would need:** Database test fixtures, cleanup between tests
|
||||
|
||||
### E2E Tests
|
||||
|
||||
**Config:** `server/test/jest-e2e.json`
|
||||
|
||||
**Difference from Integration:**
|
||||
- Run against deployed/separate server
|
||||
- Test user workflows end-to-end
|
||||
- Full database setup/teardown
|
||||
|
||||
**Currently:** Not implemented beyond placeholder
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Async Testing
|
||||
|
||||
**Server (Jest):**
|
||||
```typescript
|
||||
it('should load data', async () => {
|
||||
const result = await service.loadData();
|
||||
expect(result).toBeDefined();
|
||||
});
|
||||
```
|
||||
|
||||
- async/await preferred
|
||||
- Supertest chainable: `request().post().send().expect()`
|
||||
|
||||
**Client (not yet implemented):**
|
||||
Would use:
|
||||
```typescript
|
||||
const { result } = await renderHook(() => useCharacterSocket());
|
||||
await waitFor(() => expect(result.current.socket).toBeDefined());
|
||||
```
|
||||
|
||||
### Error Testing
|
||||
|
||||
**Not yet implemented, but pattern would be:**
|
||||
|
||||
**Server:**
|
||||
```typescript
|
||||
it('should throw NotFoundException', async () => {
|
||||
await expect(service.getCharacter('invalid-id'))
|
||||
.rejects
|
||||
.toThrow(NotFoundException);
|
||||
});
|
||||
```
|
||||
|
||||
**Actual exception handling verified through:**
|
||||
- Controllers catch exceptions
|
||||
- Guards handle auth failures
|
||||
- Integration tests verify HTTP status codes
|
||||
|
||||
## Critical Test Gaps
|
||||
|
||||
**High Priority (Authentication):**
|
||||
- AuthService.register() - conflicts, hashing, JWT generation
|
||||
- AuthService.login() - invalid credentials, token generation
|
||||
- JWT guard - token validation, public routes
|
||||
- File: `server/src/modules/auth/auth.service.ts` (lines 20-104)
|
||||
|
||||
**High Priority (Character Management):**
|
||||
- CharactersService access control (checkCampaignAccess, checkCharacterAccess)
|
||||
- HP updates and state sync via WebSocket
|
||||
- Condition application (dying, wounded, doomed mechanics)
|
||||
- Item management (equip, invest, notes)
|
||||
- File: `server/src/modules/characters/characters.service.ts`
|
||||
|
||||
**High Priority (Real-time Sync):**
|
||||
- CharactersGateway connection authentication
|
||||
- WebSocket message broadcasting
|
||||
- Concurrent client updates
|
||||
- File: `server/src/modules/characters/characters.gateway.ts`
|
||||
|
||||
**Medium Priority (Client UI):**
|
||||
- HpControl component (damage, heal, direct modes)
|
||||
- AddConditionModal (search, filtering, validation)
|
||||
- Character import from Pathbuilder JSON
|
||||
- File: `client/src/features/characters/components/`
|
||||
|
||||
**Medium Priority (Validation):**
|
||||
- DTO validation (CreateCharacterDto, LoginDto)
|
||||
- Prisma query results
|
||||
- File: `server/src/modules/*/dto/`
|
||||
|
||||
## Recommendations for Implementation
|
||||
|
||||
**Phase 1 (Auth):**
|
||||
1. Write AuthService unit tests with mocked Prisma
|
||||
2. Write JwtAuthGuard tests
|
||||
3. Write integration tests for login/register endpoints
|
||||
|
||||
**Phase 2 (Character CRUD):**
|
||||
1. Test CharactersService access control
|
||||
2. Test HP update logic
|
||||
3. Test condition application
|
||||
|
||||
**Phase 3 (WebSocket):**
|
||||
1. Mock socket.io for gateway tests
|
||||
2. Test connection/disconnect
|
||||
3. Test broadcast patterns
|
||||
|
||||
**Phase 4 (Client):**
|
||||
1. Setup vitest
|
||||
2. Test HpControl component
|
||||
3. Test modal components
|
||||
4. Test Zustand store
|
||||
|
||||
---
|
||||
|
||||
*Testing analysis: 2026-04-27*
|
||||
Reference in New Issue
Block a user