pg-wire-0.1.0.0: test/PgWire/MockServerSpec.hs
module PgWire.MockServerSpec (spec) where
import Control.Exception (try)
import Data.ByteString.Char8 qualified as BS8
import PgWire.Connection (Connection, close, connectString, simpleQuery)
import PgWire.Error (PgWireError (..))
import PgWire.MockServer
import Test.Hspec
connectToMock :: MockConfig -> (Connection -> IO a) -> IO a
connectToMock cfg action =
withMockServer cfg $ \port -> do
let connStr = "postgres://testuser:testpass@127.0.0.1:" <> BS8.pack (show port) <> "/testdb"
conn <- connectString connStr
result <- action conn
close conn
pure result
spec :: Spec
spec = do
describe "MockServer" $ do
describe "basic protocol" $ do
it "accepts a connection and responds to simple query" $ do
connectToMock defaultMockConfig $ \conn -> do
(rows, _) <- simpleQuery conn "SELECT 1"
rows `shouldBe` []
it "returns rows from a custom handler" $ do
let cfg = defaultMockConfig
{ mockQueryHandler = simpleHandler
[ ("SELECT name FROM test",
[ [("name", "Alice")]
, [("name", "Bob")]
])
]
}
connectToMock cfg $ \conn -> do
(rows, _) <- simpleQuery conn "SELECT name FROM test"
length rows `shouldBe` 2
it "handles unknown queries gracefully" $ do
let cfg = defaultMockConfig
{ mockQueryHandler = simpleHandler []
}
connectToMock cfg $ \conn -> do
(rows, _) <- simpleQuery conn "SELECT nothing"
rows `shouldBe` []
it "supports multiple sequential queries" $ do
connectToMock defaultMockConfig $ \conn -> do
_ <- simpleQuery conn "SELECT 1"
_ <- simpleQuery conn "SELECT 2"
(rows, _) <- simpleQuery conn "SELECT 3"
rows `shouldBe` []
describe "error responses" $ do
it "propagates server errors as QueryError" $ do
let cfg = defaultMockConfig
{ mockQueryHandler = errorHandler "42P01" "relation \"foo\" does not exist"
}
connectToMock cfg $ \conn -> do
result <- try @PgWireError (simpleQuery conn "SELECT * FROM foo")
case result of
Left (QueryError _) -> pure ()
Left other -> expectationFailure $ "Expected QueryError, got: " <> show other
Right _ -> expectationFailure "Expected an error"
it "recovers after an error and accepts subsequent queries" $ do
let cfg = defaultMockConfig
{ mockQueryHandler = \sql send ->
if sql == "BAD"
then send $ buildErrorResponse "42601" "syntax error"
else send $ buildCommandComplete "SELECT 0"
}
connectToMock cfg $ \conn -> do
-- First query errors
result1 <- try @PgWireError (simpleQuery conn "BAD")
case result1 of
Left (QueryError _) -> pure ()
_ -> expectationFailure "Expected QueryError for BAD query"
-- Second query succeeds on the same connection
(rows, _) <- simpleQuery conn "GOOD"
rows `shouldBe` []
describe "multi-row results" $ do
it "returns correct number of rows for large result sets" $ do
let manyRows = [ [("id", BS8.pack (show i)), ("val", "x")]
| i <- [1 :: Int .. 100]
]
cfg = defaultMockConfig
{ mockQueryHandler = simpleHandler
[("SELECT id, val FROM big", manyRows)]
}
connectToMock cfg $ \conn -> do
(rows, _) <- simpleQuery conn "SELECT id, val FROM big"
length rows `shouldBe` 100
it "returns multiple columns correctly" $ do
let cfg = defaultMockConfig
{ mockQueryHandler = simpleHandler
[("SELECT a, b, c FROM t",
[ [("a", "1"), ("b", "hello"), ("c", "true")]
, [("a", "2"), ("b", "world"), ("c", "false")]
])]
}
connectToMock cfg $ \conn -> do
(rows, _) <- simpleQuery conn "SELECT a, b, c FROM t"
length rows `shouldBe` 2
case rows of
[row1, row2] -> do
length row1 `shouldBe` 3
length row2 `shouldBe` 3
_ -> expectationFailure "Expected exactly 2 rows"