> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/pingdotgg/t3code/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Write and run tests in the T3 Code monorepo

## Overview

T3 Code uses **Vitest** for all testing across the monorepo. The test suite includes unit tests, integration tests, and browser tests for React components.

<Warning>
  **NEVER** run `bun test`. Always use `bun run test` which properly runs Vitest.
</Warning>

## Running Tests

### All Tests

Run the entire test suite across all packages:

```bash theme={null}
bun run test
```

This uses Turbo to run tests in parallel across all workspace packages.

### Package-Specific Tests

<CodeGroup>
  ```bash Server Tests theme={null}
  cd apps/server
  bun run test
  ```

  ```bash Web Tests theme={null}
  cd apps/web
  bun run test
  ```

  ```bash Contracts Tests theme={null}
  cd packages/contracts
  bun run test
  ```
</CodeGroup>

### Watch Mode

Run tests in watch mode during development:

```bash theme={null}
# In any package directory
vitest

# Or with filter
vitest --watch --testNamePattern="session"
```

### Browser Tests (Web)

The web app includes browser-based component tests:

```bash theme={null}
cd apps/web

# Install Playwright (first time only)
bun run test:browser:install

# Run browser tests
bun run test:browser
```

## Test Structure

### Unit Tests

Most files have corresponding `.test.ts` files in the same directory:

```
apps/server/src/
├── main.ts
├── main.test.ts              # Unit tests for main.ts
├── wsServer.ts
├── wsServer.test.ts          # Unit tests for wsServer.ts
└── orchestration/
    ├── decider.ts
    └── decider.test.ts       # Unit tests for decider.ts
```

### Integration Tests

Integration tests live in dedicated directories:

```
apps/server/
├── src/                      # Source code
└── integration/              # Integration tests
    ├── providerService.integration.test.ts
    └── orchestrationEngine.integration.test.ts
```

### Test File Naming

| Pattern                 | Purpose           | Example                                   |
| ----------------------- | ----------------- | ----------------------------------------- |
| `*.test.ts`             | Unit tests        | `wsServer.test.ts`                        |
| `*.integration.test.ts` | Integration tests | `orchestrationEngine.integration.test.ts` |
| `*.logic.test.ts`       | Logic/state tests | `composer-logic.test.ts`                  |

## Writing Tests

### Basic Test Structure

T3 Code uses **Effect-TS** with Vitest, providing enhanced testing capabilities:

```typescript example.test.ts theme={null}
import { assert, it } from '@effect/vitest';
import * as Effect from 'effect/Effect';
import { describe } from 'vitest';

describe('MyFeature', () => {
  it.effect('should do something', () =>
    Effect.gen(function* () {
      const result = yield* myOperation();
      assert.strictEqual(result, 'expected');
    })
  );
});
```

### Testing with Effect Services

Use test layers to provide mock services:

```typescript service.test.ts theme={null}
import { assert, it } from '@effect/vitest';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import { vi } from 'vitest';

import { MyService } from './MyService';

// Create mock implementation
const mockFn = vi.fn(() => Effect.succeed('mocked'));

const TestMyServiceLive = Layer.succeed(MyService, {
  operation: mockFn,
});

it.effect('should call service', () =>
  Effect.gen(function* () {
    const service = yield* MyService;
    const result = yield* service.operation();
    
    assert.strictEqual(result, 'mocked');
    assert.strictEqual(mockFn).toHaveBeenCalledOnce();
  }).pipe(Effect.provide(TestMyServiceLive))
);
```

### Testing React Components

Web components use standard Vitest + React Testing Library patterns:

```typescript Component.test.tsx theme={null}
import { render, screen } from '@testing-library/react';
import { describe, expect, it } from 'vitest';

import { MyComponent } from './MyComponent';

describe('MyComponent', () => {
  it('renders correctly', () => {
    render(<MyComponent title="Test" />);
    expect(screen.getByText('Test')).toBeInTheDocument();
  });
});
```

### Testing WebSocket Communication

Use MSW (Mock Service Worker) for WebSocket mocking:

