Files
cim_summary/.planning/codebase/TESTING.md
2026-02-24 10:28:22 -05:00

10 KiB

Testing Patterns

Analysis Date: 2026-02-24

Test Framework

Runner:

  • Vitest 2.1.0
  • Config: No dedicated vitest.config.ts found (uses defaults)
  • Node.js test environment

Assertion Library:

  • Vitest native assertions via expect()
  • Examples: expect(value).toBe(), expect(value).toBeDefined(), expect(array).toContain()

Run Commands:

npm test                    # Run all tests once
npm run test:watch         # Watch mode for continuous testing
npm run test:coverage      # Generate coverage report

Coverage Tool:

  • @vitest/coverage-v8 2.1.0
  • Tracks line, branch, function, and statement coverage
  • V8 backend for accurate coverage metrics

Test File Organization

Location:

  • Co-located in backend/src/__tests__/ directory
  • Subdirectories for logical grouping:
    • backend/src/__tests__/utils/ - Utility function tests
    • backend/src/__tests__/mocks/ - Mock implementations
    • backend/src/__tests__/acceptance/ - Acceptance/integration tests

Naming:

  • Pattern: [feature].test.ts or [feature].spec.ts
  • Examples:
    • backend/src/__tests__/financial-summary.test.ts
    • backend/src/__tests__/acceptance/handiFoods.acceptance.test.ts

Structure:

backend/src/__tests__/
├── utils/
│   └── test-helpers.ts              # Test utility functions
├── mocks/
│   └── logger.mock.ts               # Mock implementations
└── acceptance/
    └── handiFoods.acceptance.test.ts # Acceptance tests

Test Structure

Suite Organization:

import { describe, test, expect, beforeAll } from 'vitest';

describe('Feature Category', () => {
  describe('Nested Behavior Group', () => {
    test('should do specific thing', () => {
      expect(result).toBe(expected);
    });

    test('should handle edge case', () => {
      expect(edge).toBeDefined();
    });
  });
});

From financial-summary.test.ts:

describe('Financial Summary Fixes', () => {
  describe('Period Ordering', () => {
    test('Summary table should display periods in chronological order (FY3 → FY2 → FY1 → LTM)', () => {
      const periods = ['fy3', 'fy2', 'fy1', 'ltm'];
      const expectedOrder = ['FY3', 'FY2', 'FY1', 'LTM'];

      expect(periods[0]).toBe('fy3');
      expect(periods[3]).toBe('ltm');
    });
  });
});

Patterns:

  1. Setup Pattern:

    • Use beforeAll() for shared test data initialization
    • Example from handiFoods.acceptance.test.ts:
      beforeAll(() => {
        const normalize = (text: string) => text.replace(/\s+/g, ' ').toLowerCase();
        const cimRaw = fs.readFileSync(cimTextPath, 'utf-8');
        const outputRaw = fs.readFileSync(outputTextPath, 'utf-8');
        cimNormalized = normalize(cimRaw);
        outputNormalized = normalize(outputRaw);
      });
      
  2. Teardown Pattern:

    • Not explicitly shown in current tests
    • Use afterAll() for resource cleanup if needed
  3. Assertion Pattern:

    • Descriptive test names that read as sentences: 'should display periods in chronological order'
    • Multiple assertions per test acceptable for related checks
    • Use expect().toContain() for array/string membership
    • Use expect().toBeDefined() for existence checks
    • Use expect().toBeGreaterThan() for numeric comparisons

Mocking

Framework: Vitest vi mock utilities

Patterns:

  1. Mock Logger:

    import { vi } from 'vitest';
    
    export const mockLogger = {
      debug: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
    };
    
    export const mockStructuredLogger = {
      uploadStart: vi.fn(),
      uploadSuccess: vi.fn(),
      uploadError: vi.fn(),
      processingStart: vi.fn(),
      processingSuccess: vi.fn(),
      processingError: vi.fn(),
      storageOperation: vi.fn(),
      jobQueueOperation: vi.fn(),
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
      debug: vi.fn(),
    };
    
  2. Mock Service Pattern:

    • Create mock implementations in backend/src/__tests__/mocks/
    • Export as named exports: export const mockLogger, export const mockStructuredLogger
    • Use vi.fn() for all callable methods to track calls and arguments
  3. What to Mock:

    • External services: Firebase Auth, Supabase, Google Cloud APIs
    • Logger: always mock to prevent log spam during tests
    • File system operations (in unit tests; use real files in acceptance tests)
    • LLM API calls: mock responses to avoid quota usage
  4. What NOT to Mock:

    • Core utility functions: use real implementations
    • Type definitions: no need to mock types
    • Pure functions: test directly without mocks
    • Business logic calculations: test with real data

Fixtures and Factories

