todos-contra-el-fuego-web/smoke/README.md
vjrj 296dab4f38 smoke: REST API regression harness + baseline snapshots (Meteor 1.6.1.1)
Adds a standalone Node smoke test (smoke/) that exercises every REST endpoint
consumed by the Flutter app against a seeded local dev server and compares
responses to committed snapshots. This is the safety net for the Meteor upgrade:
it must stay byte-identical after every escala.

- smoke/seed.js: deterministic Mongo seed (fixed ObjectIds, geo indexes)
- smoke/run.js: calls endpoints, normalizes volatile fields, diffs snapshots
- smoke/smoke.sh: seed + run wrapper
- smoke/snapshots/: baseline captured on Meteor 1.6.1.1
- IPGeocoder.js: degrade gracefully when MaxMind DB absent (dev boot)
- settings-development.json: dev-only internalApiToken to enable the REST API
- .meteorignore: exclude smoke/ from the Meteor server build
2026-07-13 19:42:55 +02:00

73 lines
3 KiB
Markdown

# REST smoke test
Regression harness for the REST API in `imports/api/Rest/Rest.js` — the API the
Flutter app (`fires_flutter/lib/models/fires_api.dart`) depends on.
It calls every Flutter-consumed endpoint against a locally running dev server
with deterministic seed data, normalizes the intrinsically volatile fields
(uptime, generated ids, insert timestamps) and compares the responses to
committed snapshots in `smoke/snapshots/`.
**It must stay green after every Meteor upgrade escala. The snapshots are the
contract: any drift is treated as an upgrade bug.**
## Reproducible local startup
1. **MongoDB** (prod parity = 3.2; a dev-only instance in Docker):
```bash
docker run -d --name tcef-mongo32 -p 27018:27017 mongo:3.2
```
2. **Meteor dev server** (from the repo root):
```bash
export PATH="$HOME/.meteor:$PATH"
export MONGO_URL="mongodb://localhost:27018/fuegos"
meteor --settings settings-development.json --port 3100
```
`settings-development.json` carries a dev-only `private.internalApiToken`
(`dev-smoke-token`) which is what enables the REST API. Without it the API
routes are not even registered (see `Rest.js`).
> Note: `IPGeocoder.js` degrades gracefully when the MaxMind GeoLite2 DB is
> absent (it is provisioned by cron in production) so the server boots in a
> clean checkout.
## Running the smoke test
```bash
./smoke/smoke.sh # seed + compare against baseline snapshots
./smoke/smoke.sh --update # seed + regenerate the baseline snapshots
```
Env overrides: `MONGO_CONTAINER` (default `tcef-mongo32`), `MONGO_DB`
(`fuegos`), `BASE_URL` (`http://localhost:3100`).
## What is covered
| Snapshot | Endpoint | Flutter call |
|---|---|---|
| `status_uptime` | `GET status/uptime` | — |
| `status_active_fires_count` | `GET status/active-fires-count` | — |
| `status_last_fire_check` | `GET status/last-fire-check` | — |
| `status_last_fire_detected` | `GET status/last-fire-detected` | — |
| `status_subs_public_union` | `GET status/subs-public-union/:token` | `getMonitoredAreas` |
| `fires_in` | `GET fires-in/:token/:lat/:lng/:km` | — |
| `fires_in_full` | `GET fires-in-full/:token/:lat/:lng/:km` | `getFiresInLocation` |
| `mobile_users_post` | `POST mobile/users` | `createUser` |
| `mobile_subscriptions_post` | `POST mobile/subscriptions` | `subscribe` |
| `mobile_subscriptions_all` | `GET mobile/subscriptions/all/:token/:mobileToken` | `fetchYourLocations` |
| `mobile_subscriptions_delete` | `DELETE mobile/subscriptions/:token/:mobileToken/:subsId` | `unsubscribe` |
| `mobile_falsepositive_post` | `POST mobile/falsepositive` | `markFalsePositive` |
## Normalized (volatile) fields
Compared structurally but blanked to a placeholder because they cannot be
deterministic: `status/uptime` → `ms`; `mobile/users` → `userId`, `username`,
`upsertResult.insertedId`; `mobile/subscriptions/all` → `owner`, `createdAt`,
`updatedAt`; `mobile/falsepositive` → `upsert.insertedId`.
Everything else (ids of seeded docs, geometries, counts, jsend envelope, field
names and types) is asserted byte-for-byte.