# Testing Strategy - Maternal App **Target Coverage:** 80%+ across all layers **Testing Pyramid:** Unit (70%) → Integration (20%) → E2E (10%) --- ## 1. Backend Testing (NestJS) ### 1.1 Unit Tests (Target: 70% of tests) **Tools:** Jest, @nestjs/testing **What to Test:** - **Services** (Business Logic) - ComplianceService (data export, account deletion) - AuthService (registration with COPPA validation, login, token management) - ChildrenService (CRUD operations) - TrackingService (activity creation, daily summary) - AIService (chat, conversation memory) - VoiceService (intent classification, entity extraction) - EmbeddingsService (vector search, semantic memory) - **Guards** - JwtAuthGuard - Public decorator - **Pipes** - ValidationPipe (DTO validation) - **Utilities** - Date calculations - Age verification (COPPA compliance) - Token generation **Example Test Structure:** ```typescript describe('ComplianceService', () => { let service: ComplianceService; let userRepository: Repository; beforeEach(async () => { const module = await Test.createTestingModule({ providers: [ ComplianceService, { provide: getRepositoryToken(User), useClass: Repository }, ], }).compile(); service = module.get(ComplianceService); }); it('should export user data with all entities', async () => { // Test implementation }); it('should schedule account deletion with 30-day grace period', async () => { // Test implementation }); }); ``` ### 1.2 Integration Tests (Target: 20% of tests) **Tools:** Jest, Supertest, @nestjs/testing **What to Test:** - **API Endpoints** (Controller + Service + Database) - POST /api/v1/compliance/data-export - POST /api/v1/compliance/request-deletion - POST /api/v1/auth/register (with COPPA validation) - POST /api/v1/auth/login - POST /api/v1/activities (tracking) - POST /api/v1/ai/chat - **Authentication Flows** - Register → Login → Protected Route - Register with COPPA (age 13-17) - Register blocked (age < 13) - **Database Operations** - CRUD operations with real database - Transaction rollbacks - Cascade deletions **Example Test Structure:** ```typescript describe('ComplianceController (e2e)', () => { let app: INestApplication; let authToken: string; beforeAll(async () => { const moduleFixture = await Test.createTestingModule({ imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); await app.init(); }); it('/api/v1/compliance/data-export (GET)', () => { return request(app.getHttpServer()) .get('/api/v1/compliance/data-export') .set('Authorization', `Bearer ${authToken}`) .expect(200) .expect((res) => { expect(res.body.data).toHaveProperty('user'); expect(res.body.data).toHaveProperty('children'); expect(res.body.data).toHaveProperty('activities'); }); }); }); ``` --- ## 2. Frontend Testing (Next.js + React) ### 2.1 Unit Tests (Component Testing) **Tools:** Jest, React Testing Library, @testing-library/user-event **What to Test:** - **Components** - DataExport component (button click, download trigger) - AccountDeletion component (dialog flow, deletion request) - Registration form (COPPA age validation, form submission) - Settings page (profile update, compliance sections) - Tracking forms (feeding, sleep, diaper) - **Redux Slices** - authSlice (login, logout, token refresh) - childrenSlice (CRUD operations) - activitiesSlice (create, update, delete) - offlineSlice (sync queue, conflict resolution) - **API Clients** - complianceApi (all methods) - authApi, childrenApi, trackingApi - **Utilities** - Date formatting - Age calculation - Token storage **Example Test Structure:** ```typescript import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { DataExport } from '@/components/settings/DataExport'; import { complianceApi } from '@/lib/api/compliance'; jest.mock('@/lib/api/compliance'); describe('DataExport Component', () => { it('should download user data when button is clicked', async () => { const mockDownload = jest.fn(); (complianceApi.downloadUserData as jest.Mock) = mockDownload; render(); const button = screen.getByText('Download My Data'); fireEvent.click(button); await waitFor(() => { expect(mockDownload).toHaveBeenCalledTimes(1); }); expect(screen.getByText('Your data has been downloaded successfully!')).toBeInTheDocument(); }); it('should show error message on download failure', async () => { (complianceApi.downloadUserData as jest.Mock).mockRejectedValue( new Error('Network error') ); render(); fireEvent.click(screen.getByText('Download My Data')); await waitFor(() => { expect(screen.getByText(/Failed to export data/)).toBeInTheDocument(); }); }); }); ``` ### 2.2 Integration Tests (API Interaction) **What to Test:** - Form submission with API calls - Authentication flows - Error handling and retry logic - Optimistic updates with Redux --- ## 3. End-to-End Tests (Target: 10% of tests) **Tools:** Playwright **Critical User Journeys:** ### 3.1 Registration & COPPA Compliance ```typescript test('User under 13 cannot register', async ({ page }) => { await page.goto('/register'); await page.fill('[name="name"]', 'John Doe'); await page.fill('[name="email"]', 'john@example.com'); await page.fill('[name="dateOfBirth"]', '2015-01-01'); // 9 years old await page.fill('[name="password"]', 'SecurePass123'); await page.fill('[name="confirmPassword"]', 'SecurePass123'); await page.check('[name="agreeToTerms"]'); await page.check('[name="agreeToPrivacy"]'); await page.click('button[type="submit"]'); await expect(page.locator('text=/Users under 13/')).toBeVisible(); }); test('User 13-17 requires parental consent', async ({ page }) => { await page.goto('/register'); await page.fill('[name="dateOfBirth"]', '2010-01-01'); // 14 years old // Parental email field should appear await expect(page.locator('[name="parentalEmail"]')).toBeVisible(); await expect(page.locator('text=/parental consent/')).toBeVisible(); }); ``` ### 3.2 Account Deletion Flow ```typescript test('User can request account deletion and cancel it', async ({ page }) => { // Login await login(page, 'user@example.com', 'password'); // Navigate to settings await page.goto('/settings'); // Request deletion await page.click('text=Delete My Account'); await page.fill('[name="reason"]', 'Testing deletion flow'); await page.click('button:has-text("Delete Account")'); // Verify deletion scheduled await expect(page.locator('text=/Account deletion scheduled/')).toBeVisible(); // Cancel deletion await page.click('text=Cancel Deletion Request'); await page.click('button:has-text("Cancel Deletion")'); // Verify cancellation await expect(page.locator('text=/cancelled successfully/')).toBeVisible(); }); ``` ### 3.3 Data Export Flow ```typescript test('User can export their data', async ({ page }) => { await login(page, 'user@example.com', 'password'); await page.goto('/settings'); const [download] = await Promise.all([ page.waitForEvent('download'), page.click('text=Download My Data') ]); expect(download.suggestedFilename()).toMatch(/maternal-app-data-export.*\.json/); }); ``` ### 3.4 Activity Tracking Flow ```typescript test('User can track feeding activity', async ({ page }) => { await login(page, 'user@example.com', 'password'); await page.goto('/track/feeding'); await page.selectOption('[name="childId"]', { index: 0 }); await page.fill('[name="amount"]', '120'); await page.selectOption('[name="unit"]', 'ml'); await page.click('button:has-text("Save")'); await expect(page.locator('text=/Activity saved/')).toBeVisible(); // Verify on dashboard await page.goto('/'); await expect(page.locator('text=/Feeding/')).toBeVisible(); }); ``` --- ## 4. Code Coverage Goals ### 4.1 Overall Targets - **Total Coverage:** 80%+ - **Statements:** 80%+ - **Branches:** 75%+ - **Functions:** 80%+ - **Lines:** 80%+ ### 4.2 Module-Specific Targets **Backend:** - Auth Module: 90%+ (critical security) - Compliance Module: 95%+ (legal requirement) - Tracking Module: 85%+ - AI Module: 80%+ - Voice Module: 80%+ **Frontend:** - Compliance Components: 95%+ - Auth Components: 90%+ - Tracking Components: 85%+ - API Clients: 90%+ - Redux Slices: 85%+ --- ## 5. CI/CD Integration ### 5.1 GitHub Actions Workflow **Triggers:** - Push to main branch - Pull request to main - Scheduled nightly runs **Steps:** 1. Checkout code 2. Setup Node.js (v20) 3. Install dependencies 4. Run linter (ESLint) 5. Run backend unit tests 6. Run backend integration tests 7. Run frontend tests 8. Run E2E tests 9. Generate coverage reports 10. Upload coverage to Codecov 11. Fail PR if coverage drops below 80% **Example `.github/workflows/test.yml`:** ```yaml name: Test Suite on: push: branches: [main] pull_request: branches: [main] jobs: backend-tests: runs-on: ubuntu-latest services: postgres: image: postgres:15 env: POSTGRES_PASSWORD: test options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: '20' - run: npm ci - run: npm test -- --coverage - uses: codecov/codecov-action@v3 frontend-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 - run: npm ci - run: npm test -- --coverage e2e-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 - run: npx playwright install --with-deps - run: npm run test:e2e ``` --- ## 6. Test Data Management ### 6.1 Test Database - Use separate test database - Seed with test fixtures - Clean between tests ### 6.2 Test Fixtures - Create factory functions for test data - Predefined users, children, activities - COPPA-compliant test scenarios (various ages) **Example:** ```typescript export const testUsers = { adult: { name: 'Adult User', email: 'adult@example.com', dateOfBirth: '1990-01-01', }, minor14: { name: 'Teen User', email: 'teen@example.com', dateOfBirth: '2010-01-01', parentalEmail: 'parent@example.com', coppaConsentGiven: true, }, child12: { name: 'Child User', email: 'child@example.com', dateOfBirth: '2012-01-01', // Should be blocked }, }; ``` --- ## 7. Testing Best Practices ### 7.1 General Principles - **AAA Pattern:** Arrange, Act, Assert - **DRY:** Don't Repeat Yourself (use factories and helpers) - **Fast:** Keep unit tests under 100ms - **Isolated:** No dependencies between tests - **Deterministic:** Same input = same output ### 7.2 Mocking Strategy - Mock external services (Azure OpenAI, Mailgun, MinIO) - Mock time-dependent functions - Use in-memory database for unit tests - Real database for integration tests ### 7.3 Naming Conventions ```typescript describe('ServiceName', () => { describe('methodName', () => { it('should do something when condition', () => { // Test }); it('should throw error when invalid input', () => { // Test }); }); }); ``` --- ## 8. Continuous Improvement ### 8.1 Metrics to Track - Code coverage percentage - Test execution time - Flaky test rate - Bug escape rate ### 8.2 Review Process - All PRs must have tests - Coverage must not decrease - CI must pass before merge --- ## 9. Implementation Timeline **Week 1:** - ✅ Setup Jest for backend and frontend - ✅ Create first unit tests (Compliance, Auth) - ✅ Setup test database **Week 2:** - ✅ Create integration tests for critical endpoints - ✅ Setup Playwright for E2E - ✅ Create E2E tests for COPPA flows **Week 3:** - ✅ Expand coverage to 50%+ - ✅ Setup CI/CD pipeline - ✅ Configure coverage reporting **Week 4:** - ✅ Reach 80%+ coverage - ✅ Optimize test performance - ✅ Documentation and training --- ## 10. Quick Start Commands ```bash # Backend cd maternal-app-backend npm test # Run all tests npm test -- --coverage # With coverage npm test -- --watch # Watch mode npm run test:e2e # Integration tests # Frontend cd maternal-web npm test # Run all tests npm test -- --coverage # With coverage npm test -- --watch # Watch mode npm run test:e2e # E2E with Playwright # Coverage Reports npm test -- --coverage --coverageReporters=html # Open coverage/index.html in browser ``` --- ## Status: IN PROGRESS **Current Coverage:** 0% **Target Coverage:** 80%+ **Estimated Completion:** 3-4 weeks