positions.device_id stores the IMEI (text); entry_devices.device_id is a
uuid FK to devices.id. snapshot.ts and device-event-map.ts joined the two
columns directly, causing:
- snapshot.ts: Postgres rejected with `operator does not exist: uuid =
text` (42883). The registry caught the error and returned an empty
snapshot, masking the failure.
- device-event-map.ts: cache keyed on entry_devices.device_id (uuid),
but broadcast.ts:141 looks up by position.device_id (imei). Cache
missed every record → no live frames fanned out, silently.
Both queries now hop through the devices table (devices.imei =
positions.device_id, devices.id = entry_devices.device_id). The
device-event-map cache aliases d.imei AS device_id so cache keys stay
IMEI strings — broadcast.ts is unchanged.
The integration-test fixture schema previously had entry_devices.device_id
as text (IMEI) — a deliberate simplification that hid the production type
mismatch. Now matches production: adds a devices table and changes the FK
to uuid. seedDatabase inserts devices first.
178/178 unit tests pass. Integration test exercises the corrected join
shape.
Adds end-to-end integration test for the WebSocket live broadcast pipeline:
Redis + TimescaleDB containers + Directus stub → full pipeline boot → real
WS client assertions. Mirrors pipeline.integration.test.ts pattern with the
skip-on-no-Docker guard.
Key additions:
- test/live.integration.test.ts: 6 test scenarios — happy path (subscribe →
snapshot → live position), auth rejection (401), forbidden subscription
(error/forbidden), multi-client fan-out (both receive position), orphan
position (no WS frame), faulty snapshot exclusion (next-best non-faulty)
- test/helpers/directus-stub.ts: bare http.createServer stub for /users/me
and /items/events/:id endpoints with cookie-based user lookup
- test/fixtures/test-schema.sql: minimal schema subset (events, entries,
entry_devices with IMEI-as-device_id for Phase 1 join semantics)
The integration test runs via `pnpm test:integration`, not `pnpm test`.
Docker required; the suite skips cleanly when Docker is unavailable.