Test Data:

  1. Helper Factory Pattern: From backend/src/__tests__/utils/test-helpers.ts:

    export function createMockCorrelationId(): string {
      return `test-correlation-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }
    
    export function createMockUserId(): string {
      return `test-user-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }
    
    export function createMockDocumentId(): string {
      return `test-doc-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }
    
    export function createMockJobId(): string {
      return `test-job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
    }
    
    export function wait(ms: number): Promise<void> {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }
    
  2. Acceptance Test Fixtures:

    • Located in backend/test-fixtures/ directory
    • Example: backend/test-fixtures/handiFoods/ contains:
      • handi-foods-cim.txt - Reference CIM content
      • handi-foods-output.txt - Expected processor output
    • Loaded via fs.readFileSync() in beforeAll() hooks

Location:

  • Test helpers: backend/src/__tests__/utils/test-helpers.ts
  • Acceptance fixtures: backend/test-fixtures/ (outside src)
  • Mocks: backend/src/__tests__/mocks/

Coverage

Requirements:

  • No automated coverage enforcement detected (no threshold in config)
  • Manual review recommended for critical paths

View Coverage:

npm run test:coverage

Test Types

Unit Tests:

  • Scope: Individual functions, services, utilities
  • Approach: Test in isolation with mocks for dependencies
  • Examples:
    • Financial parser tests: parse tables with various formats
    • Period ordering tests: verify chronological order logic
    • Validate UUID format tests: regex pattern matching
  • Location: backend/src/__tests__/[feature].test.ts

Integration Tests:

  • Scope: Multiple components working together
  • Approach: May use real Supabase/Firebase or mocks depending on test level
  • Not heavily used: minimal integration test infrastructure
  • Pattern: Could use real database in test environment with cleanup

Acceptance Tests:

  • Scope: End-to-end feature validation with real artifacts
  • Approach: Load reference files, process through entire pipeline, verify output
  • Example: handiFoods.acceptance.test.ts
    • Loads CIM text file
    • Loads processor output file
    • Validates all reference facts exist in both
    • Validates key fields resolved instead of fallback messages
  • Location: backend/src/__tests__/acceptance/

E2E Tests:

  • Not implemented in current setup
  • Would require browser automation (no Playwright/Cypress config found)
  • Frontend testing: not currently automated

Common Patterns

Async Testing:

test('should process document asynchronously', async () => {
  const result = await processDocument(documentId, userId, text);
  expect(result.success).toBe(true);
});

Error Testing:

test('should validate UUID format', () => {
  const id = 'invalid-id';
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  expect(uuidRegex.test(id)).toBe(false);
});

Array/Collection Testing:

test('should extract all financial periods', () => {
  const result = parseFinancialsFromText(tableText);
  expect(result.data.fy3.revenue).toBeDefined();
  expect(result.data.fy2.revenue).toBeDefined();
  expect(result.data.fy1.revenue).toBeDefined();
  expect(result.data.ltm.revenue).toBeDefined();
});

Text/Content Testing (Acceptance):

test('verifies each reference fact exists in CIM and generated output', () => {
  for (const fact of referenceFacts) {
    for (const token of fact.tokens) {
      expect(cimNormalized).toContain(token);
      expect(outputNormalized).toContain(token);
    }
  }
});

Normalization for Content Testing:

// Normalize whitespace and case for robust text matching
const normalize = (text: string) => text.replace(/\s+/g, ' ').toLowerCase();
const normalizedCIM = normalize(cimRaw);
expect(normalizedCIM).toContain('reference-phrase');

Test Coverage Priorities

Critical Paths (Test First):

  1. Document upload and file storage operations
  2. Firebase authentication and token validation
  3. LLM service API interactions with retry logic
  4. Error handling and correlation ID tracking
  5. Financial data extraction and parsing
  6. PDF generation pipeline

Important Paths (Test Early):

  1. Vector embeddings and database operations
  2. Job queue processing and timeout handling
  3. Google Document AI text extraction
  4. Supabase Row Level Security policies

Nice-to-Have (Test Later):

  1. UI component rendering (would require React Testing Library)
  2. CSS/styling validation
  3. Frontend form submission flows
  4. Analytics tracking

Current Testing Gaps

Untested Areas:

  • Backend services: Most services lack unit tests (llmService, fileStorageService, etc.)
  • Database models: No model tests for Supabase operations
  • Controllers/Endpoints: No API endpoint tests
  • Frontend components: No React component tests
  • Integration flows: Document upload through processing to PDF generation

Missing Patterns:

  • No database integration test setup (fixtures, transactions)
  • No API request/response validation tests
  • No performance/load tests
  • No security tests (auth bypass, XSS, injection)

Deprecated Test Patterns (DO NOT USE)

  • Jest test suite - Use Vitest instead
  • Direct PostgreSQL connection tests - Use Supabase in test mode
  • Legacy test files referencing removed services - Updated implementations used only

Testing analysis: 2026-02-24