# 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.