packages feed

persistent-postgresql-2.14.3.0: test/JSONTest.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

module JSONTest where

import Control.Monad.IO.Class (MonadIO)
import Data.Aeson hiding (Key)
import qualified Data.Vector as V (fromList)
import Test.HUnit (assertBool)
import Test.Hspec.Expectations ()

import Database.Persist
import Database.Persist.Postgresql.JSON

import PgInit

share
    [mkPersist persistSettings, mkMigrate "jsonTestMigrate"]
    [persistLowerCase|
  TestValue
    json Value
    deriving Show
|]

cleanDB
    :: (BaseBackend backend ~ SqlBackend, PersistQueryWrite backend, MonadIO m)
    => ReaderT backend m ()
cleanDB = deleteWhere ([] :: [Filter TestValue])

emptyArr :: Value
emptyArr = toJSON ([] :: [Value])

insert'
    :: (MonadIO m, PersistStoreWrite backend, BaseBackend backend ~ SqlBackend)
    => Value -> ReaderT backend m (Key TestValue)
insert' = insert . TestValue

matchKeys
    :: (Show record, Show (Key record), MonadIO m, Eq (Key record))
    => [Key record] -> [Entity record] -> m ()
matchKeys ys xs = do
    msg1 `assertBoolIO` (xLen == yLen)
    forM_ ys $ \y -> msg2 y `assertBoolIO` (y `elem` ks)
  where
    ks = entityKey <$> xs
    xLen = length xs
    yLen = length ys
    msg1 =
        mconcat
            [ "\nexpected: "
            , show yLen
            , "\n but got: "
            , show xLen
            , "\n[xs: "
            , show xs
            , "]"
            , "\n[ys: "
            , show ys
            , "]"
            ]
    msg2 y =
        mconcat
            [ "key \""
            , show y
            , "\" not in result:\n  "
            , show ks
            ]

setup :: IO TestKeys
setup = asIO $ runConn_ $ do
    void $ runMigrationSilent jsonTestMigrate
    testKeys

teardown :: IO ()
teardown = asIO $ runConn_ $ do
    cleanDB

shouldBeIO :: (Show a, Eq a, MonadIO m) => a -> a -> m ()
shouldBeIO x y = liftIO $ shouldBe x y

assertBoolIO :: (MonadIO m) => String -> Bool -> m ()
assertBoolIO s b = liftIO $ assertBool s b

testKeys :: (Monad m, MonadIO m) => ReaderT SqlBackend m TestKeys
testKeys = do
    nullK <- insert' Null

    boolTK <- insert' $ Bool True
    boolFK <- insert' $ toJSON False

    num0K <- insert' $ Number 0
    num1K <- insert' $ Number 1
    numBigK <- insert' $ toJSON (1234567890 :: Int)
    numFloatK <- insert' $ Number 0.0
    numSmallK <- insert' $ Number 0.0000000000000000123
    numFloat2K <- insert' $ Number 1.5
    -- numBigFloatK will turn into 9876543210.123457 because JSON
    numBigFloatK <- insert' $ toJSON (9876543210.123456789 :: Double)

    strNullK <- insert' $ String ""
    strObjK <- insert' $ String "{}"
    strArrK <- insert' $ String "[]"
    strAK <- insert' $ String "a"
    strTestK <- insert' $ toJSON ("testing" :: Text)
    str2K <- insert' $ String "2"
    strFloatK <- insert' $ String "0.45876"

    arrNullK <- insert' $ Array $ V.fromList []
    arrListK <- insert' $ toJSON [emptyArr, emptyArr, toJSON [emptyArr, emptyArr]]
    arrList2K <-
        insert' $
            toJSON
                [ emptyArr
                , toJSON [Number 3, Bool False]
                , toJSON [emptyArr, toJSON [Object mempty]]
                ]
    arrFilledK <-
        insert' $
            toJSON
                [ Null
                , Number 4
                , String "b"
                , Object mempty
                , emptyArr
                , object ["test" .= [Null], "test2" .= String "yes"]
                ]
    arrList3K <- insert' $ toJSON [toJSON [String "a"], Number 1]
    arrList4K <- insert' $ toJSON [String "a", String "b", String "c", String "d"]

    objNullK <- insert' $ Object mempty
    objTestK <- insert' $ object ["test" .= Null, "test1" .= String "no"]
    objDeepK <-
        insert' $ object ["c" .= Number 24.986, "foo" .= object ["deep1" .= Bool True]]
    objEmptyK <- insert' $ object ["" .= Number 9001]
    objFullK <-
        insert' $
            object
                [ "a" .= Number 1
                , "b" .= Number 2
                , "c" .= Number 3
                , "d" .= Number 4
                ]
    return TestKeys{..}

data TestKeys
    = TestKeys
    { nullK :: Key TestValue
    , boolTK :: Key TestValue
    , boolFK :: Key TestValue
    , num0K :: Key TestValue
    , num1K :: Key TestValue
    , numBigK :: Key TestValue
    , numFloatK :: Key TestValue
    , numSmallK :: Key TestValue
    , numFloat2K :: Key TestValue
    , numBigFloatK :: Key TestValue
    , strNullK :: Key TestValue
    , strObjK :: Key TestValue
    , strArrK :: Key TestValue
    , strAK :: Key TestValue
    , strTestK :: Key TestValue
    , str2K :: Key TestValue
    , strFloatK :: Key TestValue
    , arrNullK :: Key TestValue
    , arrListK :: Key TestValue
    , arrList2K :: Key TestValue
    , arrFilledK :: Key TestValue
    , objNullK :: Key TestValue
    , objTestK :: Key TestValue
    , objDeepK :: Key TestValue
    , arrList3K :: Key TestValue
    , arrList4K :: Key TestValue
    , objEmptyK :: Key TestValue
    , objFullK :: Key TestValue
    }
    deriving (Eq, Ord, Show)

specs :: Spec
specs = afterAll_ teardown $ do
    beforeAll setup $ do
        describe "Testing JSON operators" $ do
            describe "@>. object queries" $ do
                it "matches an empty Object with any object" $
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. Object mempty] []
                        [objNullK, objTestK, objDeepK, objEmptyK, objFullK] `matchKeys` vals

                it "matches a subset of object properties" $
                    -- {test: null, test1: no} @>. {test: null} == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. object ["test" .= Null]] []
                        [objTestK] `matchKeys` vals

                it "matches a nested object against an empty object at the same key" $
                    -- {c: 24.986, foo: {deep1: true}} @>. {foo: {}} == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. object ["foo" .= object []]] []
                        [objDeepK] `matchKeys` vals

                it "doesn't match a nested object against a string at the same key" $
                    -- {c: 24.986, foo: {deep1: true}} @>. {foo: nope} == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. object ["foo" .= String "nope"]] []
                        [] `matchKeys` vals

                it "matches a nested object when the query object is identical" $
                    -- {c: 24.986, foo: {deep1: true}} @>. {foo: {deep1: true}} == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <-
                            selectList [TestValueJson @>. (object ["foo" .= object ["deep1" .= True]])] []
                        [objDeepK] `matchKeys` vals

                it "doesn't match a nested object when queried with that exact object" $
                    -- {c: 24.986, foo: {deep1: true}} @>. {deep1: true} == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. object ["deep1" .= True]] []
                        [] `matchKeys` vals

            describe "@>. array queries" $ do
                it "matches an empty Array with any list" $
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. emptyArr] []
                        [arrNullK, arrListK, arrList2K, arrFilledK, arrList3K, arrList4K]
                            `matchKeys` vals

                it "matches list when queried with subset (1 item)" $
                    -- [null, 4, 'b', {}, [], {test: [null], test2: 'yes'}] @>. [4] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON [4 :: Int]] []
                        [arrFilledK] `matchKeys` vals

                it "matches list when queried with subset (2 items)" $
                    -- [null, 4, 'b', {}, [], {test: [null], test2: 'yes'}] @>. [null,'b'] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON [Null, String "b"]] []
                        [arrFilledK] `matchKeys` vals

                it "doesn't match list when queried with intersecting list (1 match, 1 diff)" $
                    -- [null, 4, 'b', {}, [], {test: [null], test2: 'yes'}] @>. [null,'d'] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON [emptyArr, String "d"]] []
                        [] `matchKeys` vals

                it "matches list when queried with same list in different order" $
                    -- [null, 4, 'b', {}, [], {test: [null], test2: 'yes'}] @>.
                    -- [[],'b',{test: [null],test2: 'yes'},4,null,{}] == True
                    \TestKeys{..} -> runConnAssert $ do
                        let
                            queryList =
                                toJSON
                                    [ emptyArr
                                    , String "b"
                                    , object ["test" .= [Null], "test2" .= String "yes"]
                                    , Number 4
                                    , Null
                                    , Object mempty
                                    ]

                        vals <- selectList [TestValueJson @>. queryList] []
                        [arrFilledK] `matchKeys` vals

                it "doesn't match list when queried with same list + 1 item" $
                    -- [null,4,'b',{},[],{test:[null],test2:'yes'}] @>.
                    -- [null,4,'b',{},[],{test:[null],test2: 'yes'}, false] == False
                    \TestKeys{..} -> runConnAssert $ do
                        let
                            testList =
                                toJSON
                                    [ Null
                                    , Number 4
                                    , String "b"
                                    , Object mempty
                                    , emptyArr
                                    , object ["test" .= [Null], "test2" .= String "yes"]
                                    , Bool False
                                    ]

                        vals <- selectList [TestValueJson @>. testList] []
                        [] `matchKeys` vals

                it "matches list when it shares an empty object with the query list" $
                    -- [null,4,'b',{},[],{test: [null],test2: 'yes'}] @>. [{}] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON [Object mempty]] []
                        [arrFilledK] `matchKeys` vals

                it "matches list with nested list, when queried with an empty nested list" $
                    -- [null,4,'b',{},[],{test:[null],test2:'yes'}] @>. [{test:[]}] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON [object ["test" .= emptyArr]]] []
                        [arrFilledK] `matchKeys` vals

                it "doesn't match list with nested list, when queried with a diff. nested list" $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] @>.
                    -- [{"test1":[null]}]  == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON [object ["test1" .= [Null]]]] []
                        [] `matchKeys` vals

                it "matches many nested lists when queried with empty nested list" $
                    -- [[],[],[[],[]]]                                  @>. [[]] == True
                    -- [[],[3,false],[[],[{}]]]                         @>. [[]] == True
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] @>. [[]] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON [emptyArr]] []
                        [arrListK, arrList2K, arrFilledK, arrList3K] `matchKeys` vals

                it "matches nested list when queried with a subset of that list" $
                    -- [[],[3,false],[[],[{}]]] @>. [[3]] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON [[3 :: Int]]] []
                        [arrList2K] `matchKeys` vals

                it "doesn't match nested list againts a partial intersection of that list" $
                    -- [[],[3,false],[[],[{}]]] @>. [[true,3]] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON [[Bool True, Number 3]]] []
                        [] `matchKeys` vals

                it "matches list when queried with raw number contained in the list" $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] @>. 4 == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. Number 4] []
                        [arrFilledK] `matchKeys` vals

                it "doesn't match list when queried with raw value not contained in the list" $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] @>. 99 == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. Number 99] []
                        [] `matchKeys` vals

                it "matches list when queried with raw string contained in the list" $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] @>. "b" == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. String "b"] []
                        [arrFilledK, arrList4K] `matchKeys` vals

                it "doesn't match list with empty object when queried with \"{}\" " $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] @>. "{}" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. String "{}"] []
                        [strObjK] `matchKeys` vals

                it "doesnt match list with nested object when queried with object (not in list)" $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] @>.
                    -- {"test":[null],"test2":"yes"} == False
                    \TestKeys{..} -> runConnAssert $ do
                        let
                            queryObject = object ["test" .= [Null], "test2" .= String "yes"]
                        vals <- selectList [TestValueJson @>. queryObject] []
                        [] `matchKeys` vals

            describe "@>. string queries" $ do
                it "matches identical strings" $
                    -- "testing" @>. "testing" == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. String "testing"] []
                        [strTestK] `matchKeys` vals

                it "doesnt match case insensitive" $
                    -- "testing" @>. "Testing" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. String "Testing"] []
                        [] `matchKeys` vals

                it "doesn't match substrings" $
                    -- "testing" @>. "test" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. String "test"] []
                        [] `matchKeys` vals

                it "doesn't match strings with object keys" $
                    -- "testing" @>. {"testing":1} == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. object ["testing" .= Number 1]] []
                        [] `matchKeys` vals

            describe "@>. number queries" $ do
                it "matches identical numbers" $
                    -- 1   @>. 1 == True
                    -- [1] @>. 1 == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON (1 :: Int)] []
                        [num1K, arrList3K] `matchKeys` vals

                it "matches numbers when queried with float" $
                    -- 0 @>. 0.0 == True
                    -- 0.0 @>. 0.0 == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON (0.0 :: Double)] []
                        [num0K, numFloatK] `matchKeys` vals

                it "does not match numbers when queried with a substring of that number" $
                    -- 1234567890 @>. 123456789 == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON (123456789 :: Int)] []
                        [] `matchKeys` vals

                it "does not match number when queried with different number" $
                    -- 1234567890 @>. 234567890 == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON (234567890 :: Int)] []
                        [] `matchKeys` vals

                it "does not match number when queried with string of that number" $
                    -- 1 @>. "1" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. String "1"] []
                        [] `matchKeys` vals

                it "does not match number when queried with list of digits" $
                    -- 1234567890 @>. [1,2,3,4,5,6,7,8,9,0] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <-
                            selectList
                                [TestValueJson @>. toJSON ([1, 2, 3, 4, 5, 6, 7, 8, 9, 0] :: [Int])]
                                []
                        [] `matchKeys` vals

            describe "@>. boolean queries" $ do
                it "matches identical booleans (True)" $
                    -- true @>. true == True
                    -- false @>. true == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. toJSON True] []
                        [boolTK] `matchKeys` vals

                it "matches identical booleans (False)" $
                    -- false @>. false == True
                    -- true @>. false == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. Bool False] []
                        [boolFK] `matchKeys` vals

                it "does not match boolean with string of boolean" $
                    -- true @>. "true" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. String "true"] []
                        [] `matchKeys` vals

            describe "@>. null queries" $ do
                it "matches nulls" $
                    -- null @>. null == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. Null] []
                        [nullK, arrFilledK] `matchKeys` vals

                it "does not match null with string of null" $
                    -- null @>. "null" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson @>. String "null"] []
                        [] `matchKeys` vals

            describe "<@. queries" $ do
                it "matches subobject when queried with superobject" $
                    -- {}                         <@. {"test":null,"test1":"no","blabla":[]} == True
                    -- {"test":null,"test1":"no"} <@. {"test":null,"test1":"no","blabla":[]} == True
                    \TestKeys{..} -> runConnAssert $ do
                        let
                            queryObject =
                                object
                                    [ "test" .= Null
                                    , "test1" .= String "no"
                                    , "blabla" .= emptyArr
                                    ]
                        vals <- selectList [TestValueJson <@. queryObject] []
                        [objNullK, objTestK] `matchKeys` vals

                it "matches raw values and sublists when queried with superlist" $
                    -- []    <@. [null,4,"b",{},[],{"test":[null],"test2":"yes"},false] == True
                    -- null  <@. [null,4,"b",{},[],{"test":[null],"test2":"yes"},false] == True
                    -- false <@. [null,4,"b",{},[],{"test":[null],"test2":"yes"},false] == True
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] <@.
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"},false] == True
                    \TestKeys{..} -> runConnAssert $ do
                        let
                            queryList =
                                toJSON
                                    [ Null
                                    , Number 4
                                    , String "b"
                                    , Object mempty
                                    , emptyArr
                                    , object ["test" .= [Null], "test2" .= String "yes"]
                                    , Bool False
                                    ]

                        vals <- selectList [TestValueJson <@. queryList] []
                        [arrNullK, arrFilledK, boolFK, nullK] `matchKeys` vals

                it "matches identical strings" $
                    -- "a" <@. "a" == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson <@. String "a"] []
                        [strAK] `matchKeys` vals

                it "matches identical big floats" $
                    -- 9876543210.123457 <@ 9876543210.123457 == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson <@. Number 9876543210.123457] []
                        [numBigFloatK] `matchKeys` vals

                it "doesn't match different big floats" $
                    -- 9876543210.123457 <@. 9876543210.123456789 == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson <@. Number 9876543210.123456789] []
                        [] `matchKeys` vals

                it "matches nulls" $
                    -- null <@. null == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson <@. Null] []
                        [nullK] `matchKeys` vals

            describe "?. queries" $ do
                it "matches top level keys and not the keys of nested objects" $
                    -- {"test":null,"test1":"no"}                       ?. "test" == True
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] ?. "test" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?. "test"] []
                        [objTestK] `matchKeys` vals

                it "doesn't match nested key" $
                    -- {"c":24.986,"foo":{"deep1":true"}} ?. "deep1" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?. "deep1"] []
                        [] `matchKeys` vals

                it "matches \"{}\" but not empty object when queried with \"{}\"" $
                    -- "{}" ?. "{}" == True
                    -- {}   ?. "{}" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?. "{}"] []
                        [strObjK] `matchKeys` vals

                it "matches raw empty str and empty str key when queried with \"\"" $
                    ---- {}        ?. "" == False
                    ---- ""        ?. "" == True
                    ---- {"":9001} ?. "" == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?. ""] []
                        [strNullK, objEmptyK] `matchKeys` vals

                it "matches lists containing string value when queried with raw string value" $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] ?. "b" == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?. "b"] []
                        [arrFilledK, arrList4K, objFullK] `matchKeys` vals

                it "matches lists, objects, and raw values correctly when queried with string" $
                    -- [["a"]]                   ?. "a" == False
                    -- "a"                       ?. "a" == True
                    -- ["a","b","c","d"]         ?. "a" == True
                    -- {"a":1,"b":2,"c":3,"d":4} ?. "a" == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?. "a"] []
                        [strAK, arrList4K, objFullK] `matchKeys` vals

                it "matches string list but not real list when queried with \"[]\"" $
                    -- "[]" ?. "[]" == True
                    -- []   ?. "[]" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?. "[]"] []
                        [strArrK] `matchKeys` vals

                it "does not match null when queried with string null" $
                    -- null ?. "null" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?. "null"] []
                        [] `matchKeys` vals

                it "does not match bool whe nqueried with string bool" $
                    -- true ?. "true" == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?. "true"] []
                        [] `matchKeys` vals

            describe "?|. queries" $ do
                it "matches raw vals, lists, objects, and nested objects" $
                    -- "a"                                              ?|. ["a","b","c"] == True
                    -- [["a"],1]                                        ?|. ["a","b","c"] == False
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] ?|. ["a","b","c"] == True
                    -- ["a","b","c","d"]                                ?|. ["a","b","c"] == True
                    -- {"a":1,"b":2,"c":3,"d":4}                        ?|. ["a","b","c"] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?|. ["a", "b", "c"]] []
                        [strAK, arrFilledK, objDeepK, arrList4K, objFullK] `matchKeys` vals

                it "matches str object but not object when queried with \"{}\"" $
                    -- "{}"  ?|. ["{}"] == True
                    -- {}    ?|. ["{}"] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?|. ["{}"]] []
                        [strObjK] `matchKeys` vals

                it "doesn't match superstrings when queried with substring" $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] ?|. ["test"] == False
                    -- "testing"                                        ?|. ["test"] == False
                    -- {"test":null,"test1":"no"}                       ?|. ["test"] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?|. ["test"]] []
                        [objTestK] `matchKeys` vals

                it "doesn't match nested keys" $
                    -- {"c":24.986,"foo":{"deep1":true"}} ?|. ["deep1"] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?|. ["deep1"]] []
                        [] `matchKeys` vals

                it "doesn't match anything when queried with empty list" $
                    -- ANYTHING ?|. [] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?|. []] []
                        [] `matchKeys` vals

                it "doesn't match raw, non-string, values when queried with strings" $
                    -- true ?|. ["true","null","1"] == False
                    -- null ?|. ["true","null","1"] == False
                    -- 1    ?|. ["true","null","1"] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?|. ["true", "null", "1"]] []
                        [] `matchKeys` vals

                it "matches string array when queried with \"[]\"" $
                    -- []   ?|. ["[]"] == False
                    -- "[]" ?|. ["[]"] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?|. ["[]"]] []
                        [strArrK] `matchKeys` vals

            describe "?&. queries" $ do
                it "matches anything when queried with an empty list" $
                    -- ANYTHING ?&. [] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. []] []
                        flip
                            matchKeys
                            vals
                            [ nullK
                            , boolTK
                            , boolFK
                            , num0K
                            , num1K
                            , numBigK
                            , numFloatK
                            , numSmallK
                            , numFloat2K
                            , numBigFloatK
                            , strNullK
                            , strObjK
                            , strArrK
                            , strAK
                            , strTestK
                            , str2K
                            , strFloatK
                            , arrNullK
                            , arrListK
                            , arrList2K
                            , arrFilledK
                            , arrList3K
                            , arrList4K
                            , objNullK
                            , objTestK
                            , objDeepK
                            , objEmptyK
                            , objFullK
                            ]

                it "matches raw values, lists, and objects when queried with string" $
                    -- "a"                       ?&. ["a"] == True
                    -- [["a"],1]                 ?&. ["a"] == False
                    -- ["a","b","c","d"]         ?&. ["a"] == True
                    -- {"a":1,"b":2,"c":3,"d":4} ?&. ["a"] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. ["a"]] []
                        [strAK, arrList4K, objFullK] `matchKeys` vals

                it "matches raw values, lists, and objects when queried with multiple string" $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] ?&. ["b","c"] == False
                    -- {"c":24.986,"foo":{"deep1":true"}}               ?&. ["b","c"] == False
                    -- ["a","b","c","d"]                                ?&. ["b","c"] == True
                    -- {"a":1,"b":2,"c":3,"d":4}                        ?&. ["b","c"] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. ["b", "c"]] []
                        [arrList4K, objFullK] `matchKeys` vals

                it "matches object string when queried with \"{}\"" $
                    -- {}   ?&. ["{}"] == False
                    -- "{}" ?&. ["{}"] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. ["{}"]] []
                        [strObjK] `matchKeys` vals

                it "doesn't match superstrings when queried with substring" $
                    -- [null,4,"b",{},[],{"test":[null],"test2":"yes"}] ?&. ["test"] == False
                    -- "testing"                                        ?&. ["test"] == False
                    -- {"test":null,"test1":"no"}                       ?&. ["test"] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. ["test"]] []
                        [objTestK] `matchKeys` vals

                it "doesn't match nested keys" $
                    -- {"c":24.986,"foo":{"deep1":true"}} ?&. ["deep1"] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. ["deep1"]] []
                        [] `matchKeys` vals

                it "doesn't match anything when there is a partial match" $
                    -- "a"                       ?&. ["a","e"] == False
                    -- ["a","b","c","d"]         ?&. ["a","e"] == False
                    -- {"a":1,"b":2,"c":3,"d":4} ?&. ["a","e"] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. ["a", "e"]] []
                        [] `matchKeys` vals

                it "matches string array when queried with \"[]\"" $
                    -- []   ?&. ["[]"] == False
                    -- "[]" ?&. ["[]"] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. ["[]"]] []
                        [strArrK] `matchKeys` vals

                it "doesn't match null when queried with string null" $
                    -- THIS WILL FAIL IF THE IMPLEMENTATION USES
                    -- @ '{null}' @
                    -- INSTEAD OF
                    -- @ ARRAY['null'] @
                    -- null ?&. ["null"] == False
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. ["null"]] []
                        [] `matchKeys` vals

                it "doesn't match number when queried with str of that number" $
                    -- [["a"],1] ?&. ["1"] == False
                    -- "1"       ?&. ["1"] == True
                    \TestKeys{..} -> runConnAssert $ do
                        str1 <- insert' $ toJSON $ String "1"
                        vals <- selectList [TestValueJson ?&. ["1"]] []
                        [str1] `matchKeys` vals

                it "doesn't match empty objs or list when queried with empty string" $
                    -- {}        ?&. [""] == False
                    -- []        ?&. [""] == False
                    -- ""        ?&. [""] == True
                    -- {"":9001} ?&. [""] == True
                    \TestKeys{..} -> runConnAssert $ do
                        vals <- selectList [TestValueJson ?&. [""]] []
                        [strNullK, objEmptyK] `matchKeys` vals