{-# LANGUAGE Arrows #-} module Hasura.IncrementalSpec (spec) where import Control.Arrow.Extended import Control.Monad.Unique import Data.HashMap.Strict qualified as M import Data.HashSet qualified as S import Hasura.Incremental qualified as Inc import Hasura.Prelude import Test.Hspec spec :: Spec spec = do describe "cache" $ do it "skips re-running rules if the input didn’t change" $ do let add1 :: (MonadState Integer m) => m () add1 = modify' (+ 1) rule = proc (a, b) -> do Inc.cache $ arrM (\_ -> add1) -< a Inc.cache $ arrM (\_ -> add1 *> add1) -< b (result1, state1) <- runStateT (Inc.build rule (False, False)) 0 state1 `shouldBe` 3 (result2, state2) <- runStateT (Inc.rebuild result1 (True, False)) 0 state2 `shouldBe` 1 (_, state3) <- runStateT (Inc.rebuild result2 (True, True)) 0 state3 `shouldBe` 2 it "tracks dependencies within nested uses of cache across multiple executions" do let rule :: (MonadWriter String m, MonadUnique m) => Inc.Rule m (Inc.InvalidationKey, Inc.InvalidationKey) () rule = proc (key1, key2) -> do dep1 <- Inc.newDependency -< key2 (key1, dep1) >- Inc.cache ( proc (_, dep2) -> dep2 >- Inc.cache ( proc dep3 -> do Inc.dependOn -< dep3 arrM tell -< "executed" ) ) returnA -< () let key1 = Inc.initialInvalidationKey key2 = Inc.invalidate key1 (result1, log1) <- runWriterT $ Inc.build rule (key1, key1) log1 `shouldBe` "executed" (result2, log2) <- runWriterT $ Inc.rebuild result1 (key2, key1) log2 `shouldBe` "" (_, log3) <- runWriterT $ Inc.rebuild result2 (key2, key2) log3 `shouldBe` "executed" describe "keyed" $ do it "preserves incrementalization when entries don’t change" $ do let rule :: (MonadWriter (S.HashSet (String, Integer)) m, MonadUnique m) => Inc.Rule m (M.HashMap String Integer) (M.HashMap String Integer) rule = proc m -> (| Inc.keyed ( \k v -> do Inc.cache $ arrM (tell . S.singleton) -< (k, v) returnA -< v * 2 ) |) m (result1, log1) <- runWriterT . Inc.build rule $ M.fromList [("a", 1), ("b", 2)] Inc.result result1 `shouldBe` M.fromList [("a", 2), ("b", 4)] log1 `shouldBe` S.fromList [("a", 1), ("b", 2)] (result2, log2) <- runWriterT . Inc.rebuild result1 $ M.fromList [("a", 1), ("b", 3), ("c", 4)] Inc.result result2 `shouldBe` M.fromList [("a", 2), ("b", 6), ("c", 8)] log2 `shouldBe` S.fromList [("b", 3), ("c", 4)]