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

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
