Skip to main content

ARC // OS — Testing Requirements

This system controls daily life. Testing is not optional. It is mandatory.

Core Principles​

  1. No code without tests — Every feature must have tests before it's considered complete
  2. Tests must pass — All tests green before merge, no exceptions
  3. Coverage is enforced — 90% minimum, 100% for critical paths
  4. Tests are documentation — Tests show how code should work
  5. Tests prevent regressions — If it broke once, it must be tested

Test Categories​

1. Unit Tests (Service Layer)​

What: Test individual services and business logic in isolation

Requirements:

  • Every service method must have tests
  • Test happy paths
  • Test error cases
  • Test edge cases
  • Test business rules (validation, constraints)
  • Mock external dependencies (database, APIs)

Example:

describe('TaskService', () => {
it('should require nextStep when deferring task', async () => {
// Test business rule
});

it('should reject invalid timebox values', async () => {
// Test validation
});
});

2. Integration Tests (API Layer)​

What: Test API endpoints with real database

Requirements:

  • Every API endpoint must have tests
  • Use real database (test database)
  • Test authentication
  • Test authorization (user ownership)
  • Test request/response formats
  • Test error responses
  • Clean up after each test

Example:

describe('POST /api/tasks', () => {
it('should create task for authenticated user', async () => {
// Test with real DB
});

it('should reject task without required fields', async () => {
// Test validation
});
});

3. Component Tests (Frontend)​

What: Test React components in isolation

Requirements:

  • Every component must have tests
  • Test rendering
  • Test user interactions
  • Test props handling
  • Test error states
  • Test loading states

Example:

describe('Button', () => {
it('should call onClick when clicked', () => {
// Test interaction
});

it('should be disabled when disabled prop is true', () => {
// Test state
});
});

4. Page Tests (Frontend)​

What: Test full page components and flows

Requirements:

  • Every page must have tests
  • Test data loading
  • Test navigation
  • Test form submissions
  • Test error handling
  • Mock API calls

Example:

describe('DayPage', () => {
it('should load day data on mount', async () => {
// Test data loading
});

it('should toggle checklist item', async () => {
// Test interaction
});
});

5. E2E Tests (Critical Flows)​

What: Test complete user journeys end-to-end

Requirements:

  • Critical flows must have E2E tests
  • Use real browser (Playwright/Cypress)
  • Test full user journey
  • Test error recovery
  • Test data persistence

Example:

describe('Complete Day Workflow', () => {
it('should allow user to view and update today', async () => {
// Full flow: login → view day → update → verify
});
});

Coverage Requirements​

Minimum Coverage: 90%​

  • Services: 95%+ (business logic is critical)
  • Routes: 90%+ (all endpoints tested)
  • Components: 85%+ (UI can have some edge cases)
  • Utilities: 100% (small, critical functions)

Critical Paths: 100% Coverage​

These must have 100% coverage:

  • Authentication (login, register, token validation)
  • Data validation (all zod schemas)
  • Business rules (deferral requires nextStep, etc.)
  • User ownership checks
  • Error handling paths
  • Date normalization logic

Test Quality Standards​

Every Test Must:​

  1. Be independent — No shared state between tests
  2. Clean up — Reset database, clear mocks
  3. Be deterministic — Same input = same output, always
  4. Be fast — Unit tests < 100ms, integration < 1s
  5. Have clear name — Name describes what it tests
  6. Test one thing — One assertion per test (usually)
  7. Be readable — Clear setup, obvious assertions
  8. Fail fast — Use explicit timeouts in waitFor calls (max 5 seconds)
  9. No hanging — All async operations must have timeouts or cleanup

Test Structure​

describe('ServiceName', () => {
beforeEach(() => {
// Setup: clean database, create test data
});

afterEach(() => {
// Teardown: clean up
});

describe('methodName', () => {
it('should do X when Y', async () => {
// Arrange: set up test data
// Act: call method
// Assert: verify result
});

it('should handle error case Z', async () => {
// Test error handling
});
});
});

Important: When using waitFor in tests, always specify a timeout to prevent hanging:

await waitFor(() => {
expect(screen.getByText('Expected Text')).toBeDefined();
}, { timeout: 5000 }); // 5 seconds max - fail fast

CI/CD Requirements​

Pre-Commit​

  • Run tests
  • Run type check
  • Run linter
  • Block if any fail

Pull Request​

  • All tests must pass
  • Coverage must not decrease
  • New code must have tests
  • Type checking passes
  • Linting passes