```typescript wsTransport.test.ts theme={null}
import { setupServer } from 'msw/node';
import { ws } from 'msw';
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';

import { WsTransport } from './wsTransport';

const chat = ws.link('ws://localhost:3773');
const server = setupServer();

beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());

describe('WsTransport', () => {
  it('connects and receives messages', async () => {
    server.use(
      chat.addEventListener('connection', ({ client }) => {
        client.send(JSON.stringify({ type: 'connected' }));
      })
    );
    
    const transport = new WsTransport();
    await transport.connect('ws://localhost:3773');
    
    const message = await transport.waitForMessage();
    expect(message.type).toBe('connected');
  });
});
```

## Test Utilities

### Effect Test Utilities

Common patterns for Effect-based tests:

```typescript theme={null}
import { assert, it } from '@effect/vitest';
import * as Effect from 'effect/Effect';
import * as Exit from 'effect/Exit';

// Test success case
it.effect('succeeds', () =>
  Effect.gen(function* () {
    const result = yield* operation();
    assert.strictEqual(result, expected);
  })
);

// Test failure case
it.effect('fails with error', () =>
  Effect.gen(function* () {
    const exit = yield* Effect.exit(failingOperation());
    assert(Exit.isFailure(exit));
    assert.strictEqual(exit.cause._tag, 'Fail');
  })
);

// Test with timeout
it.effect('completes within timeout', () =>
  Effect.gen(function* () {
    const result = yield* slowOperation().pipe(
      Effect.timeout('1 second')
    );
    assert.isDefined(result);
  })
);
```

### Mock Factories

Create reusable mock factories for common types:

```typescript testUtils.ts theme={null}
import type { ProviderConfig, SessionState } from '@t3tools/contracts';

export function createMockProviderConfig(
  overrides?: Partial<ProviderConfig>
): ProviderConfig {
  return {
    providerId: 'codex',
    model: 'claude-4.5-sonnet',
    workspaceRoot: '/tmp/test',
    ...overrides,
  };
}

export function createMockSessionState(
  overrides?: Partial<SessionState>
): SessionState {
  return {
    sessionId: 'test-session',
    status: 'idle',
    turns: [],
    ...overrides,
  };
}
```

### Test Fixtures

Store test data in `__fixtures__` directories:

```
packages/contracts/src/
├── provider.ts
├── provider.test.ts
└── __fixtures__/
    ├── valid-provider-config.json
    └── invalid-provider-config.json
```

```typescript provider.test.ts theme={null}
import { readFileSync } from 'node:fs';
import { describe, expect, it } from 'vitest';

const validConfig = JSON.parse(
  readFileSync('__fixtures__/valid-provider-config.json', 'utf-8')
);

describe('ProviderConfig', () => {
  it('parses valid config', () => {
    const result = ProviderConfig.decode(validConfig);
    expect(Effect.runSync(result)).toBeDefined();
  });
});
```

## Test Configuration

### Root `vitest.config.ts`

```typescript vitest.config.ts theme={null}
import * as path from 'node:path';
import { defineConfig } from 'vitest/config';

export default defineConfig({
  resolve: {
    alias: [
      {
        find: /^@t3tools\/contracts$/,
        replacement: path.resolve(
          import.meta.dirname,
          './packages/contracts/src/index.ts'
        ),
      },
    ],
  },
});
```

### Package-Specific Config

Packages can override the root config:

```typescript apps/web/vitest.browser.config.ts theme={null}
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    browser: {
      enabled: true,
      provider: 'playwright',
      name: 'chromium',
    },
  },
});
```

## Testing Patterns

### Testing Provider Adapters

```typescript CodexAdapter.test.ts theme={null}
import { assert, it } from '@effect/vitest';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import { describe, vi } from 'vitest';

import { CodexAdapter } from './CodexAdapter';

describe('CodexAdapter', () => {
  const mockProcess = {
    send: vi.fn(),
    on: vi.fn(),
    kill: vi.fn(),
  };
  
  const testLayer = Layer.succeed(CodexAdapter, {
    startSession: (config) =>
      Effect.succeed({ sessionId: 'test', process: mockProcess }),
    sendMessage: (sessionId, message) =>
      Effect.sync(() => mockProcess.send(message)),
  });
  
  it.effect('starts session', () =>
    Effect.gen(function* () {
      const adapter = yield* CodexAdapter;
      const session = yield* adapter.startSession({
        workspaceRoot: '/tmp/test',
      });
      
      assert.strictEqual(session.sessionId, 'test');
    }).pipe(Effect.provide(testLayer))
  );
});
```

