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