module Hydra.Logging.AccountingSpec where import Hydra.Prelude hiding (first, insert) import Test.Hydra.Prelude hiding (pending) import Data.Map.Strict qualified as Map import Hydra.Logging.Monitoring.Accounting qualified as A spec :: Spec spec :: Spec spec = do String -> Expectation -> SpecWith (Arg Expectation) forall a. (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it String "counts an input once and preserves its first processing time across retries" (Expectation -> SpecWith (Arg Expectation)) -> Expectation -> SpecWith (Arg Expectation) forall a b. (a -> b) -> a -> b $ do let (Bool first, Int _, Accounting Text Int s1) = Int -> Double -> Double -> Word64 -> Int -> Accounting Text Int -> (Bool, Int, Accounting Text Int) forall txid head. Ord txid => Int -> Double -> Double -> Word64 -> txid -> Accounting head txid -> (Bool, Int, Accounting head txid) A.request Int 10 Double 3600 Double 0 Word64 1 (Int 42 :: Int) Accounting Text Int initial (Bool retry, Int _, Accounting Text Int s2) = Int -> Double -> Double -> Word64 -> Int -> Accounting Text Int -> (Bool, Int, Accounting Text Int) forall txid head. Ord txid => Int -> Double -> Double -> Word64 -> txid -> Accounting head txid -> (Bool, Int, Accounting head txid) A.request Int 10 Double 3600 Double 0.5 Word64 1 Int 42 Accounting Text Int s1 (Bool _, Maybe Double _, [Double] times, Accounting Text Int _) = Double -> SnapshotKey Text -> [Int] -> Accounting Text Int -> (Bool, Maybe Double, [Double], Accounting Text Int) forall head txid. (Eq head, Ord txid) => Double -> SnapshotKey head -> [txid] -> Accounting head txid -> (Bool, Maybe Double, [Double], Accounting head txid) A.confirmSnapshot Double 0.6 SnapshotKey Text key1 [Int 42] Accounting Text Int s2 (Bool first, Bool retry, [Double] times) (Bool, Bool, [Double]) -> (Bool, Bool, [Double]) -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` (Bool True, Bool False, [Double 0.6]) String -> Expectation -> SpecWith (Arg Expectation) forall a. (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it String "counts externally duplicated messages but preserves their transaction's first time" (Expectation -> SpecWith (Arg Expectation)) -> Expectation -> SpecWith (Arg Expectation) forall a b. (a -> b) -> a -> b $ do let (Bool _, Int _, Accounting Text Int s1) = Int -> Double -> Double -> Word64 -> Int -> Accounting Text Int -> (Bool, Int, Accounting Text Int) forall txid head. Ord txid => Int -> Double -> Double -> Word64 -> txid -> Accounting head txid -> (Bool, Int, Accounting head txid) A.request Int 10 Double 3600 Double 0 Word64 1 (Int 42 :: Int) Accounting Text Int initial (Bool fresh, Int _, Accounting Text Int s2) = Int -> Double -> Double -> Word64 -> Int -> Accounting Text Int -> (Bool, Int, Accounting Text Int) forall txid head. Ord txid => Int -> Double -> Double -> Word64 -> txid -> Accounting head txid -> (Bool, Int, Accounting head txid) A.request Int 10 Double 3600 Double 0.5 Word64 2 Int 42 Accounting Text Int s1 Bool fresh Bool -> Bool -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Bool True Accounting Text Int -> Map Int Double forall head txid. Accounting head txid -> Map txid Double A.timers Accounting Text Int s2 Map Int Double -> Map Int Double -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Int -> Double -> Map Int Double forall k a. k -> a -> Map k a Map.singleton Int 42 Double 0 String -> Expectation -> SpecWith (Arg Expectation) forall a. (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it String "bounds both timer indexes and expires entries without more traffic" (Expectation -> SpecWith (Arg Expectation)) -> Expectation -> SpecWith (Arg Expectation) forall a b. (a -> b) -> a -> b $ do let insert :: A.Accounting Text Int -> Int -> A.Accounting Text Int insert :: Accounting Text Int -> Int -> Accounting Text Int insert Accounting Text Int st Int i = let (Bool _, Int _, Accounting Text Int next) = Int -> Double -> Double -> Word64 -> Int -> Accounting Text Int -> (Bool, Int, Accounting Text Int) forall txid head. Ord txid => Int -> Double -> Double -> Word64 -> txid -> Accounting head txid -> (Bool, Int, Accounting head txid) A.request Int 3 Double 10 (Int -> Double forall a b. (Integral a, Num b) => a -> b fromIntegral Int i) (Int -> Word64 forall a b. (Integral a, Num b) => a -> b fromIntegral Int i) Int i Accounting Text Int st in Accounting Text Int next full :: Accounting Text Int full = (Accounting Text Int -> Int -> Accounting Text Int) -> Accounting Text Int -> [Int] -> Accounting Text Int forall b a. (b -> a -> b) -> b -> [a] -> b forall (t :: * -> *) b a. Foldable t => (b -> a -> b) -> b -> t a -> b foldl' Accounting Text Int -> Int -> Accounting Text Int insert Accounting Text Int initial [Int 1 .. Int 100] (Int removed, Accounting Text Int expired) = Int -> Double -> Double -> Accounting Text Int -> (Int, Accounting Text Int) forall txid head. Ord txid => Int -> Double -> Double -> Accounting head txid -> (Int, Accounting head txid) A.prune Int 3 Double 10 Double 111 Accounting Text Int full Map Int Double -> [Int] forall k a. Map k a -> [k] Map.keys (Accounting Text Int -> Map Int Double forall head txid. Accounting head txid -> Map txid Double A.timers Accounting Text Int full) [Int] -> [Int] -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` [Int 98, Int 99, Int 100] Map (Double, Int) () -> Int forall k a. Map k a -> Int Map.size (Accounting Text Int -> Map (Double, Int) () forall head txid. Accounting head txid -> Map (Double, txid) () A.orderedTimers Accounting Text Int full) Int -> Int -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Int 3 Int removed Int -> Int -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Int 3 Accounting Text Int -> Map Int Double forall head txid. Accounting head txid -> Map txid Double A.timers Accounting Text Int expired Map Int Double -> Map Int Double -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Map Int Double forall k a. Map k a Map.empty Accounting Text Int -> Map (Double, Int) () forall head txid. Accounting head txid -> Map (Double, txid) () A.orderedTimers Accounting Text Int expired Map (Double, Int) () -> Map (Double, Int) () -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Map (Double, Int) () forall k a. Map k a Map.empty String -> Expectation -> SpecWith (Arg Expectation) forall a. (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it String "removes invalid or dropped transactions from both indexes" (Expectation -> SpecWith (Arg Expectation)) -> Expectation -> SpecWith (Arg Expectation) forall a b. (a -> b) -> a -> b $ do let (Bool _, Int _, Accounting Text Int pending) = Int -> Double -> Double -> Word64 -> Int -> Accounting Text Int -> (Bool, Int, Accounting Text Int) forall txid head. Ord txid => Int -> Double -> Double -> Word64 -> txid -> Accounting head txid -> (Bool, Int, Accounting head txid) A.request Int 10 Double 3600 Double 0 Word64 1 (Int 42 :: Int) Accounting Text Int initial next :: Accounting Text Int next = Int -> Accounting Text Int -> Accounting Text Int forall txid head. Ord txid => txid -> Accounting head txid -> Accounting head txid A.forget Int 42 Accounting Text Int pending Accounting Text Int -> Map Int Double forall head txid. Accounting head txid -> Map txid Double A.timers Accounting Text Int next Map Int Double -> Map Int Double -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Map Int Double forall k a. Map k a Map.empty Accounting Text Int -> Map (Double, Int) () forall head txid. Accounting head txid -> Map (Double, txid) () A.orderedTimers Accounting Text Int next Map (Double, Int) () -> Map (Double, Int) () -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Map (Double, Int) () forall k a. Map k a Map.empty String -> Expectation -> SpecWith (Arg Expectation) forall a. (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it String "does not count repeated adoption or a version bump at the same number" (Expectation -> SpecWith (Arg Expectation)) -> Expectation -> SpecWith (Arg Expectation) forall a b. (a -> b) -> a -> b $ do let (Bool first, Maybe Double _, [Double] _, Accounting Text Int s1) = Double -> SnapshotKey Text -> [Int] -> Accounting Text Int -> (Bool, Maybe Double, [Double], Accounting Text Int) forall head txid. (Eq head, Ord txid) => Double -> SnapshotKey head -> [txid] -> Accounting head txid -> (Bool, Maybe Double, [Double], Accounting head txid) A.confirmSnapshot Double 1 SnapshotKey Text key1 [Int 42] Accounting Text Int initial (Bool again, Maybe Double _, [Double] _, Accounting Text Int s2) = Double -> SnapshotKey Text -> [Int] -> Accounting Text Int -> (Bool, Maybe Double, [Double], Accounting Text Int) forall head txid. (Eq head, Ord txid) => Double -> SnapshotKey head -> [txid] -> Accounting head txid -> (Bool, Maybe Double, [Double], Accounting head txid) A.confirmSnapshot Double 2 SnapshotKey Text key1 [Int 42] Accounting Text Int s1 (Bool bumped, Maybe Double _, [Double] _, Accounting Text Int _) = Double -> SnapshotKey Text -> [Int] -> Accounting Text Int -> (Bool, Maybe Double, [Double], Accounting Text Int) forall head txid. (Eq head, Ord txid) => Double -> SnapshotKey head -> [txid] -> Accounting head txid -> (Bool, Maybe Double, [Double], Accounting head txid) A.confirmSnapshot Double 3 (Text -> Natural -> Natural -> SnapshotKey Text forall head. head -> Natural -> Natural -> SnapshotKey head A.SnapshotKey Text "head" Natural 1 Natural 1) [Int 42] Accounting Text Int s2 (Bool first, Bool again, Bool bumped) (Bool, Bool, Bool) -> (Bool, Bool, Bool) -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` (Bool True, Bool False, Bool False) String -> Expectation -> SpecWith (Arg Expectation) forall a. (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it String "does not consume or time an unrelated in-flight snapshot when adopting an older one" (Expectation -> SpecWith (Arg Expectation)) -> Expectation -> SpecWith (Arg Expectation) forall a b. (a -> b) -> a -> b $ do let pending :: Accounting Text Int pending = SnapshotKey Text -> Maybe Double -> [Int] -> Accounting Text Int -> Accounting Text Int forall head txid. Eq head => SnapshotKey head -> Maybe Double -> [txid] -> Accounting head txid -> Accounting head txid A.startSnapshot SnapshotKey Text key2 (Double -> Maybe Double forall a. a -> Maybe a Just Double 1) [Int 43] Accounting Text Int initial (Bool _, Maybe Double duration, [Double] _, Accounting Text Int next) = Double -> SnapshotKey Text -> [Int] -> Accounting Text Int -> (Bool, Maybe Double, [Double], Accounting Text Int) forall head txid. (Eq head, Ord txid) => Double -> SnapshotKey head -> [txid] -> Accounting head txid -> (Bool, Maybe Double, [Double], Accounting head txid) A.confirmSnapshot Double 2 SnapshotKey Text key1 [Int 42] Accounting Text Int pending Maybe Double duration Maybe Double -> Maybe Double -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Maybe Double forall a. Maybe a Nothing Accounting Text Int -> Maybe (SnapshotKey Text, Maybe Double, [Int]) forall head txid. Accounting head txid -> Maybe (SnapshotKey head, Maybe Double, [txid]) A.inFlight Accounting Text Int next Maybe (SnapshotKey Text, Maybe Double, [Int]) -> Maybe (SnapshotKey Text, Maybe Double, [Int]) -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` (SnapshotKey Text, Maybe Double, [Int]) -> Maybe (SnapshotKey Text, Maybe Double, [Int]) forall a. a -> Maybe a Just (SnapshotKey Text key2, Double -> Maybe Double forall a. a -> Maybe a Just Double 1, [Int 43]) String -> Expectation -> SpecWith (Arg Expectation) forall a. (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it String "does not match the same snapshot number in another head or version" (Expectation -> SpecWith (Arg Expectation)) -> Expectation -> SpecWith (Arg Expectation) forall a b. (a -> b) -> a -> b $ do let pending :: Accounting Text Int pending = SnapshotKey Text -> Maybe Double -> [Int] -> Accounting Text Int -> Accounting Text Int forall head txid. Eq head => SnapshotKey head -> Maybe Double -> [txid] -> Accounting head txid -> Accounting head txid A.startSnapshot SnapshotKey Text key1 (Double -> Maybe Double forall a. a -> Maybe a Just Double 1) [Int 42] Accounting Text Int initial (Bool _, Maybe Double duration, [Double] _, Accounting Text Int next) = Double -> SnapshotKey Text -> [Int] -> Accounting Text Int -> (Bool, Maybe Double, [Double], Accounting Text Int) forall head txid. (Eq head, Ord txid) => Double -> SnapshotKey head -> [txid] -> Accounting head txid -> (Bool, Maybe Double, [Double], Accounting head txid) A.confirmSnapshot Double 2 (Text -> Natural -> Natural -> SnapshotKey Text forall head. head -> Natural -> Natural -> SnapshotKey head A.SnapshotKey Text "other" Natural 0 Natural 1) [Int 42] Accounting Text Int pending Maybe Double duration Maybe Double -> Maybe Double -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Maybe Double forall a. Maybe a Nothing Accounting Text Int -> Maybe (SnapshotKey Text, Maybe Double, [Int]) forall head txid. Accounting head txid -> Maybe (SnapshotKey head, Maybe Double, [txid]) A.inFlight Accounting Text Int next Maybe (SnapshotKey Text, Maybe Double, [Int]) -> Maybe (SnapshotKey Text, Maybe Double, [Int]) -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Accounting Text Int -> Maybe (SnapshotKey Text, Maybe Double, [Int]) forall head txid. Accounting head txid -> Maybe (SnapshotKey head, Maybe Double, [txid]) A.inFlight Accounting Text Int pending String -> Expectation -> SpecWith (Arg Expectation) forall a. (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it String "restores snapshot contents without inventing a duration" (Expectation -> SpecWith (Arg Expectation)) -> Expectation -> SpecWith (Arg Expectation) forall a b. (a -> b) -> a -> b $ do let pending :: Accounting Text Int pending = SnapshotKey Text -> Maybe Double -> [Int] -> Accounting Text Int -> Accounting Text Int forall head txid. Eq head => SnapshotKey head -> Maybe Double -> [txid] -> Accounting head txid -> Accounting head txid A.startSnapshot SnapshotKey Text key2 Maybe Double forall a. Maybe a Nothing [Int 43] Accounting Text Int initial{A.confirmed = Just key1} (Bool advanced, Maybe Double duration, [Double] timings, Accounting Text Int next) = Double -> SnapshotKey Text -> [Int] -> Accounting Text Int -> (Bool, Maybe Double, [Double], Accounting Text Int) forall head txid. (Eq head, Ord txid) => Double -> SnapshotKey head -> [txid] -> Accounting head txid -> (Bool, Maybe Double, [Double], Accounting head txid) A.confirmSnapshot Double 500 SnapshotKey Text key2 [Int 43] Accounting Text Int pending (Bool advanced, Maybe Double duration, [Double] timings) (Bool, Maybe Double, [Double]) -> (Bool, Maybe Double, [Double]) -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` (Bool True, Maybe Double forall a. Maybe a Nothing, []) Accounting Text Int -> Maybe (SnapshotKey Text, Maybe Double, [Int]) forall head txid. Accounting head txid -> Maybe (SnapshotKey head, Maybe Double, [txid]) A.inFlight Accounting Text Int next Maybe (SnapshotKey Text, Maybe Double, [Int]) -> Maybe (SnapshotKey Text, Maybe Double, [Int]) -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Maybe (SnapshotKey Text, Maybe Double, [Int]) forall a. Maybe a Nothing String -> Expectation -> SpecWith (Arg Expectation) forall a. (HasCallStack, Example a) => String -> a -> SpecWith (Arg a) it String "clears pending state without forgetting the confirmed watermark" (Expectation -> SpecWith (Arg Expectation)) -> Expectation -> SpecWith (Arg Expectation) forall a b. (a -> b) -> a -> b $ do let (Bool _, Int _, Accounting Text Int pending) = Int -> Double -> Double -> Word64 -> Int -> Accounting Text Int -> (Bool, Int, Accounting Text Int) forall txid head. Ord txid => Int -> Double -> Double -> Word64 -> txid -> Accounting head txid -> (Bool, Int, Accounting head txid) A.request Int 10 Double 3600 Double 0 Word64 1 Int 42 Accounting Text Int initial{A.confirmed = Just key1} cleared :: Accounting Text Int cleared = Accounting Text Int -> Accounting Text Int forall head txid. Accounting head txid -> Accounting head txid A.clearPending (Accounting Text Int -> Accounting Text Int) -> Accounting Text Int -> Accounting Text Int forall a b. (a -> b) -> a -> b $ SnapshotKey Text -> Maybe Double -> [Int] -> Accounting Text Int -> Accounting Text Int forall head txid. Eq head => SnapshotKey head -> Maybe Double -> [txid] -> Accounting head txid -> Accounting head txid A.startSnapshot SnapshotKey Text key2 (Double -> Maybe Double forall a. a -> Maybe a Just Double 0) [Int 42] Accounting Text Int pending Accounting Text Int -> Maybe (SnapshotKey Text) forall head txid. Accounting head txid -> Maybe (SnapshotKey head) A.confirmed Accounting Text Int cleared Maybe (SnapshotKey Text) -> Maybe (SnapshotKey Text) -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` SnapshotKey Text -> Maybe (SnapshotKey Text) forall a. a -> Maybe a Just SnapshotKey Text key1 Accounting Text Int -> Maybe (SnapshotKey Text, Maybe Double, [Int]) forall head txid. Accounting head txid -> Maybe (SnapshotKey head, Maybe Double, [txid]) A.inFlight Accounting Text Int cleared Maybe (SnapshotKey Text, Maybe Double, [Int]) -> Maybe (SnapshotKey Text, Maybe Double, [Int]) -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Maybe (SnapshotKey Text, Maybe Double, [Int]) forall a. Maybe a Nothing Map Int Double -> Int forall k a. Map k a -> Int Map.size (Accounting Text Int -> Map Int Double forall head txid. Accounting head txid -> Map txid Double A.timers Accounting Text Int cleared) Int -> Int -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Int 0 Map (Double, Int) () -> Int forall k a. Map k a -> Int Map.size (Accounting Text Int -> Map (Double, Int) () forall head txid. Accounting head txid -> Map (Double, txid) () A.orderedTimers Accounting Text Int cleared) Int -> Int -> Expectation forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation `shouldBe` Int 0 where initial :: A.Accounting Text Int initial :: Accounting Text Int initial = Accounting Text Int forall head txid. Accounting head txid A.empty key1 :: SnapshotKey Text key1 = Text -> Natural -> Natural -> SnapshotKey Text forall head. head -> Natural -> Natural -> SnapshotKey head A.SnapshotKey Text "head" Natural 0 Natural 1 key2 :: SnapshotKey Text key2 = Text -> Natural -> Natural -> SnapshotKey Text forall head. head -> Natural -> Natural -> SnapshotKey head A.SnapshotKey Text "head" Natural 0 Natural 2