mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
fix: distinguish partial vessel snapshots from stale data
This commit is contained in:
1 parent
531ef52ca4
commit
a25ee64209
13 files changed
+155
-3
No files matched your search
@@ -25,6 +25,7 @@ export function createEvidence({
|
||||
state.feed.loaded = true;
|
||||
state.feed.error = null;
|
||||
state.feed.stale = false;
|
||||
state.feed.partial = false;
|
||||
state.feed.lastUpdate = Date.now();
|
||||
state.feed.transportStatus = 'synthetic';
|
||||
state.feed.lastMessageAt = null;
|
||||
|
||||
@@ -102,6 +102,7 @@ export function createIngestion({
|
||||
feed.lastMessageAt = snapshot.lastMessageAt;
|
||||
feed.rawRowCount = snapshot.rawRowCount;
|
||||
feed.acceptedRowCount = snapshot.acceptedRowCount;
|
||||
feed.partial = payload?.complete === false;
|
||||
|
||||
if (snapshot.acceptedRowCount === 0) {
|
||||
feed.count = readCount();
|
||||
@@ -133,7 +134,8 @@ export function createIngestion({
|
||||
feed.count = readCount();
|
||||
feed.stale =
|
||||
Boolean(payload?.refreshing) ||
|
||||
payload?.complete === false ||
|
||||
payload?.freshness === 'stale' ||
|
||||
snapshot.transportStatus === 'stale' ||
|
||||
payload?.freshness === 'unknown';
|
||||
feed.newestPositionAt = payload?.newestPositionAt || null;
|
||||
// Not unconditionally null: a degraded feed keeps its reason even though the
|
||||
@@ -190,6 +192,7 @@ export function createVesselFeed() {
|
||||
loading: false,
|
||||
loaded: false,
|
||||
stale: false,
|
||||
partial: false,
|
||||
error: null,
|
||||
loadingLabel: '',
|
||||
lastUpdate: null,
|
||||
|
||||
@@ -100,6 +100,7 @@ export function createLifecycle({
|
||||
state.feed.loading = false;
|
||||
state.feed.loaded = false;
|
||||
state.feed.stale = false;
|
||||
state.feed.partial = false;
|
||||
state.feed.error = null;
|
||||
state.feed.loadingLabel = '';
|
||||
state.feed.lastUpdate = null;
|
||||
|
||||
@@ -124,7 +124,8 @@ test('partial observations retain missing contacts only until their receipt dead
|
||||
current = snapshot([observation('111')], { complete: false, rawRowCount: 2 });
|
||||
await layer.update();
|
||||
assert.equal(layer.hasContact('222'), true);
|
||||
assert.equal(layer.getStats().stale, true);
|
||||
assert.equal(layer.getStats().stale, false);
|
||||
assert.equal(layer.getStats().partial, true);
|
||||
assert.equal(layer.getStats().rawRowCount, 2);
|
||||
assert.equal(layer.getStats().acceptedRowCount, 1);
|
||||
assert.equal(layer.getStats().lastUpdate, 500);
|
||||
@@ -301,3 +302,62 @@ test('refresh keeps pick identity and renderer geometry separate, and eviction r
|
||||
assert.equal(primitives.has(primitive), false);
|
||||
assert.equal(layer.findByQuery('111'), null);
|
||||
});
|
||||
|
||||
test('partial vessel snapshots preserve freshness and recover after a complete update', async (t) => {
|
||||
const originalDocument = globalThis.document;
|
||||
globalThis.document = { getElementById: () => null };
|
||||
t.after(() => {
|
||||
if (originalDocument === undefined) delete globalThis.document;
|
||||
else globalThis.document = originalDocument;
|
||||
});
|
||||
const { vesselSnapshot } = await import('../../sources/live/vessels.js');
|
||||
const { layerFeedState } = await import('../../data/feedState.js');
|
||||
const valid = {
|
||||
mmsi: '123456789',
|
||||
lat: 51.93,
|
||||
lon: 4.05,
|
||||
last_position_epoch: 1,
|
||||
};
|
||||
let current = vesselSnapshot({
|
||||
rows: [valid, { ...valid }, { mmsi: '987654321', lat: 200, lon: 4 }],
|
||||
status: 'live',
|
||||
});
|
||||
const { layer } = setup({ getSnapshot: async () => current });
|
||||
await layer.update();
|
||||
assert.equal(layer.getStats().partial, true);
|
||||
assert.equal(layer.getStats().stale, false);
|
||||
assert.equal(layer.getStats().rawRowCount, 3);
|
||||
assert.equal(layer.getStats().acceptedRowCount, 1);
|
||||
assert.equal(layerFeedState(layer.getStats()), 'partial');
|
||||
assert.equal(layer.hasContact('123456789'), true);
|
||||
|
||||
for (const extra of [
|
||||
{ stale: true },
|
||||
{ freshness: 'stale' },
|
||||
{ freshness: 'unknown', observedAtMs: null },
|
||||
{ transportStatus: 'stale' },
|
||||
]) {
|
||||
const original = current;
|
||||
current = { ...original, ...extra };
|
||||
await layer.update();
|
||||
assert.equal(layer.getStats().partial, true);
|
||||
assert.equal(layerFeedState(layer.getStats()), 'stale');
|
||||
current = original;
|
||||
}
|
||||
current = { ...current, transportStatus: 'reconnecting' };
|
||||
await layer.update();
|
||||
assert.equal(layerFeedState(layer.getStats()), 'degraded');
|
||||
assert.match(layer.getStats().error, /reconnecting/);
|
||||
|
||||
current = vesselSnapshot({ rows: [valid], status: 'live' });
|
||||
await layer.update();
|
||||
assert.equal(layer.getStats().partial, false);
|
||||
assert.equal(layer.getStats().stale, false);
|
||||
assert.equal(layer.getStats().error, null);
|
||||
assert.equal(layerFeedState(layer.getStats()), 'nominal');
|
||||
current = { ...current, complete: false };
|
||||
await layer.update();
|
||||
assert.equal(layer.getStats().partial, true);
|
||||
layer.destroy();
|
||||
assert.equal(layer.getStats().partial, false);
|
||||
});
|
||||
@@ -508,6 +508,7 @@ export function createQueries({
|
||||
: state.feed.loadingLabel,
|
||||
error: state.feed.error,
|
||||
stale: state.feed.stale,
|
||||
partial: state.feed.partial,
|
||||
status:
|
||||
state.feed.firstConnectPhase === 'unavailable'
|
||||
? 'unavailable'
|
||||
|
||||
@@ -37,6 +37,7 @@ export function createTesting({
|
||||
state.feed.loaded = options.loaded === true;
|
||||
state.feed.loading = options.loading === true;
|
||||
state.feed.stale = options.stale === true;
|
||||
state.feed.partial = options.partial === true;
|
||||
state.feed.error = options.error || null;
|
||||
state.feed.lastUpdate = options.lastUpdate ?? null;
|
||||
state.records.all = records;
|
||||
|
||||
Reference in new issue
Block a user