Skip to content

Commit

Permalink
DRY Location test
Browse files Browse the repository at this point in the history
  • Loading branch information
bartelink committed Nov 29, 2019
1 parent 191712a commit b38335b
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 24 deletions.
1 change: 1 addition & 0 deletions samples/Fc/Domain.Tests/Infrastructure.fs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ open Serilog
open System

let (|Id|) (x : Guid) = x.ToString "N" |> FSharp.UMX.UMX.tag
let inline mkId () = Guid.NewGuid() |> (|Id|)
let (|Ids|) (xs : Guid[]) = xs |> Array.map (|Id|)
let (|IdsAtLeastOne|) (Id x, Ids xs) = Seq.append xs (Seq.singleton x) |> Seq.toArray

Expand Down
45 changes: 23 additions & 22 deletions samples/Fc/Domain.Tests/LocationTests.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module LocationTests

open FsCheck.Xunit
open FSharp.UMX
open Location
open Swensen.Unquote
open System
Expand All @@ -25,44 +26,44 @@ module Location =
let epochs = Epoch.create (Epoch.resolve store) maxAttempts
create (zeroBalance, shouldClose) (series, epochs)

let [<Property>] ``parallel properties`` (IdsAtLeastOne locations) (deltas : _[]) maxEvents = Async.RunSynchronously <| async {
let store = Equinox.MemoryStore.VolatileStore()
let zeroBalance = 0
let maxEvents = max 1 maxEvents
let shouldClose (state : Epoch.Folds.OpenState) = state.count > maxEvents
let service = Location.MemoryStore.createService (zeroBalance, shouldClose) store
let run (service : LocationService) (IdsAtLeastOne locations, deltas : _[]) = Async.RunSynchronously <| async {
let runId = mkId () // Need to make making state in store unique when replaying or shrinking
let locations = locations |> Array.map (fun x -> % (sprintf "%O_%O" runId x))

let updates = deltas |> Seq.mapi (fun i x -> locations.[i % locations.Length], x) |> Seq.cache

(* Apply random deltas *)

let adjust delta (bal : Epoch.Folds.Balance) =
let value = max -bal delta
if value = 0 then 0, []
else value, [Location.Epoch.Events.Delta { value = value }]
let updates = deltas |> Seq.mapi (fun i x -> locations.[i % locations.Length], x) |> Seq.cache

let! appliedDeltas = seq { for loc,x in updates -> async { let! _,eff = service.Execute(loc, adjust x) in return loc,eff } } |> Async.Parallel
let! balances = seq { for loc in locations -> async { let! bal,() = service.Execute(loc,(fun _ -> (),[])) in return loc,bal } } |> Async.Parallel
let expectedBalances = Seq.append (seq { for l in locations -> l, 0}) appliedDeltas |> Seq.groupBy fst |> Seq.map (fun (l,xs) -> l, xs |> Seq.sumBy snd) |> Set.ofSeq

(* Verify loading yields identical state *)

let! balances = seq { for loc in locations -> async { let! bal,() = service.Execute(loc,(fun _ -> (),[])) in return loc,bal } } |> Async.Parallel
test <@ expectedBalances = Set.ofSeq balances @> }

let [<Property>] ``MemoryStore properties`` maxEvents args =
let store = Equinox.MemoryStore.VolatileStore()
let zeroBalance = 0
let maxEvents = max 1 maxEvents
let shouldClose (state : Epoch.Folds.OpenState) = state.count > maxEvents
let service = Location.MemoryStore.createService (zeroBalance, shouldClose) store
run service args

type Cosmos(testOutput) =

let context,cache = Cosmos.connect ()

// NOTE this works very well for as long as we can guarantee we have a single instance of the Daemon in play
// i.e. we'll need to remove this e.g. if the Cosmos ones can run at the same time as this suite
let log = testOutput |> TestOutputAdapter |> createLogger
do Serilog.Log.Logger <- log

let [<Property>] properties (IdsAtLeastOne locations) (deltas : _[]) maxEvents = Async.RunSynchronously <| async {
let [<Property>] properties maxEvents args =
let zeroBalance = 0
let maxEvents = max 1 maxEvents
let shouldClose (state : Epoch.Folds.OpenState) = state.count > maxEvents
let service = Location.Cosmos.createService (zeroBalance, shouldClose) (context,cache,Int32.MaxValue)
let adjust delta (bal : Epoch.Folds.Balance) =
let value = max -bal delta
if value = 0 then 0, []
else value, [Location.Epoch.Events.Delta { value = value }]
let updates = deltas |> Seq.mapi (fun i x -> locations.[i % locations.Length], x) |> Seq.cache

let! appliedDeltas = seq { for loc,x in updates -> async { let! _,eff = service.Execute(loc, adjust x) in return loc,eff } } |> Async.Parallel
let! balances = seq { for loc in locations -> async { let! bal,() = service.Execute(loc,(fun _ -> (),[])) in return loc,bal } } |> Async.Parallel
let expectedBalances = Seq.append (seq { for l in locations -> l, 0}) appliedDeltas |> Seq.groupBy fst |> Seq.map (fun (l,xs) -> l, xs |> Seq.sumBy snd) |> Set.ofSeq
test <@ expectedBalances = Set.ofSeq balances @> }
run service args
4 changes: 2 additions & 2 deletions samples/Fc/Domain/Location.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ type LocationService internal (zeroBalance, shouldClose, series : Series.Service

[<AutoOpen>]
module Helpers =
let create (zeroBalance, shouldClose) (series,epochs) =
let create (zeroBalance, shouldClose) (series, epochs) =
LocationService(zeroBalance, shouldClose, series, epochs)

module Cosmos =

let createService (zeroBalance, shouldClose) (context,cache,maxAttempts) =
let createService (zeroBalance, shouldClose) (context, cache, maxAttempts) =
let series = Series.Cosmos.createService (context, cache, maxAttempts)
let epochs = Epoch.Cosmos.createService (context, cache, maxAttempts)
create (zeroBalance, shouldClose) (series, epochs)

0 comments on commit b38335b

Please sign in to comment.