summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/FundLab.Api.Tests/BondQuoteProbeTests.fs74
-rw-r--r--tests/FundLab.Web.Tests/BoundaryTests.fs35
2 files changed, 97 insertions, 12 deletions
diff --git a/tests/FundLab.Api.Tests/BondQuoteProbeTests.fs b/tests/FundLab.Api.Tests/BondQuoteProbeTests.fs
index bed590a..1e19f77 100644
--- a/tests/FundLab.Api.Tests/BondQuoteProbeTests.fs
+++ b/tests/FundLab.Api.Tests/BondQuoteProbeTests.fs
@@ -6,19 +6,35 @@ open Xunit
open FundLab.Api
module BondQuoteProbeSupport =
- let payload (code: string) (name: string option) (price: string option) (maturity: string option) =
- let optionalText =
- function
- | Some value -> sprintf "\"%s\"" value
- | None -> "null"
-
+ let private optionalText =
+ function
+ | Some value -> sprintf "\"%s\"" value
+ | None -> "null"
+
+ let payloadWith
+ (code: string)
+ (name: string option)
+ (price: string option)
+ (maturity: string option)
+ (cleanPrice: string option)
+ (accruedInterest: string option)
+ (date: string option)
+ (sourceRevision: string)
+ =
sprintf
- "{\"schema_version\":\"fund-lab.akshare.v1\",\"operation\":\"bond-quote\",\"source\":\"akshare\",\"source_revision\":\"akshare-test/eastmoney\",\"collected_at\":\"2026-09-21T08:00:00Z\",\"instrument\":{\"code\":\"%s\"},\"quote\":{\"name\":%s,\"price\":%s,\"maturity_date\":%s}}"
+ "{\"schema_version\":\"fund-lab.akshare.v1\",\"operation\":\"bond-quote\",\"source\":\"akshare\",\"source_revision\":\"%s\",\"collected_at\":\"2026-09-21T08:00:00Z\",\"instrument\":{\"code\":\"%s\"},\"quote\":{\"name\":%s,\"price\":%s,\"clean_price\":%s,\"accrued_interest\":%s,\"date\":%s,\"maturity_date\":%s}}"
+ sourceRevision
code
(optionalText name)
(optionalText price)
+ (optionalText cleanPrice)
+ (optionalText accruedInterest)
+ (optionalText date)
(optionalText maturity)
+ let payload (code: string) (name: string option) (price: string option) (maturity: string option) =
+ payloadWith code name price maturity None None None "akshare-test/eastmoney"
+
let collectorReturning (fetch: unit -> Result<string, string>) =
{ new IMarketDataCollector with
member _.Search(_, _) = Ok ""
@@ -89,15 +105,51 @@ type BondQuoteProbeTests(fixture: PostgresFixture) =
| Error failure -> failwithf "expected a bond quote, got %A" failure
[<Fact>]
+ member _.``probe parses clean price, accrued interest and date when present``() =
+ let json =
+ BondQuoteProbeSupport.payloadWith
+ "019547"
+ None
+ (Some "119.858")
+ None
+ (Some "119.858")
+ (Some "0.5000")
+ (Some "2026-09-21")
+ "akshare-test/eastmoney/hs-bond-daily"
+
+ let probe =
+ AkshareBondQuoteProbe(BondQuoteProbeSupport.collectorReturning (fun () -> Ok json)) :> IBondQuoteProbe
+
+ match probe.GetQuote("019547", CancellationToken.None) with
+ | Ok quote ->
+ Assert.Equal<decimal option>(Some 119.858m, quote.Price)
+ Assert.Equal<decimal option>(Some 119.858m, quote.CleanPrice)
+ Assert.Equal<decimal option>(Some 0.5m, quote.AccruedInterest)
+ Assert.Equal<DateOnly option>(Some(DateOnly(2026, 9, 21)), quote.Date)
+ Assert.Equal("akshare-test/eastmoney/hs-bond-daily", quote.SourceRevision)
+ | Error failure -> failwithf "expected a bond quote, got %A" failure
+
+ [<Fact>]
+ member _.``probe rejects a payload whose code disagrees with the request``() =
+ let json = BondQuoteProbeSupport.payload "123121" (Some "帝尔转债") (Some "101.5000") None
+
+ let probe =
+ AkshareBondQuoteProbe(BondQuoteProbeSupport.collectorReturning (fun () -> Ok json)) :> IBondQuoteProbe
+
+ match probe.GetQuote("110075", CancellationToken.None) with
+ | Error(InvalidMarketDataPayload message) -> Assert.Contains("does not match requested code", message)
+ | other -> failwithf "expected InvalidMarketDataPayload, got %A" other
+
+ [<Fact>]
member _.``probe surfaces a no valid quote collector failure``() =
let probe =
AkshareBondQuoteProbe(
- BondQuoteProbeSupport.collectorReturning (fun () -> Error "AKShare collector failed: no valid quote for 999999")
+ BondQuoteProbeSupport.collectorReturning (fun () -> Error "AKShare collector failed: no valid bond quote for 999999")
)
:> IBondQuoteProbe
match probe.GetQuote("999999", CancellationToken.None) with
- | Error(MarketDataCollectorUnavailable message) -> Assert.Contains("no valid quote for 999999", message)
+ | Error(MarketDataCollectorUnavailable message) -> Assert.Contains("no valid bond quote for 999999", message)
| other -> failwithf "expected MarketDataCollectorUnavailable, got %A" other
[<Fact>]
@@ -166,8 +218,12 @@ type BondQuoteProbeTests(fixture: PostgresFixture) =
Ok
{
Code = "123121"
+ SourceRevision = "akshare-test/eastmoney/hs-cov-spot"
Name = Some "帝尔转债"
Price = Some 101.5m
+ CleanPrice = Some 101.5m
+ AccruedInterest = None
+ Date = None
MaturityDate = Some(DateOnly(2026, 6, 3))
})
diff --git a/tests/FundLab.Web.Tests/BoundaryTests.fs b/tests/FundLab.Web.Tests/BoundaryTests.fs
index 4714632..2892633 100644
--- a/tests/FundLab.Web.Tests/BoundaryTests.fs
+++ b/tests/FundLab.Web.Tests/BoundaryTests.fs
@@ -463,7 +463,15 @@ module ConfirmationBoundaryTests =
let model = init ()
let raw : RawBondQuote =
- { code = "110075"; name = box "南航转债"; price = box "106.368"; maturityDate = null }
+ {
+ code = "110075"
+ name = box "南航转债"
+ price = box "106.368"
+ cleanPrice = null
+ accruedInterest = null
+ date = null
+ maturityDate = null
+ }
let updated, _ = update (BondQuoteCompleted(model.bondQuoteSeq, raw)) model
@@ -474,17 +482,38 @@ module ConfirmationBoundaryTests =
Assert.False(updated.bondQuoteInFlight)
[<Fact>]
+ let ``bond quote completion keeps clean price, accrued interest and quote date`` () =
+ let model = init ()
+
+ let raw : RawBondQuote =
+ {
+ code = "019547"
+ name = null
+ price = box "119.858"
+ cleanPrice = box "119.858"
+ accruedInterest = box "0.5000"
+ date = box "2026-09-21"
+ maturityDate = null
+ }
+
+ let updated, _ = update (BondQuoteCompleted(model.bondQuoteSeq, raw)) model
+
+ Assert.Equal(Some "119.858", updated.bondQuote |> Option.bind (fun quote -> quote.cleanPrice))
+ Assert.Equal(Some "0.5000", updated.bondQuote |> Option.bind (fun quote -> quote.accruedInterest))
+ Assert.Equal(Some "2026-09-21", updated.bondQuote |> Option.bind (fun quote -> quote.quoteDate))
+
+ [<Fact>]
let ``bond quote failure shows an unavailable message without a quote`` () =
let model = { init () with bondQuoteInFlight = true }
let updated, _ =
- update (BondQuoteFailed(model.bondQuoteSeq, "AKShare collector failed: no valid quote for 019547")) model
+ update (BondQuoteFailed(model.bondQuoteSeq, "AKShare collector failed: no valid bond quote for 019547")) model
Assert.Equal(None, updated.bondQuote)
let message = updated.bondQuoteMessage |> Option.defaultValue ""
Assert.Contains("不可用", message)
- Assert.Contains("no valid quote for 019547", message)
+ Assert.Contains("no valid bond quote for 019547", message)
Assert.False(updated.bondQuoteInFlight)
[<Fact>]