packages feed

conduit-aeson-0.1.0.0: tests/Data/Conduit/AesonSpec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Data.Conduit.AesonSpec (spec) where

import Conduit
import Data.Aeson
import Data.Conduit.Aeson
import qualified Data.Map.Strict as Map
import Data.Scientific
import qualified Data.Text as T
import GHC.Exts
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck

instance Arbitrary T.Text where
  arbitrary = T.pack <$> arbitrary

instance Arbitrary Value where
  arbitrary = sized sizedArbitraryValue

sizedArbitraryValue :: Int -> Gen Value
sizedArbitraryValue n
  | n <= 0 = oneof [pure Null, bool, number, string]
  | otherwise = resize n' $ oneof [pure Null, bool, number, string, array, object']
  where
    n' = n `div` 2
    bool = Bool <$> arbitrary
    number = Number <$> (scientific <$> arbitrary <*> arbitrary)
    string = String <$> arbitrary
    array = Array . fromList <$> arbitrary
    object' = listToObject <$> arbitrary

listToObject :: [(String, Value)] -> Value
listToObject xs = object [(fromString k, v) | (k, v) <- xs]

spec :: Spec
spec = do
  describe "roundtrips" $ do
    prop "conduitArray" $ \(xs :: [Value]) -> do
      xs' <-
        runConduit
          (sourceLazy (encode (Array (fromList xs))) .| conduitArray .| sinkList)
      xs' `shouldBe` xs
    prop "conduitObject" $ \(ls :: [(String, Value)]) -> do
      let xs = Map.fromList ls
      xs' <-
        runConduit
          (sourceLazy (encode (listToObject (Map.toAscList xs))) .|
           conduitObject .|
           sinkList)
      Map.fromList xs' `shouldBe` xs