### Testing Event Sourcing

```typescript OrchestrationEngine.test.ts theme={null}
import { assert, it } from '@effect/vitest';
import * as Effect from 'effect/Effect';
import { describe } from 'vitest';

import { OrchestrationEngine } from './OrchestrationEngine';
import { OrchestrationEventStore } from './OrchestrationEventStore';

describe('OrchestrationEngine', () => {
  it.effect('processes commands and stores events', () =>
    Effect.gen(function* () {
      const engine = yield* OrchestrationEngine;
      const store = yield* OrchestrationEventStore;
      
      // Execute command
      yield* engine.execute({
        type: 'StartTurn',
        sessionId: 'test',
        message: 'Hello',
      });
      
      // Verify events were stored
      const events = yield* store.getEvents('test');
      assert.isTrue(events.length > 0);
      assert.strictEqual(events[0].type, 'TurnStarted');
    }).pipe(Effect.provide(testLayer))
  );
});
```

### Testing WebSocket Protocol

```typescript wsServer.test.ts theme={null}
import { assert, it } from '@effect/vitest';
import * as Effect from 'effect/Effect';
import { describe } from 'vitest';
import WebSocket from 'ws';

import { Server } from './wsServer';

describe('WsServer', () => {
  it.effect('accepts connections and handles messages', () =>
    Effect.gen(function* () {
      const server = yield* Server;
      yield* server.start;
      
      // Create client
      const client = new WebSocket('ws://localhost:3773');
      yield* Effect.promise(() => new Promise(resolve => 
        client.on('open', resolve)
      ));
      
      // Send message
      client.send(JSON.stringify({
        type: 'SendUserMessage',
        message: 'test',
      }));
      
      // Verify response
      const response = yield* Effect.promise(() => new Promise(resolve =>
        client.on('message', data => resolve(JSON.parse(data.toString())))
      ));
      
      assert.isDefined(response);
      client.close();
    }).pipe(Effect.provide(testLayer))
  );
});
```

## Coverage

Generate coverage reports:

```bash theme={null}
vitest run --coverage
```

Coverage is configured in each package's `vitest.config.ts`:

```typescript theme={null}
export default defineConfig({
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
      exclude: [
        'node_modules/',
        'dist/',
        '**/*.test.ts',
        '**/__fixtures__/',
      ],
    },
  },
});
```

## CI Integration

Tests run automatically in CI:

```yaml .github/workflows/test.yml theme={null}
name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v1
        with:
          bun-version: 1.3.9
      - run: bun install
      - run: bun run test
      - run: bun run typecheck
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Test Behavior" icon="bullseye">
    Test behavior, not implementation details
  </Card>

  <Card title="Mock Layers" icon="layer-group">
    Use Effect layers for clean dependency injection in tests
  </Card>

  <Card title="Descriptive Names" icon="tag">
    Use clear, descriptive test names that explain intent
  </Card>

  <Card title="Fast Tests" icon="bolt">
    Keep unit tests fast; use integration tests for E2E flows
  </Card>
</CardGroup>

<Note>
  Both `bun lint` and `bun typecheck` must pass before tasks are considered complete.
</Note>

## Debugging Tests

### Debug Individual Tests

```bash theme={null}
# Run single test file
vitest run src/main.test.ts

# Run with debugger
node --inspect-brk node_modules/vitest/vitest.mjs run src/main.test.ts
```

### VS Code Debugging

Add to `.vscode/launch.json`:

```json theme={null}
{
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Current Test",
      "autoAttachChildProcesses": true,
      "skipFiles": ["<node_internals>/**", "**/node_modules/**"],
      "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
      "args": ["run", "${relativeFile}"],
      "smartStep": true,
      "console": "integratedTerminal"
    }
  ]
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Building" icon="hammer" href="/development/building">
    Learn how to build for production
  </Card>

  <Card title="Architecture" icon="diagram-project" href="/development/architecture-deep-dive">
    Review the system architecture
  </Card>
</CardGroup>
