Spaces:
Configuration error
Configuration error
| import { describe, expect, test } from "vitest"; | |
| import Orbit from "../Orbit"; | |
| import { formatEpoch, getSatelliteInfo } from "./entityInfo"; | |
| import { parseGpPayload, type GpRecord } from "./gp"; | |
| // The ISS: inclined enough to have nodes, and nowhere near sun-synchronous, so the | |
| // derived rows here are the apsides and nothing else. `orbitFacts.test.ts` covers | |
| // the derivation itself; these tests are about which rows appear and in what order. | |
| const ISS = new Orbit( | |
| "ISS (ZARYA)", | |
| parseGpPayload( | |
| "ISS (ZARYA)\n1 25544U 98067A 18342.69352573 .00002284 00000-0 41838-4 0 9992\n2 25544 51.6407 229.0798 0005166 124.8351 329.3296 15.54069892145658", | |
| )[0] as GpRecord, | |
| ); | |
| function labels(rows: [string, string][]): string[] { | |
| return rows.map(([label]) => label); | |
| } | |
| function valueOf(rows: [string, string][], label: string): string | undefined { | |
| return rows.find(([rowLabel]) => rowLabel === label)?.[1]; | |
| } | |
| describe("formatEpoch", () => { | |
| test("formats a julian date as UTC timestamp", () => { | |
| // JD 2460000.5 == 2023-02-25T00:00:00Z | |
| expect(formatEpoch(2460000.5)).toBe("2023-02-25 00:00:00"); | |
| }); | |
| }); | |
| describe("getSatelliteInfo", () => { | |
| test("reports the derived rows even with no metadata, class first", () => { | |
| // Derived before served, so the class is the first row whatever the record says. | |
| expect(labels(getSatelliteInfo(ISS, "LEO", {}))).toEqual(["Orbit", "Apogee / Perigee"]); | |
| expect(valueOf(getSatelliteInfo(ISS, "LEO", {}), "Orbit")).toBe("LEO"); | |
| }); | |
| test("omits every row the record does not carry, rather than showing defaults", () => { | |
| // A satellite absent from the satellite table still renders with a 200 km | |
| // swath and a 10° cone; showing those here would present a renderer fallback | |
| // as a fact about the satellite. | |
| expect(labels(getSatelliteInfo(ISS, "LEO", {}))).not.toContain("Swath"); | |
| expect(labels(getSatelliteInfo(ISS, "LEO", {}))).not.toContain("Sensor FOV"); | |
| }); | |
| test("shows a symmetric swath as a single total", () => { | |
| const rows = getSatelliteInfo(ISS, "LEO", { swathStarboardKm: 1175, swathPortKm: 1175 }); | |
| expect(valueOf(rows, "Swath")).toBe("2350 km"); | |
| }); | |
| test("spells out the sides when they differ", () => { | |
| const rows = getSatelliteInfo(ISS, "LEO", { swathStarboardKm: 1000, swathPortKm: 500 }); | |
| expect(valueOf(rows, "Swath")).toBe("1500 km (1000 stbd / 500 port)"); | |
| }); | |
| test("includes the display-only fields when present", () => { | |
| const rows = getSatelliteInfo(ISS, "LEO", { coneFovDeg: 45, operator: "ESA", missionType: "Earth observation" }); | |
| expect(valueOf(rows, "Sensor FOV")).toBe("45°"); | |
| expect(valueOf(rows, "Operator")).toBe("ESA"); | |
| expect(valueOf(rows, "Mission")).toBe("Earth observation"); | |
| }); | |
| describe("SATCAT fields", () => { | |
| test("resolves owner, launch and status codes to labels", () => { | |
| const rows = getSatelliteInfo(ISS, "LEO", { owner: "ISS", launchDate: "1998-11-20", launchSite: "TYMSC", opsStatus: "+" }); | |
| expect(valueOf(rows, "Owner")).toBe("International Space Station"); | |
| expect(valueOf(rows, "Launched")).toBe("1998-11-20 · Baikonur, Kazakhstan"); | |
| expect(valueOf(rows, "Status")).toBe("Operational"); | |
| }); | |
| test("falls back to the raw code for one it does not know", () => { | |
| // These tables go stale by design — a new nation reaches orbit and the | |
| // record arrives before the table knows about it. A code beats a blank. | |
| const rows = getSatelliteInfo(ISS, "LEO", { owner: "ZZZ", launchDate: "2026-01-01", launchSite: "QQQ", opsStatus: "!" }); | |
| expect(valueOf(rows, "Owner")).toBe("ZZZ"); | |
| expect(valueOf(rows, "Launched")).toBe("2026-01-01 · QQQ"); | |
| expect(valueOf(rows, "Status")).toBe("!"); | |
| }); | |
| test("shows the launch date alone when the site is missing", () => { | |
| expect(valueOf(getSatelliteInfo(ISS, "LEO", { launchDate: "1998-11-20" }), "Launched")).toBe("1998-11-20"); | |
| }); | |
| test("suppresses the ordinary orbit type but names the interesting ones", () => { | |
| // "Orbiting" is true of all but a handful of the satellites served, so it | |
| // would cost a row on every panel to say nothing. | |
| expect(labels(getSatelliteInfo(ISS, "LEO", { orbitType: "ORB" }))).not.toContain("Orbit type"); | |
| expect(valueOf(getSatelliteInfo(ISS, "LEO", { orbitType: "DOC" }), "Orbit type")).toBe("Docked"); | |
| expect(valueOf(getSatelliteInfo(ISS, "LEO", { orbitType: "IMP" }), "Orbit type")).toBe("Impacted"); | |
| }); | |
| test("reports a decay date only once there is one", () => { | |
| expect(labels(getSatelliteInfo(ISS, "LEO", { opsStatus: "+" }))).not.toContain("Decayed"); | |
| expect(valueOf(getSatelliteInfo(ISS, "LEO", { decayDate: "2026-08-03" }), "Decayed")).toBe("2026-08-03"); | |
| }); | |
| test("adds nothing for a satellite the catalog said nothing about", () => { | |
| // The derived rows are all that is left; no SATCAT label appears. | |
| expect(labels(getSatelliteInfo(ISS, "MEO", {}))).toEqual(["Orbit", "Apogee / Perigee"]); | |
| expect(valueOf(getSatelliteInfo(ISS, "MEO", {}), "Orbit")).toBe("MEO"); | |
| }); | |
| }); | |
| }); | |