mmzk-env-0.4.0.0: test/RecordParserSpec.hs
module RecordParserSpec ( spec ) where
import Data.Coerce ( coerce )
import Data.Env.EnumParser
import Data.Env.ParseError
import Data.Env.RecordParser
import Data.Env.TypeParser
import Data.Map qualified as M
import GHC.Generics
import Test.Hspec
data Gender = Male | Female
deriving (Show, Eq, Enum, Bounded)
deriving TypeParser via (EnumParser Gender)
newtype Age = Age Int
deriving (Show, Eq)
instance TypeParser Age where
parseType :: String -> Either String Age
parseType str = case parseType @Int str of
Right a -> if a >= 0 && a <= 120 then Right $ Age a else Left "Age is not within range!"
left -> coerce left
data Person = Person
{ name :: String
, age :: Age
, gender :: Maybe Gender
} deriving (Show, Eq, Generic)
spec :: Spec
spec = describe "parseRecord" do
it "parses a valid record" do
let env = M.fromList [("name", "Alice"), ("age", "30")]
parseRecord @Person env `shouldBe` Right (Person "Alice" (Age 30) Nothing)
it "allows extra fields" do
let env = M.fromList [("name", "Alice"), ("age", "30"), ("extra", "value")]
parseRecord @Person env `shouldBe` Right (Person "Alice" (Age 30) Nothing)
it "parses optional fields" do
let env = M.fromList [("name", "Alice"), ("age", "30"), ("gender", "Female")]
parseRecord @Person env `shouldBe` Right (Person "Alice" (Age 30) (Just Female))
it "fails to parse an invalid enum value" do
let env = M.fromList [("name", "Alice"), ("age", "30"), ("gender", "Other")]
case parseRecord @Person env of
Right _ -> expectationFailure "expected Left"
Left (ParseError errs) -> do
length errs `shouldBe` 1
(head errs).errField `shouldBe` "gender"
(head errs).errMessage `shouldContain` "\"Other\""
(head errs).errMessage `shouldContain` "Male"
(head errs).errMessage `shouldContain` "Female"
it "fails to parse an invalid age" do
let env = M.fromList [("name", "Alice"), ("age", "150"), ("gender", "Female")]
case parseRecord @Person env of
Right _ -> expectationFailure "expected Left"
Left (ParseError errs) -> do
length errs `shouldBe` 1
(head errs).errField `shouldBe` "age"
(head errs).errMessage `shouldBe` "Age is not within range!"
it "fails to parse a missing required field" do
let env = M.fromList [("name", "Alice")]
case parseRecord @Person env of
Right _ -> expectationFailure "expected Left"
Left (ParseError errs) -> do
length errs `shouldBe` 1
(head errs).errField `shouldBe` "age"
(head errs).errMessage `shouldBe` "missing required environment variable"
it "collects errors from multiple invalid fields in one pass" do
let env = M.fromList [("name", "Alice"), ("age", "150"), ("gender", "Other")]
case parseRecord @Person env of
Right _ -> expectationFailure "expected Left"
Left (ParseError errs) -> do
-- Both age and gender fail; name succeeds.
length errs `shouldBe` 2
-- Errors are in field-declaration order.
map (.errField) errs `shouldBe` ["age", "gender"]
(errs !! 0).errMessage `shouldBe` "Age is not within range!"
(errs !! 1).errMessage `shouldContain` "\"Other\""