packages feed

json-to-haskell-0.1.1.0: test/Spec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
import Test.Hspec

import JsonToHaskell
import Data.Maybe
import Data.Aeson
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Lazy.Char8 as BC
import qualified Data.Text as T
import Text.RawString.QQ (r)

shouldRender :: BL.ByteString -> T.Text -> Expectation
shouldRender inp out =
    (jsonToHaskell simpleOptions{_includeHeader=False, _includeInstances=False} (fromMaybe (error $ "bad json: " <> BC.unpack inp) $ decode inp))
      `shouldBe` T.strip out


main :: IO ()
main = hspec spec

-- TODO: Should probably alphabetize records internally to ensure consistent order.
spec :: Spec
spec = do
    describe "analyze" $ do
        it "should build a record with all types" $ do
                [r|
{ "a": 1
, "b": "hi"
, "c": [null]
, "d": 2.5
}
                |]
               `shouldRender`
                [r|
data Model = Model
  { modelA :: Double
  , modelD :: Double
  , modelB :: Text
  , modelC :: [Maybe Value]
  } deriving (Show, Eq, Ord)
                |]
        it "should share records definitions for identical subrecords" $ do
                [r|
{ "a": { "name": "bob", "age": 20 }
, "b": { "name": "alice", "age": 22 }
}
                |]
               `shouldRender`
                [r|
data A = A
  { aAge :: Double
  , aName :: Text
  } deriving (Show, Eq, Ord)

data Model = Model
  { modelA :: A
  , modelB :: A
  } deriving (Show, Eq, Ord)
                |]
        it "should pick good names for differing records which share field names" $ do
                [r|
{ "a": { "field": {"name": "bob" } }
, "b": { "field": {"age": 22 } }
}
                |]
               `shouldRender`
                [r|
data A = A
  { aField :: Field
  } deriving (Show, Eq, Ord)

data B = B
  { bField :: Field2
  } deriving (Show, Eq, Ord)

data Field = Field
  { fieldName :: Text
  } deriving (Show, Eq, Ord)

data Field2 = Field2
  { field2Age :: Double
  } deriving (Show, Eq, Ord)

data Model = Model
  { modelA :: A
  , modelB :: B
  } deriving (Show, Eq, Ord)
                |]
        it "should pick the best name if there are multiple possible names but some conflict" $ do
                [r|
{ "a": { "field": {"name": "bob" } }
, "b": { "field": {"age": 22 } }
, "c": { "other": {"age": 22 } }
}
                |]
               `shouldRender`
                [r|
data A = A
  { aField :: Field
  } deriving (Show, Eq, Ord)

data B = B
  { bField :: Other
  } deriving (Show, Eq, Ord)

data C = C
  { cOther :: Other
  } deriving (Show, Eq, Ord)

data Field = Field
  { fieldName :: Text
  } deriving (Show, Eq, Ord)

data Model = Model
  { modelA :: A
  , modelB :: B
  , modelC :: C
  } deriving (Show, Eq, Ord)

data Other = Other
  { otherAge :: Double
  } deriving (Show, Eq, Ord)
                |]

        it "should sanitize weird field and record names" $ do
                [r|
{ "'t\"!h.*#e nam9e": {"'9sub--field": "bob" }
}
                |]
               `shouldRender`
                [r|
data Model = Model
  { modelTheNam9e :: TheNam9e
  } deriving (Show, Eq, Ord)

data TheNam9e = TheNam9e
  { theNam9eSubField :: Text
  } deriving (Show, Eq, Ord)
                |]