Deployment​

  • All tests green
  • Coverage threshold met
  • E2E tests pass
  • Performance acceptable

Test Database​

  • Separate database for tests
  • Automatic cleanup between tests
  • Migrations applied before test suite
  • Seed data for common scenarios
  • Isolated from development database

Running Tests​

# Backend
cd server
pnpm test # Run all tests (one-time run, fast, fail early)
pnpm test:watch # Watch mode (for development)
pnpm test:coverage # With coverage (90% threshold enforced)
pnpm test WeekService # Specific test

# Frontend
cd web
pnpm test # Run all tests (one-time run, fast, fail early)
pnpm test:watch # Watch mode (for development)
pnpm test:coverage # With coverage (90% threshold enforced)
pnpm test Button # Specific test

⚠️ IMPORTANT: This project uses pnpm exclusively. Always use pnpm commands, never yarn or npm.

Test Performance & Avoiding Hangs​

⚠️ CRITICAL: Tests must run fast and fail early. Never use long timeouts or watch mode by default.

Performance Requirements​

  1. Default test run must be one-time execution - Use --run flag to avoid watch mode
  2. Short timeouts - Tests should fail quickly if something is wrong
  3. No hanging promises - All async operations must have timeouts
  4. Fast feedback - Tests should complete in seconds, not minutes

Configuration​

  • Default pnpm test command runs tests once and exits (not watch mode)
  • Test timeouts are set to reasonable limits (5-10 seconds max per test)
  • No infinite waits - All waitFor calls have explicit timeouts
  • Mock cleanup - Mocks are cleared between tests to prevent state leakage

Common Issues That Cause Hangs​

  1. Missing --run flag - Vitest defaults to watch mode which waits for file changes
  2. Long or missing timeouts - Tests waiting indefinitely for conditions that never occur
  3. Unresolved promises - Async operations without proper cleanup or timeouts
  4. Database connections not closing - Pooled connections left open
  5. Event listeners not removed - Memory leaks causing slowdowns

Best Practices​

  • Always use waitFor with explicit timeout: waitFor(() => {...}, { timeout: 5000 })
  • Clean up timers, intervals, and event listeners in afterEach
  • Use vi.useFakeTimers() for time-dependent tests
  • Mock external APIs to avoid network delays
  • Close database connections and clear test data after each test
  • Use --run flag explicitly if needed: vitest --run

Coverage Measurement​

Coverage thresholds are enforced at 90% minimum for:

  • Lines
  • Functions
  • Branches
  • Statements

Coverage Configuration:

  • Server: server/vitest.config.ts - 90% thresholds enforced
  • Web: web/vitest.config.ts - 90% thresholds enforced

Coverage Reports:

  • Text summary in terminal
  • JSON report: coverage/coverage-final.json
  • HTML report: coverage/index.html (open in browser for detailed view)

If coverage drops below 90%, tests will fail. This prevents merging code that decreases coverage.

Viewing Coverage:

# Backend
cd server
pnpm test:coverage
# Open coverage/index.html in browser

# Frontend
cd web
pnpm test:coverage
# Open coverage/index.html in browser

Test Checklist​

Before marking any task complete:

  • Unit tests written
  • Integration tests written (if API endpoint)
  • Component tests written (if UI component)
  • All tests passing
  • Coverage threshold met
  • Edge cases tested
  • Error cases tested
  • Business rules tested
  • User ownership verified
  • Tests reviewed
  • No flaky tests

Red Flags (Block Merging)​

  • ❌ New code without tests
  • ❌ Tests failing
  • ❌ Coverage decreased
  • ❌ Flaky tests
  • ❌ Tests that don't actually test anything
  • ❌ Tests that are too slow
  • ❌ Tests with shared state

Test Review Questions​

When reviewing tests, ask:

  1. Does this test actually verify the behavior?
  2. What happens if this test fails? Is it clear?
  3. Are edge cases covered?
  4. Are error cases covered?
  5. Is the test maintainable?
  6. Could this test be flaky?
  7. Is the test fast enough?

For detailed test review process, see TEST_REVIEW_PROCESS.md

Remember​

This system controls daily life. A bug could mean:

  • Lost workout data
  • Missed tasks
  • Broken planning
  • Data corruption
  • Wasted time

Tests are not overhead. They are insurance. Every test is a safety net. Every untested line is a risk.


Test Review Process​

For comprehensive test review guidelines, see TEST_REVIEW_PROCESS.md

Last Updated: 2026-01-18
Enforcement: Mandatory — No exceptions