import { describe, expect, it } from "vitest"; import type { TguPlatform, TguPlanStatus } from "../../model/tguTypes"; import type { TguPlanUi } from "../../model/timelineTypes"; import { isOperationalPlatform, operationalSpacecraftIds, pausedSpacecraftIds } from "./tguTimelineMapper"; function platform(overrides: Partial): TguPlatform { return { id: "x", noradId: 1, status: "OPERATIONAL", ...overrides }; } function uiPlan(spacecraftId: string, status: TguPlanStatus, startMs: number): TguPlanUi { return { planId: `${spacecraftId}-${status}-${startMs}`, spacecraftId, startTime: new Date(startMs).toISOString().slice(0, 19), endTime: new Date(startMs + 3_600_000).toISOString().slice(0, 19), kppId: "KPP-1", status, startMs, endMs: startMs + 3_600_000 }; } describe("isOperationalPlatform", () => { it("принимает только статус OPERATIONAL без учёта регистра и пробелов", () => { expect(isOperationalPlatform(platform({ status: "OPERATIONAL" }))).toBe(true); expect(isOperationalPlatform(platform({ status: " operational " }))).toBe(true); expect(isOperationalPlatform(platform({ status: "STANDBY" }))).toBe(false); expect(isOperationalPlatform(platform({ status: "LOST" }))).toBe(false); expect(isOperationalPlatform(platform({ status: null }))).toBe(false); expect(isOperationalPlatform(platform({ status: undefined }))).toBe(false); }); }); describe("operationalSpacecraftIds", () => { it("собирает norad-id в виде строк и пропускает платформы без norad-id", () => { const ids = operationalSpacecraftIds([ platform({ noradId: 2 }), platform({ noradId: 30 }), platform({ noradId: null }), platform({ noradId: undefined }) ]); expect(ids).toEqual(new Set(["2", "30"])); }); }); describe("pausedSpacecraftIds", () => { const t0 = Date.UTC(2026, 5, 4, 0, 0, 0); const hour = 3_600_000; it("помечает КА паузой, если есть REJECTED и нет продвинувшего цепочку плана после него", () => { const paused = pausedSpacecraftIds([ uiPlan("100", "REJECTED", t0), // проекция и черновик паузу не снимают uiPlan("100", "PLANNED", t0 + 2 * hour), uiPlan("100", "DRAFT", t0) ]); expect(paused).toEqual(new Set(["100"])); }); it("снимает паузу, если оператор отправил исправленный план (в полёте) на том же/более позднем окне", () => { const paused = pausedSpacecraftIds([ uiPlan("100", "REJECTED", t0), uiPlan("100", "WAITING_DECISION", t0) ]); expect(paused.has("100")).toBe(false); }); it("не помечает паузой, если цепочка ушла вперёд (ACCEPTED после отказа)", () => { const paused = pausedSpacecraftIds([ uiPlan("100", "REJECTED", t0), uiPlan("100", "ACCEPTED", t0 + 5 * hour) ]); expect(paused.has("100")).toBe(false); }); it("без REJECTED никого не помечает", () => { expect(pausedSpacecraftIds([uiPlan("100", "WAITING_DECISION", t0)])).toEqual(new Set()); }); });