Skip to content

Latest commit

 

History

History
455 lines (328 loc) · 11.8 KB

File metadata and controls

455 lines (328 loc) · 11.8 KB

Local Development & Test Execution Guide

Single reference for getting the project running locally, executing the full test suite, and resolving the most common failures. Supersedes the scattered guidance in QUICK_START.md, TEST_EXECUTION.md, and README_REPOSITORY_TESTS.md.


Prerequisites

Tool Minimum version Check
Go 1.25 go version
Git any git --version
Docker 20+ (for integration tests) docker info
PostgreSQL optional (Docker handles it)

Install Go from go.dev/dl. Docker is only required when running the integration test suite; unit tests have no external dependencies.


1. Clone and install dependencies

git clone https://github.com/YOUR_ORG/stellabill-backend.git
cd stellabill-backend
go mod download

2. Environment variables

Create a .env file in the project root — never commit it; it is already in .gitignore.

# .env — local development only, do not commit
ENV=development
PORT=8080

# Required for the server to start (use placeholder values locally)
DATABASE_URL=postgres://postgres:postgres@localhost:5432/stellarbill?sslmode=disable
JWT_SECRET=Dev-Only-Secret-Change-In-Prod-1!

# Optional
ADMIN_TOKEN=dev-admin-token
AUDIT_HMAC_SECRET=stellarbill-dev-audit
AUDIT_LOG_PATH=audit.log

# Request ID trusted proxies (comma-separated CIDRs; empty = untrusted mode)
REQUEST_ID_TRUSTED_PROXIES=

# Rate limiting (disabled by default in dev)
RATE_LIMIT_ENABLED=false

Export them before running the server:

export $(grep -v '^#' .env | xargs)

Security: Use your cloud provider's secrets manager in production. Never put real credentials in .env or any file tracked by Git.


3. Run the server

go run ./cmd/server

Verify it is up:

curl http://localhost:8080/api/health
# {"service":"stellarbill-backend","status":"ok",...}

4. Run the tests

4.1 Unit tests (no external services required)

go test ./internal/... -count=1 -timeout 60s

Expected: all packages pass. Coverage is enforced at ≥ 95% on internal/.

Generate a coverage report:

go test ./internal/... \
  -covermode=atomic \
  -coverpkg=./internal/... \
  -coverprofile=coverage.out \
  -count=1 \
  -timeout 60s

go tool cover -html=coverage.out   # opens browser

4.2 Integration tests (Docker required)

Integration tests spin up reusable Postgres, Redis, and mock webhook receiver containers automatically via testcontainers-go. No Compose bootstrap or fixed host ports are needed.

go test -tags integration -v -race -count=1 -timeout 5m ./tests/integration/...

The suite creates one stack and shares it between tests (Reuse: true). Docker assigns every host port, so parallel test processes do not contend for 5432, 6379, or 8080. Postgres and Redis credentials are generated for each run. All mapped ports bind to 127.0.0.1, so the test services are not exposed to the local network.

Ryuk removes leaked containers after interrupted runs and should remain enabled on developer machines and CI. Only set TESTCONTAINERS_RYUK_DISABLED=true in a trusted, isolated environment where the Docker socket policy prevents Ryuk from running and an external cleanup job is guaranteed. Disabling Ryuk on a shared Docker host can leave containers, credentials, and consumed resources behind.

The images use explicit version tags. Review and update those versions deliberately; do not replace them with latest. The mock webhook receiver is for local test payloads only and must never receive production secrets or PII.

4.3 Race detector

go test ./internal/... -race -count=1 -timeout 60s

4.4 Specific packages

# Middleware
go test ./internal/middleware/... -v -count=1

# Worker
go test ./internal/worker/... -v -count=1

# Outbox
go test ./internal/outbox/... -v -count=1

# Config
go test ./internal/config/... -v -count=1

# Audit
go test ./internal/audit/... -v -count=1

4.5 Pre-PR checklist

go build ./...          # must compile cleanly
go vet ./...            # no vet warnings
go fmt ./...            # code is formatted
go test ./internal/... -count=1 -timeout 60s   # all tests pass

5. Database migrations

go run ./cmd/migrate up

Migrations live in migrations/. See docs/migrations.md for conventions.


6. Troubleshooting

6.1 Server fails to start — missing environment variables

Symptom:

config error [MISSING_ENV_VAR]: required secret is missing (key=DATABASE_URL)

Fix: Export the required variables before running:

export DATABASE_URL=postgres://postgres:postgres@localhost:5432/stellarbill?sslmode=disable
export JWT_SECRET=Dev-Only-Secret-Change-In-Prod-1!
go run ./cmd/server

6.2 Server fails to start — weak JWT_SECRET

Symptom:

config error [WEAK_SECRET]: must be at least 12 characters and contain mixed
alphanumeric and special characters (key=JWT_SECRET)

Fix: Use a secret with uppercase, lowercase, digits, and a special character, minimum 12 characters:

export JWT_SECRET=Dev-Only-Secret-Change-In-Prod-1!

6.3 Database connection refused

Symptom:

dial tcp 127.0.0.1:5432: connect: connection refused

Fix options:

a) Start Postgres locally:

docker run -d \
  --name stellarbill-pg \
  -e POSTGRES_PASSWORD=postgres \
  -e POSTGRES_DB=stellarbill \
  -p 5432:5432 \
  postgres:16-alpine

b) Or point DATABASE_URL at an existing instance.

c) The server runs without a real DB for most endpoints (mock repositories are used by default in dev). Only endpoints that require persistence will fail.


6.4 Migration fails — table already exists

Symptom:

pq: relation "plans" already exists

Fix: Run the down migration first, then up:

go run ./cmd/migrate down
go run ./cmd/migrate up

Or drop and recreate the database:

docker exec -it stellarbill-pg psql -U postgres -c "DROP DATABASE stellarbill;"
docker exec -it stellarbill-pg psql -U postgres -c "CREATE DATABASE stellarbill;"
go run ./cmd/migrate up

6.5 Integration tests fail — Docker not running

Symptom:

Cannot connect to the Docker daemon at unix:///var/run/docker.sock

Fix: Start Docker Desktop (macOS/Windows) or the Docker daemon (Linux):

sudo systemctl start docker   # Linux

Then re-run:

go test -tags integration -v -count=1 -timeout 120s ./integration/...

6.6 Integration tests fail — container startup timeout

Symptom:

context deadline exceeded waiting for container to be ready

Fix: Increase the timeout or check Docker resource limits:

go test -tags integration -v -count=1 -timeout 300s ./integration/...

If Docker is resource-constrained, increase memory allocation in Docker Desktop settings (≥ 4 GB recommended).


6.7 Auth failures in tests — 401 Unauthorized

Symptom: Tests that hit protected endpoints return 401 unexpectedly.

Cause: The Authorization header is missing or the bearer token does not match JWT_SECRET.

Fix: Ensure the test sets the correct header:

req.Header.Set("Authorization", "Bearer "+jwtSecret)

For integration tests, JWT_SECRET is set in TestMain via os.Setenv("JWT_SECRET", ...). Check integration/main_test.go if the value has drifted.


6.8 Request ID not propagated — missing X-Request-ID header

Symptom: Responses lack X-Request-ID or logs show a different ID than the one sent.

Cause: The inbound X-Request-ID is only accepted from trusted sources. If REQUEST_ID_TRUSTED_PROXIES is empty (the default), all inbound IDs are discarded and a new one is generated.

Fix for local testing with curl:

# Without trusted proxies — server generates its own ID
curl -H "X-Request-ID: my-trace-id" http://localhost:8080/api/health
# Response X-Request-ID will be a generated ID, not "my-trace-id"

# To accept inbound IDs, add your IP to the allowlist
export REQUEST_ID_TRUSTED_PROXIES=127.0.0.1/32
go run ./cmd/server
curl -H "X-Request-ID: my-trace-id" http://localhost:8080/api/health
# Response X-Request-ID: my-trace-id

6.9 Tests hang — goroutine leak in rate limiter

Symptom: go test hangs and eventually times out with a goroutine dump showing cleanupExpiredBuckets blocked on a ticker.

Cause: APIRateLimiter starts a background goroutine. Tests that create one must call rl.Stop() to release it.

Fix: Always defer Stop() in tests:

rl := middleware.NewAPIRateLimiter(config)
defer rl.Stop()

6.10 Build fails — duplicate constant declarations

Symptom:

./requestid.go:8:2: RequestIDHeader redeclared in this block

Cause: internal/middleware/requestid.go (now deleted) conflicted with internal/middleware/middleware.go. If you see this after a merge or rebase, ensure internal/middleware/requestid.go does not exist:

ls internal/middleware/requestid.go   # should not exist

If it does, delete it — all request ID logic now lives in internal/requestid/requestid.go.


6.11 go test ./... fails on cmd/server

Symptom:

FAIL stellarbill-backend/cmd/server [setup failed]

Cause: cmd/server/main.go is the process entry point and cannot be instrumented as a unit-testable package. This is expected.

Fix: Run tests on internal/ only:

go test ./internal/... -count=1 -timeout 60s

6.12 Coverage below 95%

Symptom:

coverage: 87.3% of statements

Fix: Find uncovered lines:

go test ./internal/... \
  -coverprofile=coverage.out \
  -coverpkg=./internal/... \
  -count=1

go tool cover -func=coverage.out | sort -t% -k1 -n | head -20

Add tests for the uncovered paths, focusing on error branches and edge cases.


7. CI reference

The GitHub Actions workflow (.github/workflows/ci.yml) runs on every push and pull request:

Step Command
Build go build ./...
Vet go vet ./...
Unit tests + coverage go test ./internal/... -covermode=atomic -coverpkg=./internal/...
Coverage threshold ≥ 95% on internal/

Coverage artifacts are retained for 14 days.


8. Security notes

  • Never commit .env, JWT secrets, database credentials, or API keys.
  • REQUEST_ID_TRUSTED_PROXIES defaults to empty (untrusted mode). Only add CIDRs you control (e.g., your load balancer's egress range).
  • The in-memory rate limiter is process-local. In multi-instance deployments, use a shared store (Redis) or rely on upstream rate limiting.
  • CORS is configured as * in development. Set an explicit origin in production via the CORS_ALLOW_ORIGIN environment variable.
  • Audit log entries are HMAC-signed. Protect AUDIT_HMAC_SECRET as you would any signing key.

9. Related documents

Document Purpose
README.md Project overview, API reference, contributing guide
docs/migrations.md Migration conventions and production runbook
docs/RATE_LIMITING.md Rate limiting configuration and security notes
docs/outbox-pattern.md Outbox pattern design and operational notes
docs/runbooks/outbox-operations.md Outbox operational runbook
docs/security-notes.md Security analysis and threat model
internal/worker/README.md Background worker documentation