diff options
Diffstat (limited to 'src/LivingVillage.Desktop.Tests/DesktopTests.fs')
| -rw-r--r-- | src/LivingVillage.Desktop.Tests/DesktopTests.fs | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/src/LivingVillage.Desktop.Tests/DesktopTests.fs b/src/LivingVillage.Desktop.Tests/DesktopTests.fs index 52443e9..00e445b 100644 --- a/src/LivingVillage.Desktop.Tests/DesktopTests.fs +++ b/src/LivingVillage.Desktop.Tests/DesktopTests.fs @@ -208,3 +208,122 @@ type TaskPanelTests () = Assert.AreEqual<M5View>(dialogueView, afterTask) let _, closed = M5Interaction.apply ClosePanel world { dialogueView with Panel = TaskPanel } Assert.AreEqual<M5Panel>(WorldPanel, closed.Panel) + +// ---- P15 开局职业选择接入:bootstrap 职业状态 / 存档 round-trip / 面板联调 ---- + +module private OccupationFlowHarness = + + let withTempSave (action: string -> unit) = + let id = System.Guid.NewGuid().ToString("N") + let path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"living-village-p15-{id}.save") + try + action path + finally + if System.IO.File.Exists path then System.IO.File.Delete path + +[<TestClass>] +type OccupationFlowTests () = + + [<TestMethod>] + member _.BootstrapStampsOccupationMoneyEnergyAndTodaysOfferedTask () = + for kind in Occupation.all do + let state = WorldBootstrap.occupationStateFor 42UL kind 0L + let task = state.Today.Value + Assert.AreEqual<Occupation.TaskState>(Occupation.Offered, task.State) + Assert.IsTrue(Occupation.taskPoolOf kind |> List.contains task.TemplateId) + Assert.AreEqual<Occupation.DailyTask>( + (Occupation.dailyTaskOf 42UL (Occupation.occupationSeedOf kind) 0L kind).Value, + task) + let world = WorldBootstrap.initialWorldWithOccupation true 42UL 4 (Some state) + Assert.AreEqual<float32>(float32 state.Profile.InitialMoney, world.Avatar.Mind.Needs.Money) + Assert.AreEqual<float32>(state.Profile.InitialEnergy, world.Avatar.Mind.Needs.Energy) + + [<TestMethod>] + member _.ChosenOccupationAndTodaysTaskRoundTripThroughV3Save () = + for kind in Occupation.all do + let state = WorldBootstrap.occupationStateFor 42UL kind (3L * Sim.ticksPerDay + 120L) + let world = WorldBootstrap.initialWorldWithOccupation false 42UL 4 (Some state) + OccupationFlowHarness.withTempSave (fun path -> + WorldSave.saveToFileWith (Some state) path world + let text = System.IO.File.ReadAllText path + Assert.IsTrue(text.StartsWith("LV_WORLD_SAVE_V3"), "chosen occupation must save as v3") + match WorldSave.loadFromFileWith path with + | Ok (restoredWorld, Some restoredState) -> + Assert.AreEqual<Occupation.Kind>(kind, restoredState.Profile.Kind) + Assert.AreEqual<Occupation.State>(state, restoredState) + Assert.AreEqual<string>(text, WorldSave.saveWith (Some restoredState) restoredWorld) + | Ok (_, None) -> Assert.Fail($"v3 occupation lost for {kind}") + | Error message -> Assert.Fail($"v3 round trip failed for {kind}: {message}")) + + [<TestMethod>] + member _.TaskPanelShowsTheChosenOccupationsOfferedTask () = + let world = Sim.initialWorldN 42UL 4 + for kind in Occupation.all do + let state = WorldBootstrap.occupationStateFor 42UL kind 0L + let view = { M5Interaction.initial with Task = Some state } + let _, taskView = M5Interaction.apply ShowTaskPanel world view + let lines = M5Interaction.panelLines world taskView + Assert.IsTrue(lines |> List.contains (Occupation.nameOf kind), $"occupation name missing for {kind}") + Assert.IsTrue( + lines |> List.exists (fun line -> line.Contains(Occupation.taskNameOf state.Today.Value.TemplateId)), + $"task name missing for {kind}") + Assert.IsTrue(lines |> List.exists (fun line -> line.Contains("待接受")), $"offered state missing for {kind}") + + [<TestMethod>] + member _.RefreshOccupationTodayKeepsSameDayAndRegeneratesForTheNewDay () = + let state = WorldBootstrap.occupationStateFor 42UL Occupation.Farmer 0L + Assert.AreEqual<Occupation.State>(state, WorldBootstrap.refreshOccupationToday 42UL state (Sim.ticksPerDay - 1L)) + let day1 = WorldBootstrap.refreshOccupationToday 42UL state Sim.ticksPerDay + Assert.AreEqual<Occupation.DailyTask option>( + Some (Occupation.dailyTaskOf 42UL (Occupation.occupationSeedOf Occupation.Farmer) 1L Occupation.Farmer).Value, + day1.Today) + + [<TestMethod>] + member _.ChosenOccupationFirstDayTaskReachesDoneThroughItsSignal () = + // 既有事件通道:交易/对话模板由 InteractionEvent 经 signalOfInteraction 推进; + // 到达瓦片/夜水边/观察由调用方折叠为信号(Sim.step 不感知)。 + let eventOf (template: Occupation.TaskTemplateId) : Sim.InteractionEvent option = + match template with + | Occupation.DeliverGrain -> + Some { Tick = 0L; Kind = Sim.TradeEvent(NpcId 1, Sim.playerId, Food, 1, 1.0f) } + | Occupation.SellFish -> + Some { Tick = 0L; Kind = Sim.TradeEvent(NpcId 1, Sim.playerId, Fish, 1, 1.0f) } + | Occupation.Resell -> + Some { Tick = 0L; Kind = Sim.TradeEvent(NpcId 1, Sim.playerId, Spice, 1, 1.0f) } + | Occupation.BuyGoods -> + Some { Tick = 0L; Kind = Sim.TradeEvent(Sim.playerId, NpcId 1, Spice, 1, 1.0f) } + | Occupation.HelpWork + | Occupation.MarketInquiry + | Occupation.ReasonDebate -> + Some + { Tick = 0L + Kind = + Sim.DialogueEvent + { Tick = 0L + Actor = Sim.playerId + Target = NpcId 0 + Intent = SmallTalk + Response = Friendly + Valence = 0.3f } } + | Occupation.TillSoil + | Occupation.NightCatch + | Occupation.ObserveNotes -> None + let foldedSignal (template: Occupation.TaskTemplateId) : Occupation.TaskSignal = + match template with + | Occupation.TillSoil -> Occupation.ArrivedAt(52, 38) + | Occupation.NightCatch -> Occupation.NightAtWater + | Occupation.ObserveNotes -> Occupation.Observed + | _ -> failwith "event-backed template" + for kind in Occupation.all do + let state = WorldBootstrap.occupationStateFor 42UL kind 0L + let offered = state.Today.Value + Assert.AreEqual<Occupation.TaskState>(Occupation.Offered, offered.State) + Assert.IsTrue(Occupation.taskPoolOf kind |> List.contains offered.TemplateId) + let active = { state with Today = Some(Occupation.acceptTask offered) } + let completed = + match eventOf offered.TemplateId with + | Some event -> Occupation.advanceTask event active + | None -> + { active with + Today = active.Today |> Option.map (Occupation.applySignal (foldedSignal offered.TemplateId)) } + Assert.AreEqual<Occupation.TaskState>(Occupation.Done, completed.Today.Value.State) |
