diff --git a/tests/Equinox.Cosmos.Integration/CosmosFixturesInfrastructure.fs b/tests/Equinox.Cosmos.Integration/CosmosFixturesInfrastructure.fs index 98ed85c4c..9cc24fd40 100644 --- a/tests/Equinox.Cosmos.Integration/CosmosFixturesInfrastructure.fs +++ b/tests/Equinox.Cosmos.Integration/CosmosFixturesInfrastructure.fs @@ -54,16 +54,14 @@ module SerilogHelpers = [] type EqxAct = | Tip | TipNotFound | TipNotModified - | ResponseForward | ResponseBackward | ResponseWaste + | ResponseForward | ResponseBackward | QueryForward | QueryBackward | Append | Resync | Conflict let (|EqxAction|) = function | Log.Tip _ -> EqxAct.Tip | Log.TipNotFound _ -> EqxAct.TipNotFound | Log.TipNotModified _ -> EqxAct.TipNotModified - | Log.Response (Direction.Forward, {count = 0}) -> EqxAct.ResponseWaste // TODO remove, see comment where these are emitted | Log.Response (Direction.Forward,_) -> EqxAct.ResponseForward - | Log.Response (Direction.Backward, {count = 0}) -> EqxAct.ResponseWaste // TODO remove, see comment where these are emitted | Log.Response (Direction.Backward,_) -> EqxAct.ResponseBackward | Log.Query (Direction.Forward,_,_) -> EqxAct.QueryForward | Log.Query (Direction.Backward,_,_) -> EqxAct.QueryBackward @@ -107,7 +105,7 @@ module SerilogHelpers = interface Serilog.Core.ILogEventSink with member __.Emit logEvent = writeSerilogEvent logEvent member __.Clear () = captured.Clear() member __.ChooseCalls chooser = captured |> Seq.choose chooser |> List.ofSeq - member __.ExternalCalls = __.ChooseCalls (function EqxEvent (EqxAction act) (*when act <> EqxAct.Waste*) -> Some act | _ -> None) + member __.ExternalCalls = __.ChooseCalls (function EqxEvent (EqxAction act) -> Some act | _ -> None) member __.RequestCharges = __.ChooseCalls (function EqxEvent (CosmosRequestCharge e) -> Some e | _ -> None) type TestsWithLogCapture(testOutputHelper) = diff --git a/tests/Equinox.Cosmos.Integration/CosmosIntegration.fs b/tests/Equinox.Cosmos.Integration/CosmosIntegration.fs index 494a7a953..49a21caf0 100644 --- a/tests/Equinox.Cosmos.Integration/CosmosIntegration.fs +++ b/tests/Equinox.Cosmos.Integration/CosmosIntegration.fs @@ -57,30 +57,43 @@ type Tests(testOutputHelper) = let addAndThenRemoveItemsManyTimesExceptTheLastOne context cartId skuId service count = addAndThenRemoveItems true context cartId skuId service count + let verifyRequestChargesMax rus = + let tripRequestCharges = [ for e, c in capture.RequestCharges -> sprintf "%A" e, c ] + test <@ float rus >= Seq.sum (Seq.map snd tripRequestCharges) @> + [] let ``Can roundtrip against Cosmos, correctly batching the reads [without using the Index for reads]`` context skuId = Async.RunSynchronously <| async { let! conn = connectToSpecifiedCosmosOrSimulator log - let batchSize = 3 - let service = Cart.createServiceWithoutOptimization conn batchSize log + let maxItemsPerRequest = 2 + let maxEventsPerBatch = 1 + let service = Cart.createServiceWithoutOptimization conn maxItemsPerRequest log capture.Clear() // for re-runs of the test let cartId = Guid.NewGuid() |> CartId // The command processing should trigger only a single read and a single write call - let addRemoveCount = 6 - do! addAndThenRemoveItemsManyTimesExceptTheLastOne context cartId skuId service addRemoveCount - test <@ [EqxAct.ResponseWaste; EqxAct.QueryBackward; EqxAct.Append] = capture.ExternalCalls @> - // Restart the counting - capture.Clear() + let addRemoveCount = 2 + let eventsPerAction = addRemoveCount * 2 - 1 + let batches = 4 + for i in [1..batches] do + do! addAndThenRemoveItemsManyTimesExceptTheLastOne context cartId skuId service addRemoveCount + let expectedBatchesOf2Items = + match i with + | 1 -> 1 // it does cost a single trip to determine there are 0 items + | i -> ceil(float (i-1) * float eventsPerAction / float maxItemsPerRequest / float maxEventsPerBatch) |> int + test <@ List.replicate expectedBatchesOf2Items EqxAct.ResponseBackward @ [EqxAct.QueryBackward; EqxAct.Append] = capture.ExternalCalls @> + verifyRequestChargesMax 39 // 37.15 + capture.Clear() // Validate basic operation; Key side effect: Log entries will be emitted to `capture` let! state = service.Read cartId - let expectedEventCount = 2 * addRemoveCount - 1 + let expectedEventCount = batches * eventsPerAction test <@ addRemoveCount = match state with { items = [{ quantity = quantity }] } -> quantity | _ -> failwith "nope" @> - // Need to read 4 batches to read 11 events in batches of 3 - let expectedBatches = ceil(float expectedEventCount/float batchSize) |> int - test <@ List.replicate (expectedBatches-1) EqxAct.ResponseBackward @ [EqxAct.ResponseBackward; EqxAct.QueryBackward] = capture.ExternalCalls @> + // Need 6 trips of 2 maxItemsPerRequest to read 12 events + test <@ let expectedResponses = ceil(float expectedEventCount/float maxItemsPerRequest/float maxEventsPerBatch) |> int + List.replicate expectedResponses EqxAct.ResponseBackward @ [EqxAct.QueryBackward] = capture.ExternalCalls @> + verifyRequestChargesMax 20 // 18.47 } []