{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -fno-warn-deprecations #-} module Main (main) where #if !MIN_VERSION_base(4,8,0) import Control.Applicative #endif import Data.Map (Map) import Data.Maybe (isJust) import Data.String (fromString) import Data.Time (zonedTimeToUTC, UTCTime(..), Day(..)) import Data.Vector (Vector) import Test.QuickCheck.Instances () import Test.Tasty import Test.Tasty.HUnit import Test.Tasty.QuickCheck import qualified Data.HashMap.Lazy as H #if MIN_VERSION_base(4,7,0) import Data.Proxy #endif import Data.Aeson.Extra import Data.Time.TH import Orphans () main :: IO () main = defaultMain $ testGroup "Tests" [ dotColonMark , encodeStrictTests , mTests #if MIN_VERSION_base(4,7,0) , symTests , singObjectTests #endif , collapsedListTests , utctimeTests , zonedtimeTests , timeTHTests , mergeTests , streamTests ] ------------------------------------------------------------------------------ -- encodeStrict ------------------------------------------------------------------------------ encodeStrictTests :: TestTree encodeStrictTests = testGroup "encodeStrict" [ testProperty "decodeStrict . encodeStrict" prop ] where prop :: Int -> Property prop i = let lhs = decodeStrict . encodeStrict $ i rhs = Just i in lhs === rhs ------------------------------------------------------------------------------ -- M ------------------------------------------------------------------------------ mTests :: TestTree mTests = testGroup "M" [ testCase "decode" $ let lhs = decode "{\"1\": 1, \"2\": 2}" :: Maybe (M (H.HashMap Int Int)) rhs = Just result in lhs @?= rhs , testProperty "decode . encode" $ let prop :: Map Int Int -> Property prop m = let lhs = fmap getMap . decode . encode . M $ m rhs = Just m in lhs === rhs in prop ] where result = M $ H.fromList [(1,1),(2,2)] #if MIN_VERSION_base(4,7,0) ------------------------------------------------------------------------------ -- SymTag ------------------------------------------------------------------------------ symTests :: TestTree symTests = testGroup "SymTag" [ testCase "encode" $ encode (SymTag :: SymTag "foobar") @?= "\"foobar\"" , testCase "decode success" $ (decode "\"foobar\"" :: Maybe (SymTag "foobar")) @?= Just SymTag , testCase "decode failure" $ (decode "\"foobar\"" :: Maybe (SymTag "barfoo")) @?= Nothing ] ------------------------------------------------------------------------------ -- SingObject ------------------------------------------------------------------------------ -- > λ > decode "{\"value\": 42 }" :: Maybe (SingObject "value" Int) -- > Just (SingObject 42) singObjectTests :: TestTree singObjectTests = testGroup "SingObject" [ testCase "decode success" $ (decode "{\"value\": 42 }" :: Maybe (SingObject "value" Int)) @?= Just (SingObject 42) , testCase "decode failure" $ (decode "{\"value\": 42 }" :: Maybe (SingObject "key" Int)) @?= Nothing , testProperty "decode . encode" $ let prop :: Int -> Property prop n = let rhs = fmap (getSingObject p) . decode . encode . mkSingObject p $ n lhs = Just n in lhs === rhs p :: Proxy "value" p = Proxy in prop ] #endif ------------------------------------------------------------------------------ -- parseCollapsedList ------------------------------------------------------------------------------ newtype V = V [Int] deriving (Show, Eq) instance FromJSON V where parseJSON = withObject "V" $ \obj -> V <$> parseCollapsedList obj "value" collapsedListTests :: TestTree collapsedListTests = testGroup "collapsedList" [ testCase "empty" $ (decode "{}" :: Maybe V) @?= Just (V []) , testCase "null" $ (decode "{\"value\": null}" :: Maybe V) @?= Just (V []) , testCase "singleton" $ (decode "{\"value\": 42}" :: Maybe V) @?= Just (V [42]) , testCase "array" $ (decode "{\"value\": [1, 2, 3, 4]}" :: Maybe V) @?= Just (V [1,2,3,4]) , testProperty "decode . encode" $ let prop :: [Int] -> Property prop l = let rhs = fmap getCollapsedList . decode . encode . CollapsedList $ l lhs = Just l in lhs === rhs in prop , testProperty "Vector decode . encode" $ let prop :: Vector Int -> Property prop l = let rhs = fmap getCollapsedList . decode . encode . CollapsedList $ l lhs = Just l in lhs === rhs in prop ] ------------------------------------------------------------------------------- -- Stream ------------------------------------------------------------------------------- streamTests :: TestTree streamTests = testGroup "stream" [ streamDecodeTests ] where streamDecodeTests = testGroup "decode" $ map (uncurry validTestCase) valids ++ [ testCase "ws: empty" $ streamDecode " [ ] " @?= ([] :: [Int], Nothing) , testCase "ws: singleton" $ streamDecode " [ 1 ]" @?= ([1] :: [Int], Nothing) , testCase "ws: many" $ streamDecode " [ 1 , 2, 3 ] " @?= ([1,2,3] :: [Int], Nothing) -- Errors: , testCase "error begin" $ streamDecode' "," @?= ([] :: [Int], True) , testCase "parses first" $ streamDecode' "[1,2,3[" @?= ([1,2] :: [Int], True) , testCase "error begin" $ streamDecode' "[1,2,'a']" @?= ([1,2] :: [Int], True) ] validTestCase name v = testCase ("valid " ++ name) $ streamDecode (encode v) @?= (v, Nothing) streamDecode' = fmap isJust . streamDecode valids :: [(String, [Int])] valids = [ (,) "empty" [] , (,) "singleton" [1] , (,) "many" [1..200] ] ------------------------------------------------------------------------------ -- Comparison (.:?) and (.:!) ------------------------------------------------------------------------------ newtype T1 = T1 (Maybe Int) deriving (Eq, Show) newtype T2 = T2 (Maybe Int) deriving (Eq, Show) newtype T3 = T3 (Maybe Int) deriving (Eq, Show) instance FromJSON T1 where parseJSON = fmap T1 . withObject "T1" (.: "value") instance FromJSON T2 where parseJSON = fmap T2 . withObject "T2" (.:? "value") instance FromJSON T3 where parseJSON = fmap T3 . withObject "T3" (.:! "value") dotColonMark :: TestTree dotColonMark = testGroup "Operators" $ fmap t [ assertEqual ".: not-present" Nothing (decode ex1 :: Maybe T1) , assertEqual ".: 42" (Just (T1 (Just 42))) (decode ex2 :: Maybe T1) , assertEqual ".: null" (Just (T1 Nothing)) (decode ex3 :: Maybe T1) , assertEqual ".:? not-present" (Just (T2 (Nothing))) (decode ex1 :: Maybe T2) , assertEqual ".:? 42" (Just (T2 (Just 42))) (decode ex2 :: Maybe T2) , assertEqual ".:? null" (Just (T2 Nothing)) (decode ex3 :: Maybe T2) , assertEqual ".:! not-present" (Just (T3 (Nothing))) (decode ex1 :: Maybe T3) , assertEqual ".:! 42" (Just (T3 (Just 42))) (decode ex2 :: Maybe T3) , assertEqual ".:! null" Nothing (decode ex3 :: Maybe T3) ] where ex1 = "{}" ex2 = "{\"value\": 42 }" ex3 = "{\"value\": null }" t = testCase "-" ------------------------------------------------------------------------------ -- U & Z ------------------------------------------------------------------------------ utctimeTests :: TestTree utctimeTests = testGroup "U" $ [ testCase "base case" $ assertBool "base case" $ isJust simple ] ++ map t timeStrings where simple = decode "\"2015-09-07T08:16:40.807Z\"" :: Maybe U t str = testCase str . assertEqual str simple . decode . fromString $ "\"" ++ str ++ "\"" zonedtimeTests :: TestTree zonedtimeTests = testGroup "Z" $ [ testCase "base case" $ assertBool "base case" $ isJust simple ] ++ map t timeStrings where simple = decode "\"2015-09-07T08:16:40.807Z\"" :: Maybe Z t str = testCase str . assertEqual str (fmap z simple) . fmap z . decode . fromString $ "\"" ++ str ++ "\"" z (Z z') = zonedTimeToUTC z' timeStrings :: [String] timeStrings = [ "2015-09-07T08:16:40.807Z" , "2015-09-07T11:16:40.807+0300" , "2015-09-07 08:16:40.807Z" , "2015-09-07 08:16:40.807 Z" , "2015-09-07 08:16:40.807 +0000" , "2015-09-07 08:16:40.807 +00:00" , "2015-09-07 11:16:40.807 +03:00" , "2015-09-07 05:16:40.807 -03:00" ] timeTHTests :: TestTree timeTHTests = testCase "time TH example" $ assertBool "should be equal" $ lhs == rhs where lhs = UTCTime (ModifiedJulianDay 56789) 123.456 rhs = $(mkUTCTime "2014-05-12 00:02:03.456000Z") ------------------------------------------------------------------------------ -- Merge tests ------------------------------------------------------------------------------ mergeTests :: TestTree mergeTests = testGroup "Lodash merge examples" $ map f examples where f (x, y, z) = testCase "-" $ assertBool "should be equal" $ lodashMerge x y == z examples = [ (,,) $(mkValue "[1, 2, 3]") $(mkValue "[4, 5, 6, 7, 8]") $(mkValue "[4, 5, 6, 7, 8]") , (,,) $(mkValue' "{'a': 1}") $(mkValue' "{'b': 2}") $(mkValue' "{'a': 1, 'b': 2}") , (,,) $(mkValue' "{ 'data': [{ 'user': 'barney' }, { 'user': 'fred' }] }") $(mkValue' "{ 'data': [{ 'age': 36 }, { 'age': 40 }] }") $(mkValue' "{ 'data': [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] }") ]