hasql-1.9.3.2: hspec/Hasql/EncodingDecodingSpec.hs
module Hasql.EncodingDecodingSpec (spec) where
import Hasql.Decoders qualified as Decoders
import Hasql.Encoders qualified as Encoders
import Hasql.Session qualified as Session
import Hasql.Statement qualified as Statement
import Hasql.TestingKit.Testcontainers qualified as Testcontainers
import Test.Hspec
import Test.QuickCheck
import Test.QuickCheck.Instances ()
import Test.QuickCheck.Monadic (assert, monadicIO, pre, run)
import Prelude hiding (assert)
spec :: Spec
spec = aroundAll Testcontainers.withConnection do
describe "Encoding and Decoding" do
describe "Array roundtrips" do
it "handles 1D arrays" \connection -> property $ \(values :: [Int64]) -> monadicIO $ do
let statement =
Statement.Statement
"select $1"
(Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8))))))
(Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.array (Decoders.dimension replicateM (Decoders.element (Decoders.nonNullable Decoders.int8)))))))
True
result <- run $ Session.run (Session.statement values statement) connection
assert $ result == Right values
it "handles 2D arrays" \connection -> property $ \(values :: [Int64]) -> monadicIO $ do
pre (not (null values))
let input = replicate 3 values
let statement =
Statement.Statement
"select $1"
(Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8)))))))
(Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.array (Decoders.dimension replicateM (Decoders.dimension replicateM (Decoders.element (Decoders.nonNullable Decoders.int8))))))))
True
result <- run $ Session.run (Session.statement input statement) connection
assert $ result == Right input
describe "Composite types" do
it "decodes simple composites" \connection -> do
let statement =
Statement.Statement
"select (1, true)"
mempty
(Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.composite ((,) <$> (Decoders.field (Decoders.nonNullable Decoders.int8)) <*> (Decoders.field (Decoders.nonNullable Decoders.bool)))))))
True
result <- Session.run (Session.statement () statement) connection
result `shouldBe` Right (1 :: Int64, True)
it "decodes complex composites" \connection -> do
let statement =
Statement.Statement
"select ((1, true), ('hello', 3))"
mempty
( Decoders.singleRow
( Decoders.column
( Decoders.nonNullable
( Decoders.composite
( (,)
<$> ( Decoders.field
( Decoders.nonNullable
( Decoders.composite
( (,)
<$> (Decoders.field (Decoders.nonNullable Decoders.int8))
<*> (Decoders.field (Decoders.nonNullable Decoders.bool))
)
)
)
)
<*> ( Decoders.field
( Decoders.nonNullable
( Decoders.composite
( (,)
<$> (Decoders.field (Decoders.nonNullable Decoders.text))
<*> (Decoders.field (Decoders.nonNullable Decoders.int8))
)
)
)
)
)
)
)
)
)
True
result <- Session.run (Session.statement () statement) connection
result `shouldBe` Right ((1 :: Int64, True), ("hello", 3 :: Int64))
describe "Enum types" do
it "handles enum encoding and decoding" \connection -> do
-- First create the enum type
let dropStatement = Statement.Statement "drop type if exists mood" mempty Decoders.noResult True
let createStatement = Statement.Statement "create type mood as enum ('sad', 'ok', 'happy')" mempty Decoders.noResult True
let testStatement =
Statement.Statement
"select ($1 :: mood)"
(Encoders.param (Encoders.nonNullable (Encoders.enum id)))
(Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.enum (Just . id)))))
True
result <-
Session.run
( do
Session.statement () dropStatement
Session.statement () createStatement
Session.statement "ok" testStatement
)
connection
result `shouldBe` Right "ok"
describe "Unknown enum" do
it "handles unknown enum encoding" \connection -> do
-- First create the enum type
let dropStatement = Statement.Statement "drop type if exists mood" mempty Decoders.noResult True
let createStatement = Statement.Statement "create type mood as enum ('sad', 'ok', 'happy')" mempty Decoders.noResult True
let testStatement =
Statement.Statement
"select $1"
(Encoders.param (Encoders.nonNullable (Encoders.unknownEnum id)))
(Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.enum (Just . id)))))
True
result <-
Session.run
( do
Session.statement () dropStatement
Session.statement () createStatement
Session.statement "ok" testStatement
)
connection
result `shouldBe` Right "ok"