aeson-decode-0.1.0.0: test/hedgehog.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
import AesonDecode
-- aeson-qq
import Data.Aeson.QQ (aesonQQ)
-- base
import Control.Applicative (Alternative (..), optional)
import Control.Monad (when)
import Data.Semigroup ((<>))
import System.Exit (exitFailure)
import System.IO (hSetEncoding, stderr, stdout, utf8)
-- hedgehog
import Hedgehog
-- text
import Data.Text (Text)
-- time
import Data.Time.Clock.POSIX (POSIXTime)
(<&>) :: Functor f => f a -> (a -> b) -> f b
as <&> f = f <$> as
infixl 1 <&>
main :: IO ()
main = do
hSetEncoding stdout utf8
hSetEncoding stderr utf8
ok <- checkParallel $$(discover)
when (not ok) exitFailure
prop_either :: Property
prop_either = withTests 1 $ property $ do
let
d :: Decoder (Either Text Integer) = (text <&> Left) <|> (integer <&> Right)
decodeMaybe d [aesonQQ|"x"|] === Just (Left "x")
decodeMaybe d [aesonQQ|5|] === Just (Right 5)
decodeMaybe d [aesonQQ|null|] === Nothing
prop_eitherTagged :: Property
prop_eitherTagged = withTests 1 $ property $ do
let
d :: Decoder (Either Integer Integer) =
(at "type" (textIs "x") *> at "value" integer <&> Left) <|>
(at "type" (textIs "y") *> at "value" integer <&> Right)
decodeMaybe d [aesonQQ|{"type": "x", "value": 1}|] === Just (Left 1)
decodeMaybe d [aesonQQ|{"type": "y", "value": 2}|] === Just (Right 2)
decodeMaybe d [aesonQQ|{"type": "z", "value": 3}|] === Nothing
data Asset
= Asset'Image Text
| Asset'Video Text Text
deriving (Eq, Show)
prop_asset :: Property
prop_asset = withTests 1 $ property $ do
let
d'image, d'video :: Decoder Asset
d :: Decoder [Asset]
d'image = do
at "type" (textIs "image")
Asset'Image <$> at "url" text
d'video = do
at "type" (textIs "video")
Asset'Video <$> at "url" text
<*> at "poster" text
d = at "assets" $ listOf (d'image <|> d'video)
json =
[aesonQQ|
{
"assets": [
{
"type": "video",
"url": "https://subscriber.typeclasses.com/video/js-operators-2/dash/manifest.mpd",
"poster": "/_/static/operators-video.jpg"
},
{
"type": "image",
"url": "/_/static/acme.jpg"
}
]
}
|]
decodeMaybe d json === Just
[ Asset'Video "https://subscriber.typeclasses.com/video/js-operators-2/dash/manifest.mpd"
"/_/static/operators-video.jpg"
, Asset'Image "/_/static/acme.jpg"
]
newtype Resource = Resource Text
deriving (Eq, Show)
data StartTime = StartImmediately | StartTime POSIXTime
deriving (Eq, Show)
newtype EndTime = EndTime POSIXTime
deriving (Eq, Show)
data IpAddress = AnyIp | IpAddress Text
deriving (Eq, Show)
data Policy =
Policy
{ policyResource :: Resource
, policyStart :: StartTime
, policyEnd :: EndTime
, policyIpAddress :: IpAddress
}
deriving (Eq, Show)
prop_cloudFrontPolicy :: Property
prop_cloudFrontPolicy = withTests 1 $ property $ do
let
json =
[aesonQQ|
{
"Statement": [
{
"Resource": "http://d111111abcdef8.cloudfront.net/game_download.zip",
"Condition": {
"IpAddress": {"AWS:SourceIp": "192.0.2.0/24"},
"DateLessThan": {"AWS:EpochTime": 1357034400}
}
}
]
}
|]
d'time :: Decoder POSIXTime = at "AWS:EpochTime" integer <&> fromInteger
d :: Decoder Policy =
at ("Statement" <> only) $ do
res <- Resource <$> at "Resource" text
start <- maybe StartImmediately StartTime <$>
(optional $ at ("Condition" <> "DateGreaterThan") d'time)
end <- EndTime <$> at ("Condition" <> "DateLessThan") d'time
ip <- maybe AnyIp IpAddress <$>
(optional $ at ("Condition" <> "IpAddress")
(at "AWS:SourceIp" text))
pure $ Policy res start end ip
p =
Policy
(Resource "http://d111111abcdef8.cloudfront.net/game_download.zip")
StartImmediately
(EndTime 1357034400)
(IpAddress "192.0.2.0/24")
decodeMaybe d json === Just p