-- | Coverage for 'Pqi.errorMessage': the connection-level error string.
--
-- The goal is byte-identical output to libpq's @PQerrorMessage@ in all
-- documented scenarios. Error strings are compared in full — not just for
-- presence — so formatting bugs are caught. Scenarios are chosen to avoid
-- statement-position fields (@'P'@), which depend on the client-stored query
-- text and cannot be reproduced from wire fields alone.
--
-- The one structurally incomparable value is the null-connection sentinel
-- @\"connection pointer is NULL\\n\"@: it is hardcoded in libpq rather than
-- derived from a wire response, but both adapters must return exactly that
-- string.
module Pqi.Conformance.Operation.ErrorMessage
  ( spec,
  )
where

import qualified Pqi
import Pqi.Conformance.Harness
import Pqi.Conformance.Prelude
import Pqi.Conformance.Scenario (drainResults)
import Test.Hspec

spec :: Pqi.Adapter -> SpecWith ByteString
spec :: Adapter -> SpecWith ByteString
spec Adapter
adapter =
  String -> SpecWith ByteString -> SpecWith ByteString
forall a. HasCallStack => String -> SpecWith a -> SpecWith a
describe String
"errorMessage" do
    String
-> (ByteString -> IO ()) -> SpecWith (Arg (ByteString -> IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"is empty on a healthy connection" \ByteString
conninfo ->
      Adapter
-> ByteString -> (Connection -> IO (Maybe ByteString)) -> IO ()
forall a.
(Eq a, Show a, HasCallStack) =>
Adapter -> ByteString -> (Connection -> IO a) -> IO ()
differential Adapter
adapter ByteString
conninfo \Connection
connection -> do
        _ <- Connection -> ByteString -> IO (Maybe Result)
Pqi.exec Connection
connection ByteString
"select 1"
        Pqi.errorMessage connection

    String
-> (ByteString -> IO ()) -> SpecWith (Arg (ByteString -> IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"is populated after a failed exec and cleared by a subsequent success" \ByteString
conninfo ->
      Adapter
-> ByteString
-> (Connection -> IO (Maybe ByteString, Maybe ByteString))
-> IO ()
forall a.
(Eq a, Show a, HasCallStack) =>
Adapter -> ByteString -> (Connection -> IO a) -> IO ()
differential Adapter
adapter ByteString
conninfo \Connection
connection -> do
        _ <- Connection -> ByteString -> IO (Maybe Result)
Pqi.exec Connection
connection ByteString
"do $$ begin raise exception 'conformance error'; end $$"
        afterFail <- Pqi.errorMessage connection
        _ <- Pqi.exec connection "select 1"
        afterSuccess <- Pqi.errorMessage connection
        pure (afterFail, afterSuccess)

    String
-> (ByteString -> IO ()) -> SpecWith (Arg (ByteString -> IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"is populated after a failed getResult and cleared by a subsequent success" \ByteString
conninfo ->
      Adapter
-> ByteString
-> (Connection -> IO (Maybe ByteString, Maybe ByteString))
-> IO ()
forall a.
(Eq a, Show a, HasCallStack) =>
Adapter -> ByteString -> (Connection -> IO a) -> IO ()
differential Adapter
adapter ByteString
conninfo \Connection
connection -> do
        _ <- Connection -> ByteString -> IO Bool
Pqi.sendQuery Connection
connection ByteString
"do $$ begin raise exception 'conformance error'; end $$"
        _ <- drainResults connection
        afterFail <- Pqi.errorMessage connection
        _ <- Pqi.sendQuery connection "select 1"
        _ <- drainResults connection
        afterSuccess <- Pqi.errorMessage connection
        pure (afterFail, afterSuccess)

    String
-> (ByteString -> IO ()) -> SpecWith (Arg (ByteString -> IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"is populated after a connection failure" \ByteString
conninfo ->
      Adapter
-> ByteString
-> (Adapter -> ByteString -> IO (Maybe ByteString))
-> IO ()
forall a.
(Eq a, Show a, HasCallStack) =>
Adapter -> ByteString -> (Adapter -> ByteString -> IO a) -> IO ()
differentialConnect Adapter
adapter ByteString
conninfo \Adapter
adapter' ByteString
conninfo' -> do
        conn <- Adapter -> ByteString -> IO Connection
Pqi.connectdb Adapter
adapter' (ByteString
conninfo' ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
" user=pqi_no_such_user")
        msg <- Pqi.errorMessage conn
        Pqi.finish conn
        pure msg

    String
-> (ByteString -> IO ()) -> SpecWith (Arg (ByteString -> IO ()))
forall a.
(HasCallStack, Example a) =>
String -> a -> SpecWith (Arg a)
it String
"is the null-connection sentinel on a null connection" \ByteString
conninfo ->
      Adapter
-> ByteString
-> (Adapter -> ByteString -> IO (Maybe ByteString))
-> IO ()
forall a.
(Eq a, Show a, HasCallStack) =>
Adapter -> ByteString -> (Adapter -> ByteString -> IO a) -> IO ()
differentialConnect Adapter
adapter ByteString
conninfo \Adapter
adapter' ByteString
_ -> do
        conn <- Adapter -> IO Connection
Pqi.newNullConnection Adapter
adapter'
        msg <- Pqi.errorMessage conn
        Pqi.finish conn
        pure msg