mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 18:34:02 +00:00
170 lines
7.6 KiB
JavaScript
170 lines
7.6 KiB
JavaScript
// One-click transfer pipeline for clicked world targets (pre-launch defect #4).
|
|
// The layer only announces the click; the UI owns the camera.
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import * as Cesium from 'cesium';
|
|
import {
|
|
WORLD_CLICK_FOCUS_DURATION_SEC,
|
|
WORLD_FOCUS_FRAMING,
|
|
WORLD_FOCUS_REQUEST_EVENT,
|
|
flyToWorldTarget,
|
|
isValidWorldFocusTarget,
|
|
registerWorldFocusRequestListener,
|
|
requestWorldFocus,
|
|
routeWorldFocusRequest,
|
|
} from './worldFocus.js';
|
|
|
|
const POSITION = Cesium.Cartesian3.fromDegrees(-97.74, 30.26, 0);
|
|
|
|
function stubCamera() {
|
|
const calls = { cancelFlight: 0, flights: [] };
|
|
return {
|
|
calls,
|
|
heading: Cesium.Math.toRadians(45),
|
|
cancelFlight() { calls.cancelFlight += 1; },
|
|
flyToBoundingSphere(sphere, options) { calls.flights.push({ sphere, options }); },
|
|
};
|
|
}
|
|
|
|
test('a focus request carries the clicked target to any registered listener', () => {
|
|
const target = new EventTarget();
|
|
const seen = [];
|
|
const dispose = registerWorldFocusRequestListener(target, (event) => seen.push(event.detail));
|
|
assert.equal(requestWorldFocus({ kind: 'vessel', position: POSITION, id: '123' }, target), true);
|
|
assert.equal(seen.length, 1);
|
|
assert.equal(seen[0].kind, 'vessel');
|
|
assert.equal(seen[0].id, '123');
|
|
// Disposal is idempotent and removes the exact listener.
|
|
dispose();
|
|
dispose();
|
|
requestWorldFocus({ kind: 'vessel', id: '123', position: POSITION }, target);
|
|
assert.equal(seen.length, 1);
|
|
});
|
|
|
|
test('requests without a kind or a position are dropped, never dispatched', () => {
|
|
const target = new EventTarget();
|
|
let count = 0;
|
|
target.addEventListener(WORLD_FOCUS_REQUEST_EVENT, () => { count += 1; });
|
|
assert.equal(requestWorldFocus({ position: POSITION }, target), false);
|
|
assert.equal(requestWorldFocus({ kind: 'vessel' }, target), false);
|
|
assert.equal(requestWorldFocus(null, target), false);
|
|
assert.equal(count, 0);
|
|
});
|
|
|
|
test('routing hands the request to the UI policy, which owns the flight', () => {
|
|
const order = [];
|
|
const result = routeWorldFocusRequest(
|
|
{ detail: { kind: 'fire', id: 'fire-1', position: POSITION } },
|
|
(detail, fly) => {
|
|
order.push(`policy:${detail.kind}`);
|
|
return fly();
|
|
},
|
|
(detail) => {
|
|
order.push(`fly:${detail.kind}`);
|
|
return 'flew';
|
|
},
|
|
);
|
|
// The policy runs FIRST — it releases the follow camera before the flight.
|
|
assert.deepEqual(order, ['policy:fire', 'fly:fire']);
|
|
assert.equal(result, 'flew');
|
|
});
|
|
|
|
test('a refusing policy (cockpit owns the camera) never reaches the flight', () => {
|
|
let flights = 0;
|
|
const result = routeWorldFocusRequest(
|
|
{ detail: { kind: 'vessel', id: '123', position: POSITION } },
|
|
() => false,
|
|
() => { flights += 1; },
|
|
);
|
|
assert.equal(result, false);
|
|
assert.equal(flights, 0);
|
|
});
|
|
|
|
test('malformed requests are inert', () => {
|
|
assert.equal(routeWorldFocusRequest(null, () => 'x', () => 'y'), false);
|
|
assert.equal(routeWorldFocusRequest({ detail: { kind: 'vessel' } }, () => 'x', () => 'y'), false);
|
|
});
|
|
|
|
// A request that cannot fly must never cost the user their tracking: the UI
|
|
// policy releases the follow camera, so validation happens BEFORE it runs.
|
|
test('an unflyable request never reaches the release policy', () => {
|
|
const unflyable = [
|
|
{ kind: 'plane', position: POSITION }, // no framing for this kind
|
|
{ kind: 'vessel', position: { x: NaN, y: 0, z: 0 } }, // malformed position
|
|
{ kind: 'vessel', position: { x: 0, y: 0 } }, // truthy but incomplete
|
|
{ kind: 'vessel', id: '123', position: { x: 0, y: 0, z: 0 } }, // ECEF origin
|
|
{ kind: 'fire', id: 'fire-1', position: { x: 1, y: 1, z: 1 } }, // finite but inside Earth
|
|
{ kind: 'vessel', position: 'somewhere' },
|
|
{ kind: 'vessel', position: POSITION }, // missing owner id
|
|
{ position: POSITION },
|
|
];
|
|
for (const detail of unflyable) {
|
|
assert.equal(isValidWorldFocusTarget(detail), false, JSON.stringify(detail));
|
|
let policyRuns = 0;
|
|
const result = routeWorldFocusRequest(
|
|
{ detail },
|
|
() => { policyRuns += 1; return 'released'; },
|
|
() => 'flew',
|
|
);
|
|
assert.equal(result, false, JSON.stringify(detail));
|
|
assert.equal(policyRuns, 0, `the policy must not run for ${JSON.stringify(detail)}`);
|
|
// Nor may such a request be dispatched in the first place.
|
|
assert.equal(requestWorldFocus(detail, new EventTarget()), false);
|
|
}
|
|
assert.equal(isValidWorldFocusTarget({ kind: 'vessel', id: '123', position: POSITION }), true);
|
|
assert.equal(isValidWorldFocusTarget({ kind: 'fire', id: 'fire-1', position: POSITION }), true);
|
|
});
|
|
|
|
test('the transfer flight supersedes any flight in progress and keeps the operator heading', () => {
|
|
const camera = stubCamera();
|
|
assert.equal(flyToWorldTarget({ camera }, { kind: 'vessel', id: '123', position: POSITION }), true);
|
|
assert.equal(camera.calls.cancelFlight, 1, 'a prior flight must be cancelled, not queued');
|
|
assert.equal(camera.calls.flights.length, 1);
|
|
const { sphere, options } = camera.calls.flights[0];
|
|
assert.equal(sphere.radius, WORLD_FOCUS_FRAMING.vessel.radiusM);
|
|
assert.equal(options.duration, WORLD_CLICK_FOCUS_DURATION_SEC);
|
|
assert.equal(options.offset.range, WORLD_FOCUS_FRAMING.vessel.rangeM);
|
|
// Heading is preserved so the transfer never spins the operator around.
|
|
assert.equal(options.offset.heading, camera.heading);
|
|
assert.equal(options.easingFunction, Cesium.EasingFunction.CUBIC_IN_OUT);
|
|
});
|
|
|
|
test('fires frame wider than vessels — a fire is read by its surroundings', () => {
|
|
const camera = stubCamera();
|
|
flyToWorldTarget({ camera }, { kind: 'fire', id: 'fire-1', position: POSITION });
|
|
const { sphere, options } = camera.calls.flights[0];
|
|
assert.equal(sphere.radius, WORLD_FOCUS_FRAMING.fire.radiusM);
|
|
assert.equal(options.offset.range, WORLD_FOCUS_FRAMING.fire.rangeM);
|
|
assert.ok(WORLD_FOCUS_FRAMING.fire.rangeM > WORLD_FOCUS_FRAMING.vessel.rangeM);
|
|
assert.ok(WORLD_FOCUS_FRAMING.fire.radiusM > WORLD_FOCUS_FRAMING.vessel.radiusM);
|
|
});
|
|
|
|
test('every framing is a real oblique standoff, not a nadir or an inside-out sphere', () => {
|
|
const kinds = Object.keys(WORLD_FOCUS_FRAMING);
|
|
assert.deepEqual(kinds.sort(), ['fire', 'vessel']);
|
|
for (const kind of kinds) {
|
|
const framing = WORLD_FOCUS_FRAMING[kind];
|
|
// Looking DOWN at the target, but obliquely — a nadir drop reads as a map.
|
|
assert.ok(framing.pitchDeg < 0, `${kind}: pitch must look down, got ${framing.pitchDeg}`);
|
|
assert.ok(framing.pitchDeg > -80, `${kind}: pitch must stay oblique, got ${framing.pitchDeg}`);
|
|
// Standing off outside the framed sphere, at a human-readable distance.
|
|
assert.ok(framing.radiusM > 0, `${kind}: radius must be positive`);
|
|
assert.ok(
|
|
framing.rangeM > framing.radiusM,
|
|
`${kind}: range ${framing.rangeM} must stand off outside radius ${framing.radiusM}`,
|
|
);
|
|
assert.ok(framing.rangeM <= 10000, `${kind}: range must stay close-in, got ${framing.rangeM}`);
|
|
}
|
|
// Exact shipped values — a silent retune must show up as a failing test.
|
|
assert.deepEqual({ ...WORLD_FOCUS_FRAMING.vessel }, { radiusM: 150, rangeM: 1200, pitchDeg: -30 });
|
|
assert.deepEqual({ ...WORLD_FOCUS_FRAMING.fire }, { radiusM: 400, rangeM: 3000, pitchDeg: -35 });
|
|
});
|
|
|
|
test('unknown kinds and missing viewers issue no flight', () => {
|
|
const camera = stubCamera();
|
|
assert.equal(flyToWorldTarget({ camera }, { kind: 'plane', position: POSITION }), false);
|
|
assert.equal(flyToWorldTarget({ camera }, { kind: 'vessel' }), false);
|
|
assert.equal(flyToWorldTarget(null, { kind: 'vessel', position: POSITION }), false);
|
|
assert.equal(camera.calls.flights.length, 0);
|
|
});
|