271 lines
7.1 KiB
Markdown
271 lines
7.1 KiB
Markdown
# Contract Testing Documentation
|
|
|
|
## Overview
|
|
|
|
This document describes the contract testing strategy for ensuring compatibility between the API (`apps/api`) and the website (`apps/website`) in the GridPilot monorepo.
|
|
|
|
## Architecture
|
|
|
|
The contract testing system consists of several layers:
|
|
|
|
### 1. API Contract Validation
|
|
- **Location**: `apps/api/src/shared/testing/contractValidation.test.ts`
|
|
- **Purpose**: Validates that API DTOs are consistent and generate valid OpenAPI specs
|
|
- **Tests**:
|
|
- OpenAPI spec integrity
|
|
- DTO consistency
|
|
- Type generation integrity
|
|
- Contract compatibility
|
|
|
|
### 2. Type Generation
|
|
- **Scripts**:
|
|
- `npm run api:generate-spec` - Generates OpenAPI spec from DTOs
|
|
- `npm run api:generate-types` - Generates TypeScript types for website
|
|
- `npm run api:sync-types` - Runs both in sequence
|
|
- **Output**: `apps/website/lib/types/generated/`
|
|
|
|
### 3. Website Contract Consumption
|
|
- **Location**: `apps/website/lib/types/contractConsumption.test.ts`
|
|
- **Purpose**: Validates that website can properly consume generated types
|
|
- **Tests**:
|
|
- Generated types availability
|
|
- Type compatibility
|
|
- Service integration
|
|
- Error handling
|
|
|
|
### 4. Compatibility Verification
|
|
- **Location**: `scripts/contract-compatibility.ts`
|
|
- **Purpose**: Detects breaking changes between type versions
|
|
- **Features**:
|
|
- Backup current types
|
|
- Generate new types
|
|
- Compare and detect changes
|
|
- Report breaking changes
|
|
|
|
## Workflow
|
|
|
|
### Local Development
|
|
|
|
```bash
|
|
# Run full contract testing suite
|
|
npm run test:contracts
|
|
|
|
# Or run individual steps:
|
|
npm run test:api:contracts # Validate API contracts
|
|
npm run api:generate-spec # Generate OpenAPI spec
|
|
npm run api:generate-types # Generate types
|
|
npm run test:contract:compatibility # Check compatibility
|
|
npm run website:type-check # Verify website types
|
|
```
|
|
|
|
### CI/CD Pipeline
|
|
|
|
The GitHub Actions workflow (`.github/workflows/contract-testing.yml`) automatically:
|
|
|
|
1. **On Pull Requests**:
|
|
- Runs all contract tests
|
|
- Validates API contracts
|
|
- Generates types
|
|
- Checks for breaking changes
|
|
- Verifies website type checking
|
|
- Uploads generated types as artifacts
|
|
- Comments results on PR
|
|
|
|
2. **On Main Branch Push**:
|
|
- Runs all tests
|
|
- Generates and commits updated types
|
|
|
|
## Breaking Change Detection
|
|
|
|
The system detects several types of changes:
|
|
|
|
### Breaking Changes (❌ Fails CI)
|
|
- **Property Removal**: Required properties removed from DTOs
|
|
- **Type Changes**: Property types changed (e.g., `string` → `number`)
|
|
- **Required Field Addition**: New required fields added to existing DTOs
|
|
|
|
### Non-Breaking Changes (⚠️ Warning)
|
|
- **Property Addition**: Optional properties added
|
|
- **New DTOs**: New DTO types added
|
|
- **Documentation Changes**: Description updates
|
|
|
|
### Removed Changes (❌ Fails CI)
|
|
- **DTO Removal**: Entire DTO types removed
|
|
- **Property Removal**: Optional properties removed
|
|
|
|
## Generated Types Structure
|
|
|
|
The generated types follow this pattern:
|
|
|
|
```typescript
|
|
/**
|
|
* Auto-generated DTO from OpenAPI spec
|
|
* This file is generated by scripts/generate-api-types.ts
|
|
* Do not edit manually - regenerate using: npm run api:sync-types
|
|
*/
|
|
|
|
import type { RelatedDTO } from './RelatedDTO';
|
|
|
|
export interface MyDTO {
|
|
id: string;
|
|
name: string;
|
|
optionalField?: string;
|
|
related?: RelatedDTO;
|
|
}
|
|
```
|
|
|
|
## Testing Strategy
|
|
|
|
### API Layer Tests
|
|
|
|
```typescript
|
|
// apps/api/src/shared/testing/contractValidation.test.ts
|
|
describe('API Contract Validation', () => {
|
|
it('should have valid OpenAPI spec', async () => {
|
|
// Validates spec structure
|
|
});
|
|
|
|
it('should have no circular references', async () => {
|
|
// Prevents infinite loops
|
|
});
|
|
|
|
it('should maintain backward compatibility', async () => {
|
|
// Ensures critical DTOs exist
|
|
});
|
|
});
|
|
```
|
|
|
|
### Website Layer Tests
|
|
|
|
```typescript
|
|
// apps/website/lib/types/contractConsumption.test.ts
|
|
describe('Website Contract Consumption', () => {
|
|
it('should allow creating valid DTO objects', () => {
|
|
// Type-safe object creation
|
|
});
|
|
|
|
it('should work with service layer patterns', () => {
|
|
// Integration with services
|
|
});
|
|
|
|
it('should handle error cases', () => {
|
|
// Error handling patterns
|
|
});
|
|
});
|
|
```
|
|
|
|
## Integration Points
|
|
|
|
### Service Layer Integration
|
|
|
|
Services in the website can import and use generated types:
|
|
|
|
```typescript
|
|
import type { RequestAvatarGenerationInputDTO } from '@/lib/types/generated/RequestAvatarGenerationInputDTO';
|
|
import type { RequestAvatarGenerationOutputDTO } from '@/lib/types/generated/RequestAvatarGenerationOutputDTO';
|
|
|
|
class AvatarService {
|
|
async generateAvatar(input: RequestAvatarGenerationInputDTO): Promise<RequestAvatarGenerationOutputDTO> {
|
|
const response = await apiClient.post('/avatar/generate', input);
|
|
return response.data; // Type-safe
|
|
}
|
|
}
|
|
```
|
|
|
|
### View Model Integration
|
|
|
|
View models can transform DTOs for UI consumption:
|
|
|
|
```typescript
|
|
import type { RaceDTO } from '@/lib/types/generated/RaceDTO';
|
|
|
|
class RaceViewModel {
|
|
constructor(private dto: RaceDTO) {}
|
|
|
|
get displayDate(): string {
|
|
return new Date(this.dto.startTime).toLocaleDateString();
|
|
}
|
|
}
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **Type Generation Fails**
|
|
- Check that DTOs have proper `@ApiProperty` decorators
|
|
- Verify OpenAPI spec is valid JSON
|
|
- Ensure all referenced types exist
|
|
|
|
2. **Breaking Changes Detected**
|
|
- Review the change report
|
|
- Update website code to handle new types
|
|
- Consider versioning strategy for major changes
|
|
|
|
3. **Website Type Errors**
|
|
- Run `npm run api:sync-types` to regenerate
|
|
- Check import paths in website code
|
|
- Verify TypeScript configuration
|
|
|
|
### Debugging Steps
|
|
|
|
1. **Check OpenAPI Spec**:
|
|
```bash
|
|
npm run api:generate-spec
|
|
cat apps/api/openapi.json | jq '.components.schemas'
|
|
```
|
|
|
|
2. **Compare Generated Types**:
|
|
```bash
|
|
# Backup current types
|
|
cp -r apps/website/lib/types/generated /tmp/types-backup
|
|
|
|
# Regenerate
|
|
npm run api:generate-types
|
|
|
|
# Compare
|
|
diff -r /tmp/types-backup apps/website/lib/types/generated
|
|
```
|
|
|
|
3. **Run Individual Tests**:
|
|
```bash
|
|
# API tests
|
|
npm run test:api:contracts
|
|
|
|
# Website type checking
|
|
npm run website:type-check
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### For API Developers
|
|
|
|
1. **Always use `@ApiProperty` decorators** with proper types
|
|
2. **Mark optional fields explicitly** with `required: false`
|
|
3. **Use proper TypeScript types** (avoid `any`)
|
|
4. **Add descriptions** to DTO properties
|
|
5. **Test DTOs** with contract validation tests
|
|
|
|
### For Website Developers
|
|
|
|
1. **Import from generated types**, not manual types
|
|
2. **Use type assertions** when consuming API responses
|
|
3. **Handle optional fields** properly
|
|
4. **Run contract tests** before committing type changes
|
|
5. **Update view models** when DTOs change
|
|
|
|
### For CI/CD
|
|
|
|
1. **Run contract tests on every PR**
|
|
2. **Block merges on breaking changes**
|
|
3. **Generate types on main branch pushes**
|
|
4. **Upload artifacts for debugging**
|
|
5. **Comment results on PRs**
|
|
|
|
## Future Enhancements
|
|
|
|
- [ ] Add API versioning support
|
|
- [ ] Generate client SDKs from OpenAPI
|
|
- [ ] Add contract testing for WebSocket events
|
|
- [ ] Implement schema registry
|
|
- [ ] Add automated migration scripts for breaking changes |