Integration Tests
May 5, 2026 · View on GitHub
End-to-end integration tests for the httpSMS API. These tests validate the complete SMS lifecycle by running the full application stack in Docker alongside a phone emulator service.
Architecture
┌──────────────┐ HTTP ┌──────────────┐
│ Test Runner │─────────────▶│ API (Go) │
│ (Go test) │ │ Port 8000 │
└──────────────┘ └──────┬───────┘
│
FCM Push │ Events
(HTTP) │ (HTTP)
▼
┌──────────────┐
│ Emulator │
│ (Fiber v3) │
│ Port 9090 │
└──────────────┘
│
┌──────┴───────┐
│ PostgreSQL │ │ Redis │
│ Port 5435 │ │ Port 6379 │
└──────────────┘ └─────────────┘
Components
| Component | Description |
|---|---|
| API | The httpSMS Go API server running in Docker |
| Emulator | A Fiber v3 Go service that simulates an Android phone |
| PostgreSQL | Database for the API |
| Redis | Cache and queue backend |
| Seed | One-shot container that seeds test data into PostgreSQL |
| Test Runner | Go test binary that runs on the host machine |
How It Works
-
Send SMS flow: Test sends
POST /v1/messages/send→ API pushes FCM notification to emulator → Emulator callsGET /v1/messages/outstanding→ Emulator firesSENTandDELIVEREDevents → Test pollsGET /v1/messages/{id}until status isdelivered -
Receive SMS flow: Test sends
POST /v1/messages/receive(as the phone) → API stores message → Test verifies viaGET /v1/messages/{id}
FCM Redirect
The API's Firebase SDK is configured (via FCM_ENDPOINT env var) to redirect all FCM HTTP requests to the emulator instead of Google's servers. The emulator serves:
/token— Fake OAuth2 token endpoint (Firebase SDK requests tokens before sending)/v1/projects/:project/messages:send— Fake FCM push endpoint
Test Coverage
- Send SMS E2E — Full send lifecycle: API → FCM push → emulator responds with SENT/DELIVERED events → message reaches
deliveredstatus - Receive SMS E2E — Phone submits received message to API → message is stored and retrievable via GET endpoint
Prerequisites
- Docker with Docker Compose
- Go 1.22+
- jq (for Firebase credentials generation)
- OpenSSL (for RSA key generation)
Running Locally
1. Generate Firebase Credentials
The integration tests use a fake Firebase service account. Generate it with:
cd tests
bash generate-firebase-credentials.sh
This creates firebase-credentials.json with a throwaway RSA key (the emulator doesn't validate tokens).
2. Set Environment Variable
export FIREBASE_CREDENTIALS=$(jq -c . firebase-credentials.json)
3. Start the Stack
docker compose up -d --build --wait
This starts PostgreSQL, Redis, the API, and the emulator. The --wait flag blocks until all health checks pass.
4. Wait for Seeding
docker compose wait seed
sleep 2
The seed container inserts test users, phones, and API keys into PostgreSQL after the API has run its GORM migrations.
5. Run Tests
go test -v -timeout 120s ./...
6. Tear Down
docker compose down -v
The -v flag removes volumes (database data) for a clean slate next run.
One-Liner
cd tests && \
bash generate-firebase-credentials.sh && \
export FIREBASE_CREDENTIALS=$(jq -c . firebase-credentials.json) && \
docker compose up -d --build --wait && \
docker compose wait seed && \
sleep 2 && \
go test -v -timeout 120s ./... ; \
docker compose down -v
CI/CD
Integration tests run automatically via GitHub Actions (.github/workflows/integration-test.yml):
- Trigger: Push to
mainor pull request targetingmain - Flow: Generates credentials → Starts Docker stack → Seeds DB → Runs tests → Collects logs on failure → Tears down
- Gate: Deployment should only proceed if integration tests pass
Test Data
| Entity | Value |
|---|---|
| User API Key | test-user-api-key |
| Phone API Key | pk_test-phone-api-key |
| Phone Number | +18005550199 |
| Contact Number | +18005550100 |
| User ID | test-user-id |
| Phone ID | a1b2c3d4-e5f6-7890-abcd-ef1234567890 |
See seed.sql for the complete seed data.
Project Structure
tests/
├── docker-compose.yml # Full stack orchestration
├── seed.sql # Database seed data
├── .env.test # API environment variables
├── generate-firebase-credentials.sh # Generates fake Firebase credentials
├── go.mod # Test runner Go module
├── go.sum
├── helpers_test.go # Test utilities (HTTP client, polling)
├── integration_test.go # E2E test cases
└── emulator/ # Phone emulator service
├── Dockerfile
├── go.mod
├── go.sum
├── main.go # Fiber v3 entry point
├── emulator.go # Emulator struct and config
├── token_handler.go # Fake OAuth2 token endpoint
├── fcm_handler.go # Fake FCM push receiver
└── events.go # Event firing logic (SENT/DELIVERED)
Troubleshooting
API fails to start
Check the API logs:
docker compose logs api
Common issues:
FIREBASE_CREDENTIALSenv var not set or malformed- PostgreSQL not ready (increase
start_periodin healthcheck)
Tests timeout waiting for delivered status
Check the emulator logs:
docker compose logs emulator
The emulator should show:
[FCM]— Receiving the push notification[EVENTS]— Fetching outstanding messages and firing events
If no [FCM] entries appear, the API isn't reaching the emulator (check FCM_ENDPOINT in .env.test).
Seed container fails
docker compose logs seed
If you see "relation does not exist" errors, the API hasn't finished GORM migrations yet. Increase the API's start_period in docker-compose.yml.
Adding New Tests
- Add test functions to
integration_test.go(or create new*_test.gofiles) - Use
doRequest()helper for authenticated HTTP calls - Use
pollMessageStatus()to wait for async state changes - Update the test coverage checklist in this README