graphql-engine/server/src-test/Test/Aeson/Expectation.hs
Philip Lykke Carlsen d43a30e8fc feat(tests): Introduce AggregationPredicatesSpec
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5686
GitOrigin-RevId: 85b39ad569180929e5620c45bf9a98ef6ee99d42
2022-09-07 12:10:52 +00:00

61 lines
2.2 KiB
Haskell

-- | This module contains functions that help express expectations about json
-- values.
module Test.Aeson.Expectation
( shouldBeSubsetOf,
jsonSubsetOf,
)
where
import Data.Aeson qualified as A
import Data.Aeson.Encode.Pretty qualified as AP
import Data.Aeson.KeyMap qualified as A
import Data.ByteString.Lazy.Char8 qualified as LBS
import Data.Text qualified as T
import Data.Text.Encoding (decodeUtf8)
import Data.These
import Data.Vector qualified as V
import Hasura.Backends.Postgres.Instances.Schema ()
import Hasura.Prelude
import Test.Hspec
-- | Assert that one json value should be a subset of another, in the sense of 'jsonSubsetOf'.
shouldBeSubsetOf :: A.Value -> A.Value -> IO ()
shouldBeSubsetOf subset superset | subset `jsonSubsetOf` superset = return ()
shouldBeSubsetOf subset superset =
expectationFailure $
T.unpack $
decodeUtf8 $
LBS.toStrict $
AP.encodePretty subset <> " is not a subset of " <> AP.encodePretty superset
-- | Compute whether one json value 'sub' is a subset of another value 'sup', in the sense that:
--
-- * For arrays, there is a contiguous segment in 'sup' in which all elements are subset-related with 'sub' in order
-- * For objects, the keys of 'sub' are a subset of those of 'sup', and all their associated values are also subset-related
-- * Leaf values are identical
jsonSubsetOf :: A.Value -> A.Value -> Bool
jsonSubsetOf (A.Array sub) (A.Array sup) = sub `subarrayOf` sup
jsonSubsetOf (A.Object sub) (A.Object sup) = sub `subobjectOf` sup
jsonSubsetOf (A.String sub) (A.String sup) = sub == sup
jsonSubsetOf (A.Number sub) (A.Number sup) = sub == sup
jsonSubsetOf (A.Bool sub) (A.Bool sup) = sub == sup
jsonSubsetOf A.Null A.Null = True
jsonSubsetOf _sub _sup = False
subobjectOf :: A.KeyMap A.Value -> A.KeyMap A.Value -> Bool
subobjectOf sub sup =
A.foldr (&&) True $
A.alignWith
( \case
This _ -> False -- key is only in the sub
That _ -> True -- key is only in sup
These l r -> l `jsonSubsetOf` r
)
sub
sup
subarrayOf :: V.Vector A.Value -> V.Vector A.Value -> Bool
subarrayOf sub sup | V.length sub > V.length sup = False
subarrayOf sub sup | V.and $ V.zipWith jsonSubsetOf sub sup = True
subarrayOf sub sup = subarrayOf sub (V.tail sup)