mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
Pin that bikeshare systems sharing a station ID stay apart and an outage keeps the last status card
This commit is contained in:
1 parent
299a5943a0
commit
dae4011604
1 file changed
+144
@@ -0,0 +1,144 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { createFakeEngine } from '../../globe/testing/fakeEngine.js';
|
||||
import { createBikeshareLayer } from './index.js';
|
||||
import { createBikeshareNavaraRenderer } from './render/navara.js';
|
||||
import { CITY_BY_ID } from './registry.js';
|
||||
|
||||
// Two systems that cover one view and both number a station "1".
|
||||
const BICING = 'barcelona-bicing';
|
||||
const AMBICI = 'barcelona-ambici';
|
||||
const STATION = '1';
|
||||
const settle = () => new Promise((resolve) => setImmediate(resolve));
|
||||
|
||||
const information = (name, lat, lon) => ({
|
||||
data: {
|
||||
stations: [{ station_id: STATION, name, lat, lon, capacity: 20 }],
|
||||
},
|
||||
});
|
||||
const status = (bikes) => ({
|
||||
data: {
|
||||
stations: [
|
||||
{
|
||||
station_id: STATION,
|
||||
num_bikes_available: bikes,
|
||||
num_docks_available: 20 - bikes,
|
||||
is_installed: true,
|
||||
is_renting: true,
|
||||
is_returning: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
function fixture() {
|
||||
const bicing = CITY_BY_ID.get(BICING);
|
||||
const ambici = CITY_BY_ID.get(AMBICI);
|
||||
const feeds = new Map([
|
||||
[bicing.stationInformationUrl, () => information('Bicing 1', 41.39, 2.16)],
|
||||
[ambici.stationInformationUrl, () => information('AMBici 1', 41.37, 2.07)],
|
||||
[bicing.stationStatusUrl, () => status(5)],
|
||||
[ambici.stationStatusUrl, () => status(11)],
|
||||
]);
|
||||
const makeEngine = () => {
|
||||
const engine = createFakeEngine({ kind: 'navara' });
|
||||
engine.fake.setPose({
|
||||
longitude: (2.12 * Math.PI) / 180,
|
||||
latitude: (41.38 * Math.PI) / 180,
|
||||
height: 10000,
|
||||
});
|
||||
engine.fake.script('viewRectangle', null);
|
||||
return engine;
|
||||
};
|
||||
let engine = makeEngine();
|
||||
const entries = new Map();
|
||||
const layer = createBikeshareLayer({
|
||||
source: {
|
||||
async getStations(url) {
|
||||
const feed = feeds.get(url);
|
||||
if (!feed) throw new Error(`unexpected ${url}`);
|
||||
return feed();
|
||||
},
|
||||
},
|
||||
services: {
|
||||
render: { governorRequestRender() {} },
|
||||
overlays: {
|
||||
setOverlayEntries: (id, value) => entries.set(id, value),
|
||||
setOverlaySourceVisible() {},
|
||||
clearOverlaySource: (id) => entries.delete(id),
|
||||
},
|
||||
},
|
||||
rendererFactory: (context) =>
|
||||
createBikeshareNavaraRenderer(engine, context),
|
||||
});
|
||||
layer.init(null, { engine });
|
||||
return {
|
||||
layer,
|
||||
entries,
|
||||
feeds,
|
||||
get engine() {
|
||||
return engine;
|
||||
},
|
||||
switchEngine() {
|
||||
layer.detachRenderer();
|
||||
engine = makeEngine();
|
||||
layer.attachRenderer(null, { engine });
|
||||
},
|
||||
card: () => entries.get('bikeshare-selected')?.[0] ?? null,
|
||||
click(key) {
|
||||
engine.fake.script('pick', { id: key });
|
||||
engine.fake.input('click', { position: { x: 100, y: 100 } });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
test('edge:bikeshare: two systems with the same station ID stay two stations', async () => {
|
||||
const f = fixture();
|
||||
try {
|
||||
f.layer.enable();
|
||||
await settle();
|
||||
const pickIds = f.engine.fake.moverSet('bikeshare').store.pickIds;
|
||||
const keys = [...pickIds].slice(0, f.layer.getStats().count).sort();
|
||||
assert.deepEqual(keys, [`${AMBICI}:${STATION}`, `${BICING}:${STATION}`]);
|
||||
f.click(`${BICING}:${STATION}`);
|
||||
assert.equal(f.card().title, 'Bicing 1');
|
||||
assert.match(f.card().details[0], /5 avail/);
|
||||
f.click(`${AMBICI}:${STATION}`);
|
||||
assert.equal(f.card().title, 'AMBici 1');
|
||||
assert.match(f.card().details[0], /11 avail/);
|
||||
// A status poll of one system never rewrites the other's station.
|
||||
f.feeds.set(CITY_BY_ID.get(BICING).stationStatusUrl, () => status(0));
|
||||
await f.layer.update();
|
||||
assert.match(f.card().details[0], /11 avail/);
|
||||
} finally {
|
||||
f.layer.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
test('edge:bikeshare: during a source outage the card keeps the last status, across an engine switch', async (t) => {
|
||||
t.mock.method(console, 'warn', () => {});
|
||||
const f = fixture();
|
||||
try {
|
||||
f.layer.enable();
|
||||
await settle();
|
||||
const key = `${AMBICI}:${STATION}`;
|
||||
f.click(key);
|
||||
assert.match(f.card().details[0], /11 avail/);
|
||||
f.feeds.set(CITY_BY_ID.get(AMBICI).stationStatusUrl, () => {
|
||||
throw new Error('HTTP 503');
|
||||
});
|
||||
await f.layer.update();
|
||||
assert.equal(f.card().title, 'AMBici 1');
|
||||
assert.match(f.card().details[0], /11 avail/);
|
||||
// The other system still draws, so the layer is not reported as failed.
|
||||
assert.equal(f.layer.getStats().error, undefined);
|
||||
|
||||
f.switchEngine();
|
||||
await settle();
|
||||
assert.equal(f.card()?.title, 'AMBici 1');
|
||||
assert.match(f.card().details[0], /11 avail/);
|
||||
assert.equal(f.layer.getStats().count, 2);
|
||||
} finally {
|
||||
f.layer.destroy();
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user