{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE BlockArguments #-}
import Control.Applicative
import Control.Monad
import Data.Aeson.Quick
import Data.Aeson.Quick.Internal
import Data.String
import Lens.Micro
import Test.QuickCheck
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Debug.Trace
main :: IO ()
main = defaultMain $ testGroup "Tests"
[ oneKey
, multipleKeys
, deepKey
, keyInArray
, complex
, optionalKey
, nonExistentKey
, parseInvalid
, showQuick
, quotedKey
, quasi
, props
]
oneKey :: TestTree
oneKey = testGroup "oneKey"
[ testCase "get" $
val .! "{a}" @?= one
, testCase "set" $
"{a}" .% Null @?= [jsonlit|{"a":null}|]
]
where val = [jsonlit|{"a":1}|]
multipleKeys :: TestTree
multipleKeys = testGroup "multipleKeys"
[ testCase "get" $
multiple .! "{a,b}" @?= (one,two)
, testCase "set" $
(.%) "{a,b}" (two,[one,one]) @?= [jsonlit|{"a":2,"b":[1,1]}|]
]
where multiple = [jsonlit|{"a":1,"b":2}|]
deepKey :: TestTree
deepKey = testGroup "deepKey"
[ testCase "get" $
multilevel .! "{a:{b}}" @?= Just one
, testCase "set" $
(.%) "{a:{b}}" two @?= [jsonlit|{"a":{"b":2}}|]
]
where multilevel = [jsonlit|{"a":{"b":1}}|]
keyInArray :: TestTree
keyInArray = testGroup "keyInArray"
[ testCase "get" $
many .! "[{a}]" @?= [one,two]
, testCase "set" $
(.%) "[{a}]" [True,False] @?= [jsonlit|[{"a":true},{"a":false}]|]
]
where many = [jsonlit|[{"a":1},{"a":2}]|]
complex :: TestTree
complex = testGroup "complex"
[ testCase "get" $
val .! "{a,b:[{a}]}" @?= (one,[[two,one]])
, testCase "set" $
(.%) "{a,b:[{a}]}" (two,[[one]]) @?= [jsonlit|{"a":2,"b":[{"a":[1]}]}|]
]
where val = [jsonlit|{"a":1,"b":[{"a":[2,1]}]}|]
optionalKey :: TestTree
optionalKey = testGroup "optionalKey"
[ testCase "get" $
val .! "{a?,b?}" @?= (Just one, Nothing :: Maybe Int)
]
where val = [jsonlit|{"a":1}|]
nonExistentKey :: TestTree
nonExistentKey = testGroup "nonExistentKey"
[ testCase "get" $
val .? "{b}" @?= (Nothing :: Maybe Int)
, testCase "set" $
(.%) "{a}" Null @?= [jsonlit|{"a":null}|]
, testCase "setDeep" $
"{a:[{b}]}" .% [Null] @?= [jsonlit|{"a":[{"b":null}]}|]
, testCase "setDeepArray" $
"[{a}]" .% [one,two] @?= [jsonlit|[{"a":1},{"a":2}]|]
, testCase "setDeepMany" $
let v = (1,[(10,11)]) :: (Int,[(Int,Int)])
in "{my,complex:[{data,structure}]}" .% v
@?= [jsonlit|{"my":1,"complex":[{"data":10,"structure":11}]}|]
, testCase "inArray" do
[jsonlit|[{"foo": 1}, {}, {"foo": 2}]|] .! "[{foo?}]" @?= [Just 1, Nothing, Just (2::Int)]
]
where val = [jsonlit|{"a":1}|]
-- For now, Quick cannot be used as a lens
-- in order to update objects.
-- Perhaps revisit the old "build" function one day.
--asLens :: TestTree
--asLens = testGroup "asLens"
-- [ testCase "get" $
-- let l = queLens "{a,b}" :: Lens' Value (Int,Int)
-- in val ^. l . _2 @?= two
--
-- --, testCase "getDoesNotExist" $
-- -- -- doesn't work, make a Traversal?
-- -- --d "{}" ^? queLens "{a}" @?= (Nothing :: Maybe (Int,Int))
-- -- putStr "SKIPPED " >> pure ()
--
-- , testCase "set" $
-- (val & (queLens "{a,b}") .~ (two,one)) @?= [jsonlit|{"a":2,"b":1}"
-- ]
-- where
-- val = d "{"a":1,"b":2}"
-- queLens :: (FromJSON a, ToJSON a) => Quick -> Lens' Value a
-- queLens s = lens (.!s) ((.%) s)
parseInvalid :: TestTree
parseInvalid = testGroup "parseInvalid"
[ testCase "noKey" $
isLeft (parseQuick "{a,{b}}") @?= True
]
showQuick :: TestTree
showQuick = testGroup "showQuick"
[ testCase "all" $
show ("[{a:[{b?,c}]}]"::Quick) @?= "[{a:[{b?,c}]}]"
]
quotedKey :: TestTree
quotedKey = testGroup "quotedKey"
[ testCase "chars" $
("{\"_:,}\"?}") @?= Obj [("_:,}", True, Val)]
]
quasi :: TestTree
quasi = testGroup "quasi-quoter"
[ testCase "quasi" do
[quick|.|] @?= Val
[quick|{a:[.]0}|] @?= "{a:[.]0}"
]
instance Arbitrary Bounds where
arbitrary = do
r <- arbitrary
case r of
LT -> pure All
EQ -> Single <$> choose (0, 100)
GT -> Range <$> choose (0, 100) <*> (fmap abs <$> arbitrary)
instance Arbitrary Quick where
arbitrary = sized arbQuick where
arbKey = do
i <- choose (1, 10)
s <- replicateM i (elements ['a'..'z'])
pure $ fromString s
arbItem x = (,,) <$> arbKey <*> arbitrary <*> arbQuick x
arbQuick 0 = pure Val
arbQuick x
| even x = Arr <$> arbQuick (quot x 2) <*> arbitrary
| otherwise = do
i <- choose (0, 3)
Obj <$> replicateM i (arbItem (quot x 2))
props :: TestTree
props = testGroup "props"
[
testProperty "isomorphic parse" $
\q -> fromString (show (q::Quick)) == q
, testProperty "isomorphic show" $
\q -> let s = show (q::Quick) in s == show (fromString s :: Quick)
, testProperty "iso01" $
\x -> let q = "[{a,b?}]" in (q .% x) .! q == (x :: [(Int, Maybe Int)])
, testProperty "iso02" $
\x -> let q = "[{a:[{d}],b}]" in (q .% x) .! q == (x :: [([Int], Int)])
]
one, two :: Int
one = 1
two = 2
isLeft :: Either a b -> Bool
isLeft e = case e of Left _ -> True
_ -> False