--- name: testing description: Use when writing or running tests for this project. Covers unit vs E2E test decisions, test file locations, mock patterns, and project-specific testing conventions. (project) --- # Testing in Sandbox SDK This skill covers project-specific testing conventions. For TDD methodology, use the `superpowers:test-driven-development` skill. ## Two Test Suites ### Unit Tests **When to use**: Testing isolated logic, client behavior, service methods, utilities. **Location**: - SDK: `packages/sandbox/tests/` - Container: `packages/sandbox-container/tests/` **Runtime**: - SDK tests run in Workers runtime via `@cloudflare/vitest-pool-workers` - Container tests run in Bun runtime **Commands**: ```bash npm test # All unit tests npm test -w @cloudflare/sandbox # SDK tests only npm test -w @repo/sandbox-container # Container tests only ``` **Mock patterns**: - SDK tests use a mock container (no Docker needed) - Container tests mock external dependencies (filesystem, processes) - Use `createNoOpLogger()` from `@repo/shared` for logger mocks **Known issue**: SDK unit tests may hang on exit due to vitest-pool-workers workerd shutdown. Tests still pass/fail correctly - the hang is cosmetic. ### E2E Tests **When to use**: Testing full request flow, container integration, real Docker behavior. **Location**: `tests/e2e/` **Runtime**: Real Cloudflare Workers + Docker containers **Commands**: ```bash npm run test:e2e # All E2E tests (vitest + browser) npm run test:e2e:vitest -- -- tests/e2e/process-lifecycle-workflow.test.ts # Single vitest file npm run test:e2e:vitest -- -- tests/e2e/git-clone-workflow.test.ts -t 'test name' # Single vitest test npm run test:e2e:browser # Browser tests only (Playwright) ``` **Note**: Use `test:e2e:vitest` when filtering tests. The `test:e2e` wrapper doesn't support argument passthrough. **Key patterns**: - All tests share ONE container for performance - Use unique sessions for test isolation - Tests run in parallel via thread pool - Config: `vitest.e2e.config.ts` (root level) **Writing E2E tests**: ```typescript import { createTestSession } from './helpers'; describe('Feature X', () => { let session: TestSession; beforeEach(async () => { session = await createTestSession(); // Gets unique session }); it('should do something', async () => { const result = await session.sandbox.exec('echo hello'); expect(result.stdout).toBe('hello\n'); }); }); ``` ## When to Use Which | Scenario | Test Type | | --------------------------------------- | --------- | | Client method logic | Unit | | Service business logic | Unit | | Request/response handling | Unit | | Full command execution flow | E2E | | File operations with real filesystem | E2E | | Process lifecycle (start, stop, signal) | E2E | | Port exposure and preview URLs | E2E | | Git operations | E2E | ## Test-Specific Conventions **File naming**: `*.test.ts` for both unit and E2E tests **Test structure**: ```typescript describe('ComponentName', () => { describe('methodName', () => { it('should do X when Y', async () => { // Arrange // Act // Assert }); }); }); ``` **Assertions**: Use vitest's `expect()` with clear, specific assertions ## Running Tests During Development After making any meaningful code change: 1. `npm run check` - catch type errors first 2. `npm test` - verify unit tests pass 3. `npm run test:e2e` - if touching core functionality **Build trust**: The monorepo build system handles dependencies automatically. E2E tests always run against latest built code - no manual rebuild needed.