Overview
LatentGEO uses multiple testing frameworks:
Backend : pytest for unit, integration, and E2E tests
Frontend : Vitest for unit and component tests
E2E : Playwright for end-to-end browser tests
Quick Validation
Run this before committing or creating a PR:
Install dependencies
# Backend
python -m pip install -r backend/requirements.txt
# Frontend
pnpm --dir frontend install
Run fast validation suite
# Frontend checks
pnpm --dir frontend lint
pnpm --dir frontend run format:check
pnpm --dir frontend run type-check
pnpm --dir frontend test:ci
pnpm --dir frontend build
# Backend checks
python -m ruff check backend/app backend/tests
python -m black --check backend
python -m isort --check-only backend
python -m mypy backend/app --ignore-missing-imports --show-error-codes
python -m bandit -r backend/app -q
python -m pip_audit -r backend/requirements.txt
# Fast tests (exclude integration and live tests)
pytest -q backend/tests -m "not integration and not live"
All commands should exit with status 0 for the validation to pass.
Backend Testing
Running Pytest
All Tests
Specific Tests
By Marker
# Run all tests
pytest backend/tests/
# Run with coverage
pytest backend/tests/ --cov=app --cov-report=html
Test Organization
Tests are organized by component:
backend/tests/
├── test_audit_service.py # Audit logic tests
├── test_pdf_locking.py # PDF security tests
├── test_security_remediation.py # Auth and ownership tests
├── test_sse_auth_contract.py # SSE authentication
├── test_pipeline_service.py # Pipeline orchestration
├── test_github_fix_inputs_api.py # GitHub integration
└── test_release_smoke_external.py # External smoke tests
Test Markers
Tests use pytest markers to categorize:
@pytest.mark.unit
def test_fast_unit ():
pass
@pytest.mark.integration
def test_requires_db ():
pass
@pytest.mark.live
def test_requires_external_api ():
pass
Security Regression Suite
After changing auth, ownership, or security code:
pytest -q backend/tests/test_security_remediation.py
pytest -q backend/tests/test_pdf_locking.py
pytest -q backend/tests/test_ssl_fallback_flags.py
pytest -q backend/tests/test_github_fix_inputs_api.py
pytest -q backend/tests/test_additional_remediation.py
pytest -q backend/tests/test_security_middleware.py
pytest -q backend/tests/test_rate_limit_middleware.py
Docker Testing
Run tests inside containers:
# Start services
docker compose up -d db redis backend worker
# Execute tests in backend container
docker compose exec backend pytest -q tests/
# Specific test files
docker compose exec backend pytest -q tests/test_competitor_queries.py tests/test_pdf_fast_mode.py
Inside the container, the project root is /app, so use tests/... paths (not backend/tests/).
Frontend Testing
Vitest (Unit & Component Tests)
# Watch mode (development)
pnpm --dir frontend test
# CI mode (single run)
pnpm --dir frontend test:ci
# With coverage
pnpm --dir frontend test:ci --coverage
Test Structure
import { describe , it , expect } from 'vitest' ;
import { render , screen } from '@testing-library/react' ;
import { Button } from '@/components/ui/button' ;
describe ( 'Button' , () => {
it ( 'renders with text' , () => {
render (< Button > Click me </ Button > );
expect ( screen . getByText ( 'Click me' )). toBeInTheDocument ();
});
});
Playwright (E2E Tests)
End-to-end browser tests:
# Install browsers (first time)
pnpm --dir frontend run perf:e2e:install
# Run E2E tests
pnpm --dir frontend run perf:e2e
# Run in headed mode (see browser)
pnpm --dir frontend run perf:e2e --headed
# Run specific test
pnpm --dir frontend run perf:e2e e2e/geo-performance.spec.ts
API Contract Sync
When backend routes or schemas change, regenerate frontend types:
Start backend server
docker compose -f docker-compose.dev.yml up backend
Generate types from OpenAPI
pnpm --dir frontend run api:generate-types
This updates:
frontend/lib/api-client/openapi.json
frontend/lib/api-client/schema.ts
Verify types compile
pnpm --dir frontend run type-check
Always regenerate types after modifying FastAPI route signatures or Pydantic schemas.
Backend Linting
Ruff (Linter)
Black (Formatter)
isort (Import Sorting)
mypy (Type Checking)
# Check for issues
python -m ruff check backend/app backend/tests
# Auto-fix safe issues
python -m ruff check --fix backend/app backend/tests
Security Scanning
# Bandit (security linter)
python -m bandit -r backend/app -q
# pip-audit (dependency vulnerabilities)
python -m pip_audit -r backend/requirements.txt
Frontend Linting
# Check for issues
pnpm --dir frontend lint
# Auto-fix
pnpm --dir frontend lint --fix
External Smoke Tests
Optional pre-release validation against staging/production:
SMOKE_BASE_URL = https://your-staging-url \
SMOKE_BEARER_TOKEN=optional-token \
pytest -q backend/tests/test_release_smoke_external.py
Docker Validation
Validate Docker Compose configuration:
# Validate compose files
docker compose config
docker compose -f docker-compose.dev.yml config
# Verify required env vars
DB_PASSWORD = docker compose config # Should fail
DB_PASSWORD = test docker compose config # Should succeed
# Check runtime user in images
docker build -f Dockerfile.backend -t latentgeo-backend .
docker run --rm latentgeo-backend id
CI/CD Pipeline
The release gate validates:
Linting : ruff, black, isort, ESLint
Type checking : mypy, TypeScript
Security : bandit, pip-audit
Unit tests : pytest (fast), vitest
Build : Frontend production build
All checks must pass before merging to main.
Test Coverage
Generate coverage reports:
# Backend coverage
pytest backend/tests/ --cov=app --cov-report=html
open htmlcov/index.html
# Frontend coverage
pnpm --dir frontend test:ci --coverage
open frontend/coverage/index.html
Next Steps
Contributing Guide Learn the workflow and commit conventions
Backend Services Understand service layer architecture