Two new test suites covering the driver app, dispatch endpoints, and offline sync queue. 24 endpoint tests against wrangler dev verify auth, R2 side-effects, and admin/driver role gates. 19 unit tests in jsdom + fake-indexeddb verify the offline sync queue persists and drains correctly. Plus the original 22 bot tests still passing — total 65 tests, run in ~6 seconds.
Worker tests need wrangler dev running so they can hit real endpoints + R2. Unit tests don't need anything external — they run entirely in jsdom with fake-indexeddb.
Tests use a driver-auth.ts helper that logs in as a known dev driver and caches the JWT. R2 side-effects are verified by reading delivery-events/{date}/{order_id}.json directly via wrangler CLI after each POST.
Verifies JWT format (3 base64 segments), driver fields (id, name, branch, role), expires_in: 86400.
Confirms driver_a gets role: "driver" back, not admin.
Constant-time PIN comparison rejects without leaking timing info.
Same status as wrong PIN — doesn't leak whether a driver exists.
Body missing pin field gets a real 400, not a generic 500.
Bearer token required.
Belleville driver gets the 3-stop seeded route. Verifies stop schema (order_id, customer, order_summary).
Empty stops: [] + friendly message field. No 404 — graceful empty state.
Admins logged into a branch see that branch's route — same flow as drivers, no special handling needed.
Verifies delivery-events/{today}/{order_id}.json is written with correct shape via direct R2 read.
Tiny 1×1 JPEG base64 → confirms delivery-photos/{today}/{order_id}.jpg exists, event has photo_url: r2://....
SVG signature path round-trips verbatim. Notes preserved.
Verifies status='exception', reason, attempted_at, notes all stored.
Loops through no_one_home, wrong_address, refused, damaged, other — each round-trips correctly through R2.
Branch comes from JWT, not request body. Driver_a's pings tag as belleville.
Two pings with different coords — second wins. Dispatch dashboard always sees the freshest position.
Role gate enforced — drivers can't see the dispatch view, even with a valid JWT.
Returns both branches (belleville + kingston) with totals + exceptions inbox.
Tests run in order — earlier delivery completion tests created events. This test verifies the dispatch endpoint reads them correctly: TEST-001 should now show status: 'delivered'.
GET /api/v1/driver/list returns drivers with role field, no auth required.
No wrangler dev needed — these run in pure Node.js with fake-indexeddb polyfilling IndexedDB and vi.spyOn(global, 'fetch') mocking the network. Verifies the queue persists, drains, retries, and never re-syncs.
Sets auth with expires_at in the past — getAuth() returns null AND removes it from IDB.
Stores 3 records (delivered, exception, synced delivered) — verifies all 3 come back.
Only returns delivered/exception with synced: false — pending records and synced records are excluded.
Same order_id key → second write replaces first. Critical for marking records synced after upload.
Driver can log out cleanly without losing their token mid-rotation. Specifically tests partial clear semantics.
No deliveries pending → { synced: 0, failed: 0, total: 0 } — fetch never called.
Verifies fetch URL contains /{order_id}/complete, body includes photo/signature/notes, Authorization header has Bearer JWT, and record is marked synced: true + last_sync_attempt: number.
Different endpoint, different payload shape, but same drain logic.
Mock fetch rejects → result has failed=1, synced=0. Record stays in IDB with last_sync_attempt bumped.
Server error treated same as network error — record stays queued for next retry.
Two records, mock implements per-call success/failure → result.synced=1, result.failed=1. Verifies records are independently tracked.
Idempotency. listUnsyncedDeliveries() filters them out → fetch only called for new records.
Pending records aren't ready to sync — they get skipped even if synced=false.
Driver tests need a few new pieces: a route template for today, an auth helper that logs in once and caches the JWT, and real PIN hashes for the dev drivers (so login actually works in tests).
// Cached per-process so tests don't re-login for every assertion
const tokenCache = new Map<string, string>();
export const KNOWN_DRIVERS = {
himmat: { id: 'himmat', pin: '1234', branch: 'belleville', role: 'admin' },
preet: { id: 'preet', pin: '2345', branch: 'belleville', role: 'admin' },
driver_a: { id: 'driver_a', pin: '3456', branch: 'belleville', role: 'driver' },
driver_b: { id: 'driver_b', pin: '4567', branch: 'kingston', role: 'driver' },
driver_c: { id: 'driver_c', pin: '5678', branch: 'kingston', role: 'driver' },
};
export async function getDriverToken(driverId): Promise<string> {
const cached = tokenCache.get(driverId);
if (cached) return cached;
const driver = KNOWN_DRIVERS[driverId];
const r = await fetch(`${BASE_URL}/api/v1/driver/login`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ driver_id: driver.id, pin: driver.pin }),
});
const body = await r.json();
tokenCache.set(driverId, body.token);
return body.token;
}
export async function authedJSON<T>(driverId, path, init = {}): Promise<T> {
const r = await authedFetch(driverId, path, init);
if (!r.ok) throw new Error(`Authed request failed: ${r.status} ${path}`);
return await r.json() as T;
}
// Polyfills IndexedDB so idb-keyval works in jsdom.
// Each test file should clear IDB between tests via beforeEach() in the
// individual specs.
import 'fake-indexeddb/auto';
{
"branch": "belleville",
"stops": [
{
"order_id": "TEST-001",
"sequence": 1,
"customer": {
"name": "Marcus T.",
"phone": "613-555-1234",
"address": "23 Main St, Belleville, ON K8N 4Z2",
"lat": 44.1628,
"lng": -77.3833
},
"order_summary": {
"items": [{ "qty": 1, "name": "ACDelco PF64 Engine Oil Filter", "sku": "ACD-PF64" }],
"total_paid": 14.99,
"fulfillment": "drop_paid"
},
"delivery_window": "9:00-17:00"
}
/* +2 more stops */
]
}