packages feed

valiant-0.1.0.0: test/Valiant/Binary/RefineSpec.hs

module Valiant.Binary.RefineSpec (spec) where

import Data.ByteString qualified as BS
import Data.Int (Int32)
import Data.Text (Text)
import Data.Text qualified as T
import Valiant.Binary.Decode (refine, refineWith)
import Valiant.Binary.Encode ()
import PgWire.Binary.Types (PgEncode (..))
import Test.Hspec

newtype PositiveInt = PositiveInt Int32
  deriving (Eq, Show)

mkPositive :: Int32 -> Either String PositiveInt
mkPositive n
  | n > 0 = Right (PositiveInt n)
  | otherwise = Left ("expected positive, got " <> show n)

newtype Email = Email Text
  deriving (Eq, Show)

mkEmail :: Text -> Either String Email
mkEmail t
  | T.any (== '@') t = Right (Email t)
  | otherwise = Left "missing '@'"

spec :: Spec
spec = do
  describe "refine" $ do
    it "passes through valid values" $
      refine mkPositive (pgEncode (42 :: Int32))
        `shouldBe` Right (PositiveInt 42)

    it "fails when the predicate rejects" $
      refine mkPositive (pgEncode (0 :: Int32))
        `shouldBe` (Left "expected positive, got 0" :: Either String PositiveInt)

    it "fails when the underlying decode fails" $
      refine mkPositive (BS.pack [0, 0])
        `shouldBe` (Left "int32: expected 4 bytes, got 2" :: Either String PositiveInt)

    it "works for Text refinements" $
      refine mkEmail (pgEncode ("a@b" :: Text))
        `shouldBe` Right (Email "a@b")

  describe "refineWith" $ do
    it "prepends context to predicate failures" $
      refineWith "PositiveInt" mkPositive (pgEncode (-1 :: Int32))
        `shouldBe` (Left "PositiveInt: expected positive, got -1" :: Either String PositiveInt)

    it "prepends context to decode failures" $
      refineWith "PositiveInt" mkPositive (BS.pack [0])
        `shouldBe` (Left "PositiveInt: int32: expected 4 bytes, got 1" :: Either String PositiveInt)

    it "passes valid values unchanged" $
      refineWith "Email" mkEmail (pgEncode ("x@y" :: Text))
        `shouldBe` Right (Email "x@y")