Files
gods-eye-view/src/data/militaryAwarenessEngine.test.mjs
T

97 lines
4.3 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {
AWARENESS_RELATIONSHIP,
findByDoublingRadius,
getAwarenessNavigationTargets,
formatAwarenessDistance,
formatAwarenessLabel,
summarizeAwarenessCohort,
} from './militaryAwarenessEngine.js';
test('awareness formatters preserve valid zeroes and replace missing or invalid values', () => {
assert.equal(formatAwarenessDistance(0), '0 m');
assert.equal(formatAwarenessDistance(950), '950 m');
assert.equal(formatAwarenessDistance(Number.NaN), '—');
assert.equal(formatAwarenessDistance(-1), '—');
assert.equal(formatAwarenessLabel(' UAL649 '), 'UAL649');
assert.equal(formatAwarenessLabel(0), '0');
assert.equal(formatAwarenessLabel({ callsign: ' UAL649 ', id: 'fallback' }), 'UAL649');
assert.equal(formatAwarenessLabel({ name: '', label: 'Mapped site' }), 'Mapped site');
assert.equal(formatAwarenessLabel({ id: 0 }), '0');
assert.equal(formatAwarenessLabel(''), '—');
assert.equal(formatAwarenessLabel({}), '—');
assert.equal(formatAwarenessLabel(undefined), '—');
});
test('awareness cohorts sort nearest-first and do not turn empty data into out of range', () => {
const result = summarizeAwarenessCohort([{ id: 'far', distance: 9000 }, { id: 'near', distanceM: 1000 }]);
assert.equal(result.relationship, 'NEARBY');
assert.deepEqual(result.nearest.map((item) => item.id), ['near', 'far']);
assert.equal(summarizeAwarenessCohort([]).relationship, 'UNKNOWN');
});
test('awareness cohorts reject negative and non-finite distances as unavailable data', () => {
const result = summarizeAwarenessCohort([
{ id: 'negative', distanceM: -1 },
{ id: 'nan', distanceM: Number.NaN },
{ id: 'valid-zero', distanceM: 0 },
]);
assert.equal(result.relationship, 'NEARBY');
assert.deepEqual(result.nearest.map((item) => item.id), ['valid-zero']);
});
test('unavailable and stale feeds remain unknown', () => {
assert.equal(AWARENESS_RELATIONSHIP.OUTSIDE_RANGE, 'OUTSIDE_RANGE');
assert.equal(summarizeAwarenessCohort([], { available: false }).reason, 'feed unavailable');
assert.equal(summarizeAwarenessCohort([], { stale: true }).reason, 'feed stale');
});
test('navigation prioritizes nearby targets from the selected cohort', () => {
const cohorts = [
{ id: 'flights', summary: { nearest: [{ icao24: 'a1', distanceM: 1000 }, { icao24: 'a2', distanceM: 2000 }] } },
{ id: 'military', summary: { nearest: [{ icao24: 'm1', distanceM: 500 }] } },
];
const targets = getAwarenessNavigationTargets(cohorts, { layerId: 'flights', id: 'subject' }, ['flights:a1']);
// Cohort affinity still wins among UNVISITED targets: a2 leads at 2000 m even
// though military m1 sits at 500 m.
//
// Visited state now outranks that affinity, which is the corrected half of
// this ordering. Ranking the subject's own layer first unconditionally put
// the already-visited a1 ahead of the unvisited m1, so NEXT cycled inside the
// subject's layer and never reached military, vessels, or installations.
assert.deepEqual(targets.map((target) => `${target.layerId}:${target.id}`), ['flights:a2', 'military:m1', 'flights:a1']);
});
test('awareness cycling excludes satellites, which retain independent tracking', () => {
const cohorts = [
{ id: 'satellites', summary: { nearest: [{ id: 'iss', distanceM: 100 }] } },
{ id: 'ais-live-vessels', summary: { nearest: [{ mmsi: 'v1', distanceM: 1000 }] } },
];
const targets = getAwarenessNavigationTargets(cohorts);
assert.deepEqual(targets.map((target) => `${target.layerId}:${target.id}`), ['ais-live-vessels:v1']);
});
test('empty proximity searches double their radius until an aircraft is found', () => {
const searched = [];
const result = findByDoublingRadius((radiusM) => {
searched.push(radiusM);
return radiusM >= 1000000 ? { id: 'closest-flight' } : null;
});
assert.deepEqual(searched, [250000, 500000, 1000000]);
assert.deepEqual(result, {
candidate: { id: 'closest-flight' },
radiusM: 1000000,
});
});
test('expanding proximity search stops at its global bound when feeds are empty', () => {
const searched = [];
const result = findByDoublingRadius((radiusM) => {
searched.push(radiusM);
return null;
}, { initialRadiusM: 250000, maxRadiusM: 1000000 });
assert.equal(result, null);
assert.deepEqual(searched, [250000, 500000, 1000000]);
});