Skip to content

Commit 40b6f34

Browse files
AchoArnoldCopilot
andcommitted
docs: add integration test documentation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f0375a8 commit 40b6f34

2 files changed

Lines changed: 235 additions & 0 deletions

File tree

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Quick Start Guide 👉 [https://docs.httpsms.com](https://docs.httpsms.com)
4343
- [6. Build and Run](#6-build-and-run)
4444
- [7. Create the System User](#7-create-the-system-user)
4545
- [8. Build the Android App.](#8-build-the-android-app)
46+
- [Integration Testing](#integration-testing)
4647
- [License](#license)
4748

4849
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -255,6 +256,26 @@ docker compose up --build
255256

256257
- Before building the Android app in [Android Studio](https://developer.android.com/studio), you need to replace the `google-services.json` file in the `android/app` directory with the file which you got from step 1. You need to do this for the firebase FCM messages to work properly.
257258

259+
## Integration Testing
260+
261+
The project includes end-to-end integration tests that validate the complete SMS send/receive lifecycle. Tests run the full stack (API, PostgreSQL, Redis) in Docker alongside a phone emulator that simulates an Android device.
262+
263+
📖 **Full documentation:** [`tests/README.md`](tests/README.md)
264+
265+
**Quick run:**
266+
267+
```bash
268+
cd tests
269+
bash generate-firebase-credentials.sh
270+
export FIREBASE_CREDENTIALS=$(jq -c . firebase-credentials.json)
271+
docker compose up -d --build --wait
272+
docker compose wait seed && sleep 2
273+
go test -v -timeout 120s ./...
274+
docker compose down -v
275+
```
276+
277+
Integration tests also run automatically in CI on every push/PR to `main`.
278+
258279
## License
259280

260281
This project is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 - see the [LICENSE](LICENSE) file for details

tests/README.md

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Integration Tests
2+
3+
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.
4+
5+
## Architecture
6+
7+
```
8+
┌──────────────┐ HTTP ┌──────────────┐
9+
│ Test Runner │─────────────▶│ API (Go) │
10+
│ (Go test) │ │ Port 8000 │
11+
└──────────────┘ └──────┬───────┘
12+
13+
FCM Push │ Events
14+
(HTTP) │ (HTTP)
15+
16+
┌──────────────┐
17+
│ Emulator │
18+
│ (Fiber v3) │
19+
│ Port 9090 │
20+
└──────────────┘
21+
22+
┌──────┴───────┐
23+
│ PostgreSQL │ │ Redis │
24+
│ Port 5435 │ │ Port 6379 │
25+
└──────────────┘ └─────────────┘
26+
```
27+
28+
### Components
29+
30+
| Component | Description |
31+
| --------------- | ------------------------------------------------------- |
32+
| **API** | The httpSMS Go API server running in Docker |
33+
| **Emulator** | A Fiber v3 Go service that simulates an Android phone |
34+
| **PostgreSQL** | Database for the API |
35+
| **Redis** | Cache and queue backend |
36+
| **Seed** | One-shot container that seeds test data into PostgreSQL |
37+
| **Test Runner** | Go test binary that runs on the host machine |
38+
39+
### How It Works
40+
41+
1. **Send SMS flow**: Test sends `POST /v1/messages/send` → API pushes FCM notification to emulator → Emulator calls `GET /v1/messages/outstanding` → Emulator fires `SENT` and `DELIVERED` events → Test polls `GET /v1/messages/{id}` until status is `delivered`
42+
43+
2. **Receive SMS flow**: Test sends `POST /v1/messages/receive` (as the phone) → API stores message → Test verifies via `GET /v1/messages/{id}`
44+
45+
### FCM Redirect
46+
47+
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:
48+
49+
- `/token` — Fake OAuth2 token endpoint (Firebase SDK requests tokens before sending)
50+
- `/v1/projects/:project/messages:send` — Fake FCM push endpoint
51+
52+
## Test Coverage
53+
54+
- [x] **Send SMS E2E** — Full send lifecycle: API → FCM push → emulator responds with SENT/DELIVERED events → message reaches `delivered` status
55+
- [x] **Receive SMS E2E** — Phone submits received message to API → message is stored and retrievable via GET endpoint
56+
57+
## Prerequisites
58+
59+
- [Docker](https://docs.docker.com/get-docker/) with Docker Compose
60+
- [Go 1.22+](https://go.dev/dl/)
61+
- [jq](https://jqlang.github.io/jq/download/) (for Firebase credentials generation)
62+
- [OpenSSL](https://www.openssl.org/) (for RSA key generation)
63+
64+
## Running Locally
65+
66+
### 1. Generate Firebase Credentials
67+
68+
The integration tests use a fake Firebase service account. Generate it with:
69+
70+
```bash
71+
cd tests
72+
bash generate-firebase-credentials.sh
73+
```
74+
75+
This creates `firebase-credentials.json` with a throwaway RSA key (the emulator doesn't validate tokens).
76+
77+
### 2. Set Environment Variable
78+
79+
```bash
80+
export FIREBASE_CREDENTIALS=$(jq -c . firebase-credentials.json)
81+
```
82+
83+
### 3. Start the Stack
84+
85+
```bash
86+
docker compose up -d --build --wait
87+
```
88+
89+
This starts PostgreSQL, Redis, the API, and the emulator. The `--wait` flag blocks until all health checks pass.
90+
91+
### 4. Wait for Seeding
92+
93+
```bash
94+
docker compose wait seed
95+
sleep 2
96+
```
97+
98+
The seed container inserts test users, phones, and API keys into PostgreSQL after the API has run its GORM migrations.
99+
100+
### 5. Run Tests
101+
102+
```bash
103+
go test -v -timeout 120s ./...
104+
```
105+
106+
### 6. Tear Down
107+
108+
```bash
109+
docker compose down -v
110+
```
111+
112+
The `-v` flag removes volumes (database data) for a clean slate next run.
113+
114+
### One-Liner
115+
116+
```bash
117+
cd tests && \
118+
bash generate-firebase-credentials.sh && \
119+
export FIREBASE_CREDENTIALS=$(jq -c . firebase-credentials.json) && \
120+
docker compose up -d --build --wait && \
121+
docker compose wait seed && \
122+
sleep 2 && \
123+
go test -v -timeout 120s ./... ; \
124+
docker compose down -v
125+
```
126+
127+
## CI/CD
128+
129+
Integration tests run automatically via GitHub Actions (`.github/workflows/integration-test.yml`):
130+
131+
- **Trigger**: Push to `main` or pull request targeting `main`
132+
- **Flow**: Generates credentials → Starts Docker stack → Seeds DB → Runs tests → Collects logs on failure → Tears down
133+
- **Gate**: Deployment should only proceed if integration tests pass
134+
135+
## Test Data
136+
137+
| Entity | Value |
138+
| -------------- | -------------------------------------- |
139+
| User API Key | `test-user-api-key` |
140+
| Phone API Key | `pk_test-phone-api-key` |
141+
| Phone Number | `+18005550199` |
142+
| Contact Number | `+18005550100` |
143+
| User ID | `test-user-id` |
144+
| Phone ID | `a1b2c3d4-e5f6-7890-abcd-ef1234567890` |
145+
146+
See [`seed.sql`](./seed.sql) for the complete seed data.
147+
148+
## Project Structure
149+
150+
```
151+
tests/
152+
├── docker-compose.yml # Full stack orchestration
153+
├── seed.sql # Database seed data
154+
├── .env.test # API environment variables
155+
├── generate-firebase-credentials.sh # Generates fake Firebase credentials
156+
├── go.mod # Test runner Go module
157+
├── go.sum
158+
├── helpers_test.go # Test utilities (HTTP client, polling)
159+
├── integration_test.go # E2E test cases
160+
└── emulator/ # Phone emulator service
161+
├── Dockerfile
162+
├── go.mod
163+
├── go.sum
164+
├── main.go # Fiber v3 entry point
165+
├── emulator.go # Emulator struct and config
166+
├── token_handler.go # Fake OAuth2 token endpoint
167+
├── fcm_handler.go # Fake FCM push receiver
168+
└── events.go # Event firing logic (SENT/DELIVERED)
169+
```
170+
171+
## Troubleshooting
172+
173+
### API fails to start
174+
175+
Check the API logs:
176+
177+
```bash
178+
docker compose logs api
179+
```
180+
181+
Common issues:
182+
183+
- `FIREBASE_CREDENTIALS` env var not set or malformed
184+
- PostgreSQL not ready (increase `start_period` in healthcheck)
185+
186+
### Tests timeout waiting for `delivered` status
187+
188+
Check the emulator logs:
189+
190+
```bash
191+
docker compose logs emulator
192+
```
193+
194+
The emulator should show:
195+
196+
1. `[FCM]` — Receiving the push notification
197+
2. `[EVENTS]` — Fetching outstanding messages and firing events
198+
199+
If no `[FCM]` entries appear, the API isn't reaching the emulator (check `FCM_ENDPOINT` in `.env.test`).
200+
201+
### Seed container fails
202+
203+
```bash
204+
docker compose logs seed
205+
```
206+
207+
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`.
208+
209+
## Adding New Tests
210+
211+
1. Add test functions to `integration_test.go` (or create new `*_test.go` files)
212+
2. Use `doRequest()` helper for authenticated HTTP calls
213+
3. Use `pollMessageStatus()` to wait for async state changes
214+
4. Update the test coverage checklist in this README

0 commit comments

Comments
 (0)