From 545f2a507fd0138f7fd50ed7fb2631d0273db625 Mon Sep 17 00:00:00 2001 From: "Somhairle H. Marisol" Date: Tue, 22 Sep 2026 04:59:12 +0800 Subject: Integrate bond and stock positions into fund valuation (3d-23) --- tests/FundLab.Web.Tests/BoundaryTests.fs | 69 ++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) (limited to 'tests/FundLab.Web.Tests/BoundaryTests.fs') diff --git a/tests/FundLab.Web.Tests/BoundaryTests.fs b/tests/FundLab.Web.Tests/BoundaryTests.fs index 3a9ec61..d8bb472 100644 --- a/tests/FundLab.Web.Tests/BoundaryTests.fs +++ b/tests/FundLab.Web.Tests/BoundaryTests.fs @@ -709,6 +709,75 @@ module ConfirmationBoundaryTests = Assert.Equal("10", updated.bondPositions[0].quantity) Assert.False(updated.bondPositionsInFlight) + [] + let ``valuation completion stores totals and per-position marks`` () = + let fundId = "3fa85f64-5717-4562-b3fc-2c963f66afa6" + + let model = + { init () with + createdFund = Some(FundBoundaryTests.sampleFund fundId) + valuationReadSeq = 1 + valuationInFlight = true } + + let raw : RawFundValuation = + { + fundId = fundId + currency = "CNY" + cash = "10000.00" + positionsMarketValue = "126320.68" + portfolioValue = "136320.68" + pricedPositions = 1 + unavailablePositions = 1 + positions = + [| + { + instrumentCode = "600519" + name = box "贵州茅台" + assetClass = "stock" + quantity = "100.00000000" + price = box "1252.57" + priceSource = box "live" + marketValue = box "125257.00" + status = "priced" + } + { + instrumentCode = "110075" + name = box "南航转债" + assetClass = "bond" + quantity = "10.00000000" + price = null + priceSource = null + marketValue = null + status = "unavailable" + } + |] + } + + let updated, _ = update (ValuationReadCompleted(1, fundId, raw)) model + + let valuation = updated.valuation.Value + Assert.Equal("136320.68", valuation.portfolioValue) + Assert.Equal(1, valuation.unavailablePositions) + Assert.Equal(2, valuation.positions.Length) + Assert.Equal("unavailable", valuation.positions[1].status) + Assert.False(updated.valuationInFlight) + + [] + let ``valuation failure clears totals and surfaces a message`` () = + let fundId = "3fa85f64-5717-4562-b3fc-2c963f66afa6" + + let model = + { init () with + createdFund = Some(FundBoundaryTests.sampleFund fundId) + valuationReadSeq = 1 + valuationInFlight = true } + + let updated, _ = update (ValuationReadFailed(1, fundId, "collector exited with code 2")) model + + Assert.Equal(None, updated.valuation) + Assert.Contains("组合估值不可用", updated.valuationMessage |> Option.defaultValue "") + Assert.False(updated.valuationInFlight) + [] let ``order decode keeps decimal strings and decodes option fields`` () = let confirmed = Api.decodeOrder (sampleConfirmedRaw ()) -- cgit v1.2.3