Route ground legs along real roads via an OSRM directions proxy
- GET /api/directions proxies OSRM (OSRM_URL env, default public demo server) with validation, Leaflet-order geometry, 24h capped cache - Map draws straight lines first, then upgrades ground legs to road-following polylines with routed km (marked road) as directions arrive; silent fallback to great-circle on failure; air legs unchanged - README documents OSRM_URL
This commit is contained in:
@@ -514,6 +514,99 @@ test('geocode requires auth and a query', async () => {
|
||||
assert.equal(noQuery.status, 400);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Directions (mocked upstream — never hits the real OSRM)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test('directions proxies and caches results (mocked fetch)', async () => {
|
||||
const { agent } = await createAccount();
|
||||
const original = global.fetch;
|
||||
let calls = 0;
|
||||
global.fetch = async () => {
|
||||
calls += 1;
|
||||
return {
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
code: 'Ok',
|
||||
routes: [
|
||||
{
|
||||
distance: 12345.6,
|
||||
geometry: {
|
||||
coordinates: [
|
||||
[98.9853, 18.7883],
|
||||
[99.0, 18.8],
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
};
|
||||
};
|
||||
try {
|
||||
const first = await agent.get('/api/directions?from=18.7883,98.9853&to=18.8,99.0');
|
||||
assert.equal(first.status, 200);
|
||||
assert.equal(first.body.km, 12.3);
|
||||
assert.deepEqual(first.body.geometry, [
|
||||
[18.7883, 98.9853],
|
||||
[18.8, 99.0],
|
||||
]);
|
||||
|
||||
// Second identical request is served from cache -> fetch not called again.
|
||||
const second = await agent.get('/api/directions?from=18.7883,98.9853&to=18.8,99.0');
|
||||
assert.equal(second.status, 200);
|
||||
assert.equal(calls, 1);
|
||||
} finally {
|
||||
global.fetch = original;
|
||||
}
|
||||
});
|
||||
|
||||
test('directions validates from/to params', async () => {
|
||||
const { agent } = await createAccount();
|
||||
|
||||
const missing = await agent.get('/api/directions?from=18.7883,98.9853');
|
||||
assert.equal(missing.status, 400);
|
||||
assert.deepEqual(missing.body, { error: 'from and to must be lat,lng' });
|
||||
|
||||
const malformed = await agent.get('/api/directions?from=abc&to=18.8,99.0');
|
||||
assert.equal(malformed.status, 400);
|
||||
|
||||
const outOfRange = await agent.get('/api/directions?from=999,98.9853&to=18.8,99.0');
|
||||
assert.equal(outOfRange.status, 400);
|
||||
});
|
||||
|
||||
test('directions returns 502 on non-Ok OSRM code and on fetch failure', async () => {
|
||||
const { agent } = await createAccount();
|
||||
const original = global.fetch;
|
||||
|
||||
global.fetch = async () => ({
|
||||
ok: true,
|
||||
json: async () => ({ code: 'NoRoute', routes: [] }),
|
||||
});
|
||||
try {
|
||||
const noRoute = await agent.get('/api/directions?from=18.7883,98.9853&to=18.8,99.0');
|
||||
assert.equal(noRoute.status, 502);
|
||||
assert.deepEqual(noRoute.body, { error: 'directions unavailable' });
|
||||
} finally {
|
||||
global.fetch = original;
|
||||
}
|
||||
|
||||
global.fetch = async () => {
|
||||
throw new Error('network down');
|
||||
};
|
||||
try {
|
||||
const res = await agent.get('/api/directions?from=18.7883,98.9853&to=18.8,99.0');
|
||||
assert.equal(res.status, 502);
|
||||
assert.deepEqual(res.body, { error: 'directions unavailable' });
|
||||
} finally {
|
||||
global.fetch = original;
|
||||
}
|
||||
});
|
||||
|
||||
test('directions requires auth', async () => {
|
||||
const noAuth = await request(app).get('/api/directions?from=18.7883,98.9853&to=18.8,99.0');
|
||||
assert.equal(noAuth.status, 401);
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Unknown /api route -> JSON 404
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user