A community based topic aggregation platform built on atproto

Coves Testing Guide#

This document explains how testing works in Coves, including setup, running tests, and understanding the test infrastructure.

Overview#

Coves uses a unified testing approach with:

  • Single configuration file: .env.dev for all environments (dev + test)
  • Isolated test database: PostgreSQL on port 5434 (separate from dev on 5433)
  • Makefile commands: Simple make test command handles everything
  • Docker Compose profiles: Test database spins up automatically

Quick Start#

# Run all tests (starts test DB, runs migrations, executes tests)
make test

# Reset test database (clean slate)
make test-db-reset

# Stop test database
make test-db-stop

Test Infrastructure#

Configuration (.env.dev)#

All test configuration lives in .env.dev:

# Test Database Configuration
POSTGRES_TEST_DB=coves_test
POSTGRES_TEST_USER=test_user
POSTGRES_TEST_PASSWORD=test_password
POSTGRES_TEST_PORT=5434

No separate .env.test file needed! Everything is in .env.dev.

Test Database#

The test database runs in Docker via docker-compose.dev.yml:

  • Service: postgres-test (profile: test)
  • Port: 5434 (separate from dev database on 5433)
  • Automatic startup: The Makefile handles starting/stopping
  • Isolated data: Completely separate from development database

Running Tests Manually#

If you need to run tests without the Makefile:

# 1. Load environment variables
set -a && source .env.dev && set +a

# 2. Start test database
docker-compose -f docker-compose.dev.yml --env-file .env.dev --profile test up -d postgres-test

# 3. Wait for it to be ready
sleep 3

# 4. Run migrations
goose -dir internal/db/migrations postgres \
  "postgresql://$POSTGRES_TEST_USER:$POSTGRES_TEST_PASSWORD@localhost:$POSTGRES_TEST_PORT/$POSTGRES_TEST_DB?sslmode=disable" up

# 5. Run tests
go test ./... -v

# 6. Stop test database (optional)
docker-compose -f docker-compose.dev.yml --env-file .env.dev --profile test stop postgres-test

Note: The Makefile automatically loads .env.dev variables, so make test is simpler than running manually.

Test Types#

1. Unit Tests#

Test individual components in isolation.

Note: Unit tests will be added as needed. Currently focusing on integration tests.

# Run unit tests for a specific package (when available)
go test -v ./internal/core/users/...

2. Integration Tests#

Test full request/response flows with a real database.

Location: tests/integration/

How they work:

  • Start test database
  • Run migrations
  • Execute HTTP requests against real handlers
  • Verify responses

Example: tests/integration/integration_test.go

# Run integration tests
make test
# or
go test -v ./tests/integration/...

3. Lexicon Validation Tests#

Validate AT Protocol Lexicon schemas and test data.

Components:

Running validation:

# Full validation (schemas + test data)
go run cmd/validate-lexicon/main.go

# Schemas only (skip test data)
go run cmd/validate-lexicon/main.go --schemas-only

# Verbose output
go run cmd/validate-lexicon/main.go -v

# Strict mode
go run cmd/validate-lexicon/main.go --strict

Test data naming convention:

  • *-valid*.json - Should pass validation
  • *-invalid-*.json - Should fail validation (tests error detection)

Current coverage (as of last update):

  • ✅ social.coves.actor.profile
  • ✅ social.coves.community.profile
  • ✅ social.coves.post.record
  • ✅ social.coves.interaction.vote
  • ✅ social.coves.moderation.ban

Database Migrations#

Migrations are managed with goose and stored in internal/db/migrations/.

Running Migrations#

# Development database
make db-migrate

# Test database (automatically run by `make test`)
make test-db-reset

Creating Migrations#

# Create a new migration
goose -dir internal/db/migrations create migration_name sql

# This creates:
# internal/db/migrations/YYYYMMDDHHMMSS_migration_name.sql

Migration Best Practices#

  • Always test migrations on test database first
  • Write both Up and Down migrations
  • Keep migrations atomic - one logical change per migration
  • Test rollback - verify the Down migration works
  • Don't modify old migrations - create new ones instead

Test Database Management#

Fresh Start#

# Complete reset (deletes all data)
make test-db-reset

Connecting to Test Database#

# Using psql
PGPASSWORD=test_password psql -h localhost -p 5434 -U test_user -d coves_test

# Using docker exec
docker exec -it coves-test-postgres psql -U test_user -d coves_test

Inspecting Test Data#

-- List all tables
\dt

-- View table schema
\d table_name

-- Query data
SELECT * FROM users;

-- Check migrations
SELECT * FROM goose_db_version;

Writing Tests#

Integration Test Template#

package integration

import (
	"testing"
	// ... imports
)

func TestYourFeature(t *testing.T) {
	db := setupTestDB(t)
	defer db.Close()

	// Wire up dependencies
	repo := postgres.NewYourRepository(db)
	service := yourpackage.NewYourService(repo)

	// Create test router
	r := chi.NewRouter()
	r.Mount("/api/path", routes.YourRoutes(service))

	// Make request
	req := httptest.NewRequest("GET", "/api/path", nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)

	// Assert
	if w.Code != http.StatusOK {
		t.Errorf("Expected 200, got %d", w.Code)
	}
}

Test Helper: setupTestDB#

The setupTestDB function (in tests/integration/integration_test.go):

  • Reads config from environment variables (set by .env.dev)
  • Connects to test database
  • Runs migrations
  • Cleans up test data
  • Returns ready-to-use *sql.DB

Common Test Commands#

# Run all tests
make test

# Run specific test package
go test -v ./internal/core/users/...

# Run specific test
go test -v ./tests/integration/... -run TestCreateUser

# Run with coverage
go test -v -cover ./...

# Run with race detector
go test -v -race ./...

# Verbose output
go test -v ./...

# Clean and run
make test-db-reset && make test

Troubleshooting#

Test database won't start#

# Check if port 5434 is in use
lsof -i :5434

# Check container logs
docker logs coves-test-postgres

# Nuclear reset
docker-compose -f docker-compose.dev.yml --profile test down -v
make test-db-reset

Migrations failing#

# Load environment and check migration status
set -a && source .env.dev && set +a
goose -dir internal/db/migrations postgres \
  "postgresql://$POSTGRES_TEST_USER:$POSTGRES_TEST_PASSWORD@localhost:$POSTGRES_TEST_PORT/$POSTGRES_TEST_DB?sslmode=disable" status

# Reset and retry
make test-db-reset

Tests can't connect to database#

# Verify test database is running
docker ps | grep coves-test-postgres

# Verify environment variables
set -a && source .env.dev && set +a && echo "Test DB Port: $POSTGRES_TEST_PORT"

# Test connection manually
PGPASSWORD=test_password psql -h localhost -p 5434 -U test_user -d coves_test -c "SELECT 1"

Lexicon validation errors#

# Check schema syntax
cat internal/atproto/lexicon/path/to/schema.json | jq .

# Validate specific schema
go run cmd/validate-lexicon/main.go -v

# Check test data format
cat tests/lexicon-test-data/your-test.json | jq .

Best Practices#

Test Organization#

  • ✅ Unit tests live next to the code they test (*_test.go)
  • ✅ Integration tests live in tests/integration/
  • ✅ Test data lives in tests/lexicon-test-data/
  • ✅ One test file per feature/endpoint

Test Data#

  • ✅ Use .test handles for test users (e.g., alice.test) (auto-cleaned by setupTestDB)
  • ✅ Clean up data in tests (or rely on setupTestDB cleanup)
  • ✅ Don't rely on specific test execution order
  • ✅ Each test should be independent

Database Tests#

  • ✅ Always use the test database (port 5434)
  • ✅ Never connect to development database (port 5433) in tests
  • ✅ Use transactions for fast test cleanup (where applicable)
  • ✅ Test with realistic data sizes

Lexicon Tests#

  • ✅ Create both valid and invalid test cases
  • ✅ Cover all required fields
  • ✅ Test edge cases (empty strings, max lengths, etc.)
  • ✅ Update tests when schemas change

CI/CD Integration#

When setting up CI/CD, the test pipeline should:

# Example GitHub Actions workflow
steps:
  - name: Start test database
    run: |
      docker-compose -f docker-compose.dev.yml --env-file .env.dev --profile test up -d postgres-test
      sleep 5

  - name: Run migrations
    run: make test-db-migrate  # (add this to Makefile if needed)

  - name: Run tests
    run: make test

  - name: Cleanup
    run: docker-compose -f docker-compose.dev.yml --profile test down -v

Getting Help#

If tests are failing:

  1. Check Troubleshooting section above
  2. Verify .env.dev has correct test database config
  3. Run make test-db-reset for a clean slate
  4. Check test database logs: docker logs coves-test-postgres

For questions about: