packages feed

boilerplate-0.0.2: test/Boilerplate/InterpreterTests.hs

{-# LANGUAGE OverloadedStrings #-}

module Boilerplate.InterpreterTests where

import Boilerplate.Interpreter
import Boilerplate.RuleParserTests (fromJSONRule, toJSONRule)
import Boilerplate.Types
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Text (Text)
import HsInspect.Types (Type(..))
import Test.Tasty.Hspec

-- data Coord = Coordy Double Double
egProduct :: Type
egProduct = ProductType "Coord" [] False "Coordy" [("Double", []), ("Double", [])]

-- data Coord = Coordy { a :: Double, b :: Double}
egRecord :: Type
egRecord = RecordType "Coord" [] False "Coordy" [("a", "Double", []), ("b", "Double", [])]

-- data Union = Wales String | England Int Int | Scotland
egSum :: Type
egSum = SumType "Union" [] [("Wales", [("String", [])]), ("England", [("Int", []), ("Int", [])]), ("Scotland", [])]

-- data Things a b = T a b Double
egPolyProduct :: Type
egPolyProduct = ProductType "Things" ["a", "b"] False "T" [("a", ["a"]), ("b", ["b"]), ("Double", [])]

-- data Logger m = Logger { log :: Text -> m () }
egPolyRecord :: Type
egPolyRecord = RecordType "Logger" ["m"] False "Logger" [("log", "Text -> m ()", ["m"])]

-- data Action a = Admin [a] Text | User a Text
egPolySum :: Type
egPolySum = SumType "Action" ["a"] [("Admin", [("[a]", ["a"]), ("Text", [])]), ("User", [("a", ["a"]), ("Text", [])])]

custom :: Text -> Custom -> Map Text Custom
custom key value = M.fromList [(key, value)]

spec_interpreter :: Spec
spec_interpreter = do
  it "should interpret ToJSON for products" $ do
    let options = custom "field" $ Indexed ["x", "y"]
    interpretRule toJSONRule egProduct options `shouldBe`
      Right "instance ToJSON Coord where\n\
            \  toJSON (Coordy p_1_1 p_1_2) = object [\"x\" .= p_1_1, \"y\" .= p_1_2]\n\
            \  toEncoding (Coordy p_1_1 p_1_2) = pairs (\"x\" .= p_1_1 <> \"y\" .= p_1_2)"

  it "should interpret FromJSON for products" $ do
    let options = custom "field" $ Indexed ["x", "y"]
    interpretRule fromJSONRule egProduct options `shouldBe`
      Right "instance FromJSON Coord where\n\
            \  parseJSON = withObject \"Coord\" $ \\v ->\n\
            \    Coordy <$> v .: \"x\" <*> v .: \"y\""

  it "should interpret FromJSON for records" $ do
    interpretRule fromJSONRule egRecord M.empty `shouldBe`
      Right "instance FromJSON Coord where\n\
            \  parseJSON = withObject \"Coord\" $ \\v ->\n\
            \    Coordy <$> v .: \"a\" <*> v .: \"b\""

  it "should interpret ToJSON for polymorphic products" $ do
    let options = custom "field" $ Indexed ["x", "y", "z"]
    interpretRule toJSONRule egPolyProduct options `shouldBe`
      Right "instance (ToJSON a, ToJSON b) => ToJSON (Things a b) where\n\
            \  toJSON (T p_1_1 p_1_2 p_1_3) = object [\"x\" .= p_1_1, \"y\" .= p_1_2, \"z\" .= p_1_3]\n\
            \  toEncoding (T p_1_1 p_1_2 p_1_3) = pairs (\"x\" .= p_1_1 <> \"y\" .= p_1_2 <> \"z\" .= p_1_3)"

  it "should interpret ToJSON for polymorphic sum" $ do
    let options = custom "field" $ Indexed ["x", "y", "z"]
    interpretRule toJSONRule egPolySum options `shouldBe`
      Right "instance (ToJSON a) => ToJSON (Action a) where\n\
            \  toJSON (Admin p_1_1 p_1_2) = object [\"Admin\" .= object [\"x\" .= p_1_1, \"y\" .= p_1_2]]\n\
            \  toJSON (User p_1_1 p_1_2) = object [\"User\" .= object [\"x\" .= p_1_1, \"y\" .= p_1_2]]\n\
            \  toEncoding (Admin p_1_1 p_1_2) = pairs . pair \"Admin\" $ pairs (\"x\" .= p_1_1 <> \"y\" .= p_1_2 :: Series)\n\
            \  toEncoding (User p_1_1 p_1_2) = pairs . pair \"User\" $ pairs (\"x\" .= p_1_1 <> \"y\" .= p_1_2 :: Series)"

  it "should interpret ToJSON for records without custom" $ do
    interpretRule toJSONRule egRecord M.empty `shouldBe`
      Right "instance ToJSON Coord where\n\
            \  toJSON (Coordy p_1_1 p_1_2) = object [\"a\" .= p_1_1, \"b\" .= p_1_2]\n\
            \  toEncoding (Coordy p_1_1 p_1_2) = pairs (\"a\" .= p_1_1 <> \"b\" .= p_1_2)"

  it "should interpret ToJSON for records with custom" $ do
    let options = custom "field" . Named $ M.fromList [("a", "aa"), ("b", "bb")]
    interpretRule toJSONRule egRecord options `shouldBe`
      Right "instance ToJSON Coord where\n\
            \  toJSON (Coordy p_1_1 p_1_2) = object [\"aa\" .= p_1_1, \"bb\" .= p_1_2]\n\
            \  toEncoding (Coordy p_1_1 p_1_2) = pairs (\"aa\" .= p_1_1 <> \"bb\" .= p_1_2)"

  it "should interpret ToJSON for sum types" $ do
    let options = custom "field" . NamedIndexed $ M.fromList [("Wales", ["foo"]), ("England", ["a", "b"]), ("Scotland", [])]
    interpretRule toJSONRule egSum options `shouldBe`
      Right "instance ToJSON Union where\n\
            \  toJSON (Wales p_1_1) = object [\"Wales\" .= object [\"foo\" .= p_1_1]]\n\
            \  toJSON (England p_1_1 p_1_2) = object [\"England\" .= object [\"a\" .= p_1_1, \"b\" .= p_1_2]]\n\
            \  toJSON Scotland = object [\"Scotland\" .= object []]\n\
            \  toEncoding (Wales p_1_1) = pairs . pair \"Wales\" $ pairs (\"foo\" .= p_1_1 :: Series)\n\
            \  toEncoding (England p_1_1 p_1_2) = pairs . pair \"England\" $ pairs (\"a\" .= p_1_1 <> \"b\" .= p_1_2 :: Series)\n\
            \  toEncoding Scotland = pairs . pair \"Scotland\" $ emptyObject_"

  it "should interpret FromJSON for sum types" $ do
    let options = custom "field" . NamedIndexed $ M.fromList [("Wales", ["foo"]), ("England", ["a", "b"]), ("Scotland", [])]
    interpretRule fromJSONRule egSum options `shouldBe`
      Right "instance FromJSON Union where\n\
            \  parseJSON = withObject \"Union\" $ \\v ->\n\
            \    let withField key parse = (maybe (fail \"\") pure $ H.lookup key v) >>= (withObject (T.unpack key) parse)\n\
            \     in (withField \"Wales\" $ \\v' -> Wales <$> v' .: \"foo\")\n\
            \    <|> (withField \"England\" $ \\v' -> England <$> v' .: \"a\" <*> v' .: \"b\")\n\
            \    <|> (withField \"Scotland\" $ \\_ -> pure Scotland)\n\
            \    <|> (fail \"no valid type constructor tags\")"

  it "should interpret ToJSON for sum types, extra options" $ do
    let options = M.fromList [("field", NamedIndexed $ M.fromList [("Wales", ["foo"]), ("England", ["a", "b"]), ("Scotland", [])]),
                              ("tag", Named $ M.fromList [("Wales", "dragon"), ("England", "lion"), ("Scotland", "unicorn")])]
    interpretRule toJSONRule egSum options `shouldBe`
      Right "instance ToJSON Union where\n\
            \  toJSON (Wales p_1_1) = object [\"dragon\" .= object [\"foo\" .= p_1_1]]\n\
            \  toJSON (England p_1_1 p_1_2) = object [\"lion\" .= object [\"a\" .= p_1_1, \"b\" .= p_1_2]]\n\
            \  toJSON Scotland = object [\"unicorn\" .= object []]\n\
            \  toEncoding (Wales p_1_1) = pairs . pair \"dragon\" $ pairs (\"foo\" .= p_1_1 :: Series)\n\
            \  toEncoding (England p_1_1 p_1_2) = pairs . pair \"lion\" $ pairs (\"a\" .= p_1_1 <> \"b\" .= p_1_2 :: Series)\n\
            \  toEncoding Scotland = pairs . pair \"unicorn\" $ emptyObject_"