diff options
Diffstat (limited to 'tests/FundLab.Api.Tests/OrderTests.fs')
| -rw-r--r-- | tests/FundLab.Api.Tests/OrderTests.fs | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/tests/FundLab.Api.Tests/OrderTests.fs b/tests/FundLab.Api.Tests/OrderTests.fs index 4fc1da0..1ab8fb8 100644 --- a/tests/FundLab.Api.Tests/OrderTests.fs +++ b/tests/FundLab.Api.Tests/OrderTests.fs @@ -1880,3 +1880,133 @@ type RedemptionTests(fixture: PostgresFixture) = Assert.Contains("\"instrumentCode\":\"" + code + "\"", listResponse) Assert.Contains("\"status\":\"confirmed\"", listResponse) Assert.Contains("\"confirmedProceeds\":\"99.60\"", listResponse) + +[<Collection("postgres")>] +type CapitalDepositTests(fixture: PostgresFixture) = + let sharedRepository = + lazy + let value = FundRepository(fixture.ConnectionString) + value.EnsureSchema() + value + + let repository () = sharedRepository.Value + + let createFund (initialCash: decimal) = + let command = + { + Name = "追加资金测试 FOF" + InitialCash = initialCash + InitialUnitNav = 1.00000000m + IsSynthetic = true + } + + let key = fixture.Key(sprintf "capital-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 postDeposit (fundId: Guid) (idempotencyKey: string) (amount: string) (note: string) = + let body = sprintf "{\"amount\":\"%s\",\"note\":\"%s\"}" amount note + + PersistenceTestHelpers.invoke + (app ()) + "POST" + (sprintf "/api/funds/%O/capital/deposit" fundId) + [ + "Authorization", "Bearer test-token" + "Idempotency-Key", idempotencyKey + ] + body + + let availableCash fundId = + use connection = new NpgsqlConnection(fixture.ConnectionString) + connection.Open() + use command = connection.CreateCommand() + command.CommandText <- "SELECT available_cash FROM funds WHERE id = @fund_id" + let parameter = command.Parameters.Add("fund_id", NpgsqlTypes.NpgsqlDbType.Uuid) + parameter.Value <- box fundId + command.ExecuteScalar() :?> decimal + + let depositCount fundId = + use connection = new NpgsqlConnection(fixture.ConnectionString) + connection.Open() + use command = connection.CreateCommand() + command.CommandText <- "SELECT count(*) FROM fund_capital_deposits WHERE fund_id = @fund_id" + let parameter = command.Parameters.Add("fund_id", NpgsqlTypes.NpgsqlDbType.Uuid) + parameter.Value <- box fundId + command.ExecuteScalar() :?> int64 + + [<Fact>] + member _.``capital deposit credits available cash and records the deposit``() = + let fundId = createFund 10000.00m + let key = fixture.Key(sprintf "cap-%s" (Guid.NewGuid().ToString("N"))) + + let status, response = postDeposit fundId key "500.00" "首次追加" + + Assert.Equal(201, status) + Assert.Contains("\"amount\":\"500.00\"", response) + Assert.Equal(10500.00m, availableCash fundId) + Assert.Equal(1L, depositCount fundId) + + [<Fact>] + member _.``capital deposit replay with the same key credits cash exactly once``() = + let fundId = createFund 10000.00m + let key = fixture.Key(sprintf "cap-%s" (Guid.NewGuid().ToString("N"))) + + let firstStatus, firstResponse = postDeposit fundId key "300.00" "重复追加" + let secondStatus, secondResponse = postDeposit fundId key "300.00" "重复追加" + + Assert.Equal(201, firstStatus) + Assert.Equal(200, secondStatus) + Assert.Equal(firstResponse, secondResponse) + Assert.Equal(10300.00m, availableCash fundId) + Assert.Equal(1L, depositCount fundId) + + let conflictingStatus, conflictingResponse = postDeposit fundId key "100.00" "不同金额" + + Assert.Equal(409, conflictingStatus) + Assert.Contains("IDEMPOTENCY_CONFLICT", conflictingResponse) + Assert.Equal(10300.00m, availableCash fundId) + + [<Fact>] + member _.``invalid deposit amounts are rejected without any cash movement``() = + let fundId = createFund 10000.00m + + for amount in [ "0.00"; "-5.00"; "1.234" ] do + let key = fixture.Key(sprintf "cap-%s" (Guid.NewGuid().ToString("N"))) + let status, response = postDeposit fundId key amount "非法" + Assert.Equal(400, status) + Assert.Contains("INVALID_CAPITAL_REQUEST", response) + + Assert.Equal(10000.00m, availableCash fundId) + Assert.Equal(0L, depositCount fundId) + + [<Fact>] + member _.``deposit for an unknown fund returns 404``() = + let key = fixture.Key(sprintf "cap-%s" (Guid.NewGuid().ToString("N"))) + let status, response = postDeposit (Guid.NewGuid()) key "100.00" "无基金" + + Assert.Equal(404, status) + Assert.Contains("FUND_NOT_FOUND", response) + + [<Fact>] + member _.``deposit history is exposed via GET``() = + let fundId = createFund 10000.00m + let _, _ = postDeposit fundId (fixture.Key(sprintf "cap-%s" (Guid.NewGuid().ToString("N")))) "111.00" "第一笔" + let _, _ = postDeposit fundId (fixture.Key(sprintf "cap-%s" (Guid.NewGuid().ToString("N")))) "222.00" "第二笔" + + let status, response = + PersistenceTestHelpers.invoke + (app ()) + "GET" + (sprintf "/api/funds/%O/capital/deposits" fundId) + [ "Authorization", "Bearer test-token" ] + "" + + Assert.Equal(200, status) + Assert.Contains("\"amount\":\"111.00\"", response) + Assert.Contains("\"amount\":\"222.00\"", response) + Assert.Contains("第一笔", response) |
