# Tests — Accessible Video Processing Platform

<!-- SCOPE: tests | owner: ln-140 | generated: 2026-04-29 -->

## Test Commands

### Backend

| Command | Description |
|---------|-------------|
| `cd backend && poetry run pytest` | Run all unit tests |
| `cd backend && poetry run pytest -v` | Verbose output |
| `cd backend && poetry run pytest tests/unit/test_security.py` | Single file |
| `cd backend && poetry run pytest -k "test_jwt"` | Keyword filter |
| `docker compose exec api python -m pytest` | Tests inside Docker container |
| `docker compose exec api python -m pytest --cov=app` | With coverage report |

### Frontend

| Command | Description |
|---------|-------------|
| `cd frontend && npm run test` | Vitest unit tests (watch mode) |
| `cd frontend && npm run test:run` | Vitest single run |
| `cd frontend && npm run test:coverage` | Coverage report |
| `cd frontend && npm run test:e2e` | Playwright E2E tests |
| `cd frontend && npx playwright test --ui` | Playwright UI mode |

### Lint and Type Check (must pass before commit)

| Command | Description |
|---------|-------------|
| `cd backend && ruff check .` | Python linting |
| `cd backend && poetry run mypy app/` | Python type checking |
| `cd frontend && npm run lint` | ESLint |
| `cd frontend && npm run type-check` | TypeScript compile check |

---

## Test Structure

### Backend (`backend/tests/`)

| Directory | Purpose | Framework |
|-----------|---------|-----------|
| `tests/unit/` | Business logic unit tests | pytest |
| `tests/fixtures/` | VTT and JSON test fixtures | — |
| `tests/integration/` | FastAPI TestClient route tests | pytest (does not exist yet) |
| `conftest.py` | Shared fixtures | pytest (does not exist yet) |

### Frontend (`frontend/src/` and `frontend/tests/`)

| Directory | Purpose | Framework |
|-----------|---------|-----------|
| `src/**/__tests__/` | Component unit tests | Vitest + RTL |
| `src/hooks/__tests__/` | Hook tests | Vitest + RTL |
| `src/lib/__tests__/` | Utility tests | Vitest |
| `src/test/utils.tsx` | Shared test utilities | RTL |
| `tests/e2e/` | End-to-end specs | Playwright |
| `tests/helpers/auth.ts` | Auth fixture (exists, not yet wired) | Playwright |

---

## Coverage Targets

| Layer | Target | Current |
|-------|--------|---------|
| Backend unit | 80% line coverage on services/ | ~3% (critically low) |
| Frontend unit | 70% branch coverage on hooks/ | ~12% |
| E2E | All happy paths for QC workflow | 0% (tests skipped) |

---

## Writing New Tests

### Backend Unit Test Pattern

```python
# Use AsyncMock for async service methods
from unittest.mock import AsyncMock, patch

async def test_something():
    with patch('app.services.gemini.settings') as mock_settings:
        mock_settings.gemini_api_key = "test-key"
        # test body
```

Wait — **move the settings mock to a shared fixture in conftest.py**. See anti-patterns in [testing-strategy.md](../docs/reference/guides/testing-strategy.md).

### Frontend Hook Test Pattern

Use `renderHook` from `@testing-library/react` wrapped with `test/utils.tsx` providers.

### E2E Auth Pattern

Use `tests/helpers/auth.ts` in `beforeEach`:

```typescript
// Wire auth fixture before un-skipping any test
test.beforeEach(async ({ page }) => {
  await loginAs(page, 'reviewer');
});
```

---

## Known Issues

| Issue | Impact | Fix needed |
|-------|--------|-----------|
| `conftest.py` missing | All tests define fixtures inline | Create shared conftest with MockSettings, mock_db |
| E2E tests mostly skipped | Zero E2E coverage | Implement auth + seed fixtures |
| `MagicMock` used for async services | May silently pass on sync mocks | Replace with `AsyncMock` |
| Hardcoded `test-job-123` in E2E | Tests would fail if un-skipped | Use seed fixtures |

---

## Maintenance

**Update triggers:** New test file added, test framework version changed, new command available.
**Verification:** All commands in the Commands section execute without error on a clean checkout.

<!-- END SCOPE: tests -->
