ARC // OS — Testing Requirements
This system controls daily life. Testing is not optional. It is mandatory.
Core Principles
- No code without tests — Every feature must have tests before it's considered complete
- Tests must pass — All tests green before merge, no exceptions
- Coverage is enforced — 90% minimum, 100% for critical paths
- Tests are documentation — Tests show how code should work
- 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:
- Be independent — No shared state between tests
- Clean up — Reset database, clear mocks
- Be deterministic — Same input = same output, always
- Be fast — Unit tests < 100ms, integration < 1s
- Have clear name — Name describes what it tests
- Test one thing — One assertion per test (usually)
- Be readable — Clear setup, obvious assertions
- Fail fast — Use explicit timeouts in
waitForcalls (max 5 seconds) - 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
- Default test run must be one-time execution - Use
--runflag to avoid watch mode - Short timeouts - Tests should fail quickly if something is wrong
- No hanging promises - All async operations must have timeouts
- Fast feedback - Tests should complete in seconds, not minutes
Configuration
- Default
pnpm testcommand 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
waitForcalls have explicit timeouts - Mock cleanup - Mocks are cleared between tests to prevent state leakage
Common Issues That Cause Hangs
- Missing
--runflag - Vitest defaults to watch mode which waits for file changes - Long or missing timeouts - Tests waiting indefinitely for conditions that never occur
- Unresolved promises - Async operations without proper cleanup or timeouts
- Database connections not closing - Pooled connections left open
- Event listeners not removed - Memory leaks causing slowdowns
Best Practices
- Always use
waitForwith 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
--runflag 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:
- Does this test actually verify the behavior?
- What happens if this test fails? Is it clear?
- Are edge cases covered?
- Are error cases covered?
- Is the test maintainable?
- Could this test be flaky?
- 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