summaryrefslogtreecommitdiff
path: root/tests/FundLab.Api.Tests/RebalanceArchiveTests.fs
diff options
context:
space:
mode:
Diffstat (limited to 'tests/FundLab.Api.Tests/RebalanceArchiveTests.fs')
-rw-r--r--tests/FundLab.Api.Tests/RebalanceArchiveTests.fs149
1 files changed, 149 insertions, 0 deletions
diff --git a/tests/FundLab.Api.Tests/RebalanceArchiveTests.fs b/tests/FundLab.Api.Tests/RebalanceArchiveTests.fs
new file mode 100644
index 0000000..ebb238b
--- /dev/null
+++ b/tests/FundLab.Api.Tests/RebalanceArchiveTests.fs
@@ -0,0 +1,149 @@
+namespace FundLab.Api.Tests
+
+open System
+open Xunit
+open FundLab.Api
+
+[<Collection("postgres")>]
+type RebalanceArchiveTests(fixture: PostgresFixture) =
+ let sharedRepository =
+ lazy
+ let value = FundRepository(fixture.ConnectionString)
+ value.EnsureSchema()
+ value
+
+ let repository () = sharedRepository.Value
+
+ let seedInstrument () =
+ let code = Random.Shared.Next(0, 1000000).ToString("D6")
+
+ let payload =
+ {
+ Source = "akshare"
+ SourceRevision = "akshare-test/eastmoney"
+ CollectedAt = DateTimeOffset(2026, 9, 21, 8, 0, 0, TimeSpan.Zero)
+ Instruments = [ { Code = code; Name = "再平衡归档测试基金"; FundType = None } ]
+ }
+
+ repository().UpsertInstruments(payload, "rebalance-archive-test-hash")
+ code
+
+ let createFund (initialCash: decimal) =
+ let command =
+ {
+ Name = "再平衡归档测试 FOF"
+ InitialCash = initialCash
+ InitialUnitNav = 1.00000000m
+ IsSynthetic = true
+ }
+
+ let key = fixture.Key(sprintf "rebalance-archive-fund-%s" (Guid.NewGuid().ToString("N")))
+
+ match repository().CreateFund(key, command) with
+ | FundWriteResult.Created fund -> fund.Id
+ | other -> failwithf "unexpected fund creation result: %A" other
+
+ let app () = App.createApplication (repository ())
+
+ let createPlan (fundId: Guid) (code: string) =
+ let key = fixture.Key(sprintf "rebalance-archive-plan-%s" (Guid.NewGuid().ToString("N")))
+
+ match
+ repository().CreateRebalancePlan(key, fundId, { Targets = [ { InstrumentCode = code; TargetPercent = 100.00m } ] })
+ with
+ | RebalanceWriteResult.RebalancePlanCreated plan -> plan.Id
+ | other -> failwithf "unexpected rebalance plan result: %A" other
+
+ let archiveViaApi fundId planId =
+ PersistenceTestHelpers.invoke
+ (app ())
+ "POST"
+ (sprintf "/api/funds/%O/rebalance/plans/%O/archive" fundId planId)
+ [ "Authorization", "Bearer test-token" ]
+ "{}"
+
+ let previewViaApi fundId planId =
+ PersistenceTestHelpers.invoke
+ (app ())
+ "GET"
+ (sprintf "/api/funds/%O/rebalance/plans/%O/preview" fundId planId)
+ [ "Authorization", "Bearer test-token" ]
+ ""
+
+ let executeViaApi fundId planId =
+ PersistenceTestHelpers.invoke
+ (app ())
+ "POST"
+ (sprintf "/api/funds/%O/rebalance/plans/%O/execute" fundId planId)
+ [ "Authorization", "Bearer test-token" ]
+ "{}"
+
+ let plansViaApi fundId =
+ PersistenceTestHelpers.invoke
+ (app ())
+ "GET"
+ (sprintf "/api/funds/%O/rebalance/plans" fundId)
+ [ "Authorization", "Bearer test-token" ]
+ ""
+
+ [<Fact>]
+ member _.``archiving an active plan marks it archived in the plan list``() =
+ let fundId = createFund 1000.00m
+ let code = seedInstrument ()
+ let planId = createPlan fundId code
+
+ let status, body = archiveViaApi fundId planId
+ Assert.Equal(200, status)
+ Assert.Contains("\"status\":\"archived\"", body)
+
+ let _, plansBody = plansViaApi fundId
+ Assert.Contains("\"status\":\"archived\"", plansBody)
+
+ [<Fact>]
+ member _.``archived plan cannot be previewed``() =
+ let fundId = createFund 1000.00m
+ let code = seedInstrument ()
+ let planId = createPlan fundId code
+
+ let _, _ = archiveViaApi fundId planId
+ let status, body = previewViaApi fundId planId
+
+ Assert.Equal(409, status)
+ Assert.Contains("PLAN_ARCHIVED", body)
+
+ [<Fact>]
+ member _.``archived plan cannot be executed``() =
+ let fundId = createFund 1000.00m
+ let code = seedInstrument ()
+ let planId = createPlan fundId code
+
+ let _, _ = archiveViaApi fundId planId
+ let status, body = executeViaApi fundId planId
+
+ Assert.Equal(409, status)
+ Assert.Contains("PLAN_ARCHIVED", body)
+
+ [<Fact>]
+ member _.``archiving an already archived plan is idempotent``() =
+ let fundId = createFund 1000.00m
+ let code = seedInstrument ()
+ let planId = createPlan fundId code
+
+ let firstStatus, firstBody = archiveViaApi fundId planId
+ Assert.Equal(200, firstStatus)
+ Assert.Contains("\"status\":\"archived\"", firstBody)
+
+ let secondStatus, secondBody = archiveViaApi fundId planId
+ Assert.Equal(200, secondStatus)
+ Assert.Contains("\"status\":\"archived\"", secondBody)
+
+ [<Fact>]
+ member _.``archiving a plan owned by another fund answers 404``() =
+ let fundId = createFund 1000.00m
+ let otherFundId = createFund 1000.00m
+ let code = seedInstrument ()
+ let planId = createPlan fundId code
+
+ let status, body = archiveViaApi otherFundId planId
+ Assert.Equal(404, status)
+ Assert.Contains("PLAN_NOT_FOUND", body)