import { describe, it, expect } from 'vitest'; import { parseTelemetry, computeDeviations, distHaversine, parsePrediction } from '$domain'; import type { TelemetryPoint, PredictionStage } from '$domain'; const points: TelemetryPoint[] = [ { latitude: 62.0, longitude: 129.0, altitude: 0, datetime: '2026-01-01T00:00:00Z', payload: '{}' }, { latitude: 62.05, longitude: 129.1, altitude: 15000, datetime: '2026-01-01T00:30:00Z', payload: '{}' } ]; describe('parseTelemetry', () => { it('builds a flight path and takes the first sample as launch', () => { const t = parseTelemetry(points); expect(t.flight_path).toEqual([ [62.0, 129.0, 0], [62.05, 129.1, 15000] ]); expect(t.launch.latlng.lat).toBe(62.0); expect(t.datapoints).toHaveLength(2); }); it('throws on an empty series', () => { expect(() => parseTelemetry([])).toThrow(); }); }); describe('distHaversine', () => { it('is zero for identical points and ~343 km London→Paris', () => { expect(distHaversine({ lat: 55, lng: 37 }, { lat: 55, lng: 37 })).toBe(0); const d = distHaversine({ lat: 51.5074, lng: -0.1278 }, { lat: 48.8566, lng: 2.3522 }); expect(d).toBeGreaterThan(340); expect(d).toBeLessThan(346); }); }); describe('computeDeviations', () => { // Prediction runs 00:00 → 01:30 along the same corridor as the telemetry. const stages: PredictionStage[] = [ { stage: 'ascent', trajectory: [ { altitude: 0, datetime: '2026-01-01T00:00:00Z', latitude: 62.0, longitude: 129.0 }, { altitude: 30000, datetime: '2026-01-01T01:00:00Z', latitude: 62.1, longitude: 129.2 } ] }, { stage: 'descent', trajectory: [ { altitude: 30000, datetime: '2026-01-01T01:00:00Z', latitude: 62.1, longitude: 129.2 }, { altitude: 0, datetime: '2026-01-01T01:30:00Z', latitude: 62.05, longitude: 129.4 } ] } ]; const prediction = parsePrediction(stages); it('matches each sample to the closest prediction point', () => { const devs = computeDeviations(points, prediction); expect(devs).toHaveLength(2); // First sample sits exactly on the predicted launch point. expect(devs[0].horizontal).toBeCloseTo(0, 6); expect(devs[0].vertical).toBe(0); // Second sample is offset from the prediction, both laterally and in altitude. expect(devs[1].horizontal).toBeGreaterThan(0); expect(devs[1].altActual).toBe(15000); }); it('skips samples outside the prediction window', () => { const outside: TelemetryPoint[] = [ { latitude: 62, longitude: 129, altitude: 0, datetime: '2025-12-31T00:00:00Z', // a day early payload: '{}' } ]; expect(computeDeviations(outside, prediction)).toHaveLength(0); }); it('returns nothing without telemetry', () => { expect(computeDeviations([], prediction)).toEqual([]); }); });