···
+
This document explains how testing works in Coves, including setup, running tests, and understanding the test infrastructure.
+
Coves uses a unified testing approach with:
+
- **Single configuration file**: [.env.dev](.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
+
# Run all tests (starts test DB, runs migrations, executes tests)
+
# Reset test database (clean slate)
+
### Configuration (.env.dev)
+
All test configuration lives in [.env.dev](.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`.
+
The test database runs in Docker via [docker-compose.dev.yml](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
+
goose -dir internal/db/migrations postgres \
+
"postgresql://$POSTGRES_TEST_USER:$POSTGRES_TEST_PASSWORD@localhost:$POSTGRES_TEST_PORT/$POSTGRES_TEST_DB?sslmode=disable" up
+
# 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 individual components in isolation.
+
**Example**: [internal/core/users/service_test.go](internal/core/users/service_test.go)
+
# Run unit tests for a specific package
+
go test -v ./internal/core/users/...
+
### 2. Integration Tests
+
Test full request/response flows with a real database.
+
**Location**: [tests/integration/](tests/integration/)
+
- Execute HTTP requests against real handlers
+
**Example**: [tests/integration/integration_test.go](tests/integration/integration_test.go)
+
# Run integration tests
+
go test -v ./tests/integration/...
+
### 3. Lexicon Validation Tests
+
Validate AT Protocol Lexicon schemas and test data.
+
- **Schemas**: [internal/atproto/lexicon/](internal/atproto/lexicon/) - 57 lexicon schema files
+
- **Test Data**: [tests/lexicon-test-data/](tests/lexicon-test-data/) - Example records for validation
+
- **Validator**: [cmd/validate-lexicon/](cmd/validate-lexicon/) - Validation tool
+
- **Library**: [internal/validation/](internal/validation/) - Validation helpers
+
**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
go run cmd/validate-lexicon/main.go -v
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
+
Migrations are managed with [goose](https://github.com/pressly/goose) and stored in [internal/db/migrations/](internal/db/migrations/).
+
# Test database (automatically run by `make test`)
+
### Creating Migrations
+
# Create a new migration
+
goose -dir internal/db/migrations create migration_name sql
+
# 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
+
# Complete reset (deletes all data)
+
### Connecting to Test Database
+
PGPASSWORD=test_password psql -h localhost -p 5434 -U test_user -d coves_test
+
docker exec -it coves-test-postgres psql -U test_user -d coves_test
+
### Inspecting Test Data
+
SELECT * FROM goose_db_version;
+
### Integration Test Template
+
func TestYourFeature(t *testing.T) {
+
// Wire up dependencies
+
repo := postgres.NewYourRepository(db)
+
service := yourpackage.NewYourService(repo)
+
r.Mount("/api/path", routes.YourRoutes(service))
+
req := httptest.NewRequest("GET", "/api/path", nil)
+
w := httptest.NewRecorder()
+
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](tests/integration/integration_test.go)):
+
- Reads config from environment variables (set by `.env.dev`)
+
- Connects to test database
+
- Returns ready-to-use `*sql.DB`
+
## Common Test Commands
+
# Run specific test package
+
go test -v ./internal/core/users/...
+
go test -v ./tests/integration/... -run TestCreateUser
+
go test -v -cover ./...
+
# Run with race detector
+
make test-db-reset && make test
+
### Test database won't start
+
# Check if port 5434 is in use
+
docker logs coves-test-postgres
+
docker-compose -f docker-compose.dev.yml --profile test down -v
+
# 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
+
### 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
+
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 .
+
- ✅ 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
+
- ✅ Use `@example.com` emails for test users (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
+
- ✅ 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
+
- ✅ Create both valid and invalid test cases
+
- ✅ Cover all required fields
+
- ✅ Test edge cases (empty strings, max lengths, etc.)
+
- ✅ Update tests when schemas change
+
When setting up CI/CD, the test pipeline should:
+
# Example GitHub Actions workflow
+
- name: Start test database
+
docker-compose -f docker-compose.dev.yml --env-file .env.dev --profile test up -d postgres-test
+
run: make test-db-migrate # (add this to Makefile if needed)
+
run: docker-compose -f docker-compose.dev.yml --profile test down -v
+
## Related Documentation
+
- [Makefile](Makefile) - All test commands
+
- [.env.dev](.env.dev) - Test configuration
+
- [docker-compose.dev.yml](docker-compose.dev.yml) - Test database setup
+
- [LOCAL_DEVELOPMENT.md](docs/LOCAL_DEVELOPMENT.md) - Full development setup
+
- [CLAUDE.md](CLAUDE.md) - Build guidelines
+
1. Check [Troubleshooting](#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`
+
- **Test infrastructure**: This document
+
- **AT Protocol testing**: [ATPROTO_GUIDE.md](ATPROTO_GUIDE.md)
+
- **Development setup**: [LOCAL_DEVELOPMENT.md](docs/LOCAL_DEVELOPMENT.md)