namespace SomhairlesDream.Shared.Tests open System open Xunit open SomhairlesDream.Shared module RunStateTests = let private at hour = DateTimeOffset(2026, 9, 20, hour, 0, 0, 0, TimeSpan.Zero) [] let ``a new run is idle with no design work scheduled`` () = let snapshot = RunState.initial "heritage-001" "run-001" (at 8) Assert.Equal("heritage-001", snapshot.ProjectId) Assert.Equal("run-001", snapshot.RunId) Assert.Equal(RunStatus.Idle, snapshot.Status) Assert.Equal(0, snapshot.CurrentVersion) Assert.Equal(0, snapshot.TargetVersion) Assert.False(snapshot.NextDesignRunScheduled) Assert.Equal("等待设计运行", snapshot.Message) [] let ``starting a run increments the target version and heartbeat keeps it running`` () = let started = RunState.initial "heritage-001" "run-001" (at 8) |> RunState.start (at 9) let alive = RunState.heartbeat (at 9) started Assert.Equal(RunStatus.Running, alive.Status) Assert.Equal(1, alive.TargetVersion) Assert.Equal(Some(at 9), alive.HeartbeatAt) [] let ``a running run becomes stale after the heartbeat window`` () = let started = RunState.initial "heritage-001" "run-001" (at 8) |> RunState.start (at 9) let stale = RunState.statusAt (TimeSpan.FromMinutes 2.) (at 10) started Assert.Equal(RunStatus.Stale, stale.Status) Assert.Equal("心跳超时,等待重连", stale.Message) [] let ``publishing a mesh records the current version and makes the run successful`` () = let started = RunState.initial "heritage-001" "run-001" (at 8) |> RunState.start (at 9) let mesh = { Version = 1 Label = "基础结构" Vertices = [| { X = 0.; Y = 0.; Z = 0. } |] Faces = [||] } let published = RunState.publish (at 9) mesh started Assert.Equal(RunStatus.Succeeded, published.Status) Assert.Equal(1, published.CurrentVersion) Assert.Equal(Some mesh, published.Mesh) Assert.Equal("已发布基础结构", published.Message)