module Test.MarshallError
( marshallErrorTests
)
where
import qualified Control.Exception as E
import qualified Data.ByteString.Char8 as B8
import qualified Data.Set as Set
import qualified Data.Text as T
import Hedgehog ((===))
import qualified Hedgehog as HH
import qualified Hedgehog.Gen as Gen
import qualified Orville.PostgreSQL as Orville
import qualified Orville.PostgreSQL.ErrorDetailLevel as ErrorDetailLevel
import qualified Orville.PostgreSQL.Marshall.MarshallError as MarshallError
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
import qualified Orville.PostgreSQL.Raw.SqlValue as SqlValue
import qualified Test.Property as Property
marshallErrorTests :: Orville.ConnectionPool -> Property.Group
marshallErrorTests pool =
Property.group
"MarshallError"
[ prop_renderDecodingErrorWithFullDetails
, prop_renderDecodingErrorWithoutSchemaNames
, prop_renderDecodingErrorWithoutRowIdValues
, prop_renderDecodingErrorWithoutNonIdValues
, prop_renderDecodingErrorWithoutErrorMessage
, prop_renderMissingErrorDetailsWithFullDetails
, prop_renderMissingErrorDetailsWithoutSchemaNames
, prop_showMarshallErrorRaisedFromOrvilleContext pool
]
{- | Shared example used to test error detail level rendering of decoding errors below.
@since 1.0.0.0
-}
exampleDecodingError :: MarshallError.MarshallError
exampleDecodingError =
MarshallError.MarshallError
{ MarshallError.marshallErrorDetailLevel = ErrorDetailLevel.minimalErrorDetailLevel
, MarshallError.marshallErrorRowIdentifier = [(B8.pack "foo", SqlValue.fromInt8 1)]
, MarshallError.marshallErrorDetails =
MarshallError.DecodingError $
MarshallError.DecodingErrorDetails
{ MarshallError.decodingErrorValues = [(B8.pack "bar", SqlValue.fromText (T.pack "baz"))]
, MarshallError.decodingErrorMessage = "Failure"
}
}
prop_renderDecodingErrorWithFullDetails :: Property.NamedProperty
prop_renderDecodingErrorWithFullDetails =
Property.singletonNamedProperty "Render decoding error with full details" $
let
rendered =
MarshallError.renderMarshallError
ErrorDetailLevel.maximalErrorDetailLevel
exampleDecodingError
in
rendered
=== "Unable to decode row with identifier [foo = 1]: \
\Unable to decode columns from result set: Failure. \
\Value(s) that failed to decode: [bar = baz]"
prop_renderDecodingErrorWithoutSchemaNames :: Property.NamedProperty
prop_renderDecodingErrorWithoutSchemaNames =
Property.singletonNamedProperty "Render decoding error without schema names" $
let
rendered =
MarshallError.renderMarshallError
( ErrorDetailLevel.maximalErrorDetailLevel
{ ErrorDetailLevel.includeSchemaNames = False
}
)
exampleDecodingError
in
rendered
=== "Unable to decode row with identifier [[REDACTED] = 1]: \
\Unable to decode columns from result set: Failure. \
\Value(s) that failed to decode: [[REDACTED] = baz]"
prop_renderDecodingErrorWithoutRowIdValues :: Property.NamedProperty
prop_renderDecodingErrorWithoutRowIdValues =
Property.singletonNamedProperty "Render decoding error without row id values" $
let
rendered =
MarshallError.renderMarshallError
( ErrorDetailLevel.maximalErrorDetailLevel
{ ErrorDetailLevel.includeRowIdentifierValues = False
}
)
exampleDecodingError
in
rendered
=== "Unable to decode row with identifier [foo = [REDACTED]]: \
\Unable to decode columns from result set: Failure. \
\Value(s) that failed to decode: [bar = baz]"
prop_renderDecodingErrorWithoutNonIdValues :: Property.NamedProperty
prop_renderDecodingErrorWithoutNonIdValues =
Property.singletonNamedProperty "Render decoding error without non id values" $
let
rendered =
MarshallError.renderMarshallError
( ErrorDetailLevel.maximalErrorDetailLevel
{ ErrorDetailLevel.includeNonIdentifierValues = False
}
)
exampleDecodingError
in
rendered
=== "Unable to decode row with identifier [foo = 1]: \
\Unable to decode columns from result set: Failure. \
\Value(s) that failed to decode: [bar = [REDACTED]]"
prop_renderDecodingErrorWithoutErrorMessage :: Property.NamedProperty
prop_renderDecodingErrorWithoutErrorMessage =
Property.singletonNamedProperty "Render decoding error without non id values" $
let
rendered =
MarshallError.renderMarshallError
( ErrorDetailLevel.maximalErrorDetailLevel
{ ErrorDetailLevel.includeErrorMessage = False
}
)
exampleDecodingError
in
rendered
=== "Unable to decode row with identifier [foo = 1]: \
\Unable to decode columns from result set: [REDACTED]. \
\Value(s) that failed to decode: [bar = baz]"
{- | Shared example used to test error detail level rendering of missing column errors below.
@since 1.0.0.0
-}
exampleMissingColumnError :: MarshallError.MarshallError
exampleMissingColumnError =
MarshallError.MarshallError
{ MarshallError.marshallErrorDetailLevel = ErrorDetailLevel.minimalErrorDetailLevel
, MarshallError.marshallErrorRowIdentifier = [(B8.pack "foo", SqlValue.fromInt8 1)]
, MarshallError.marshallErrorDetails =
MarshallError.MissingColumnError $
MarshallError.MissingColumnErrorDetails
{ MarshallError.missingColumnName = B8.pack "baz"
, MarshallError.actualColumnNames = Set.fromList [B8.pack "foo", B8.pack "bar"]
}
}
prop_renderMissingErrorDetailsWithFullDetails :: Property.NamedProperty
prop_renderMissingErrorDetailsWithFullDetails =
Property.singletonNamedProperty "Render missing column error with full details" $
let
rendered =
MarshallError.renderMarshallError
ErrorDetailLevel.maximalErrorDetailLevel
exampleMissingColumnError
in
rendered
=== "Unable to decode row with identifier [foo = 1]: \
\Column baz not found in results set. \
\Actual columns were [bar, foo]"
prop_renderMissingErrorDetailsWithoutSchemaNames :: Property.NamedProperty
prop_renderMissingErrorDetailsWithoutSchemaNames =
Property.singletonNamedProperty "Render missing column error without schema names" $
let
rendered =
MarshallError.renderMarshallError
( ErrorDetailLevel.maximalErrorDetailLevel
{ ErrorDetailLevel.includeSchemaNames = False
}
)
exampleMissingColumnError
in
rendered
=== "Unable to decode row with identifier [[REDACTED] = 1]: \
\Column [REDACTED] not found in results set. \
\Actual columns were [[REDACTED], [REDACTED]]"
prop_showMarshallErrorRaisedFromOrvilleContext :: Property.NamedDBProperty
prop_showMarshallErrorRaisedFromOrvilleContext =
Property.namedDBProperty "Showing a MarshallError raised from an Orville context" $ \pool -> do
errorDetailLevel <- HH.forAll generateErrorDetailLevel
let
sql =
RawSql.fromString "SELECT 1 as id, 'notAnNumber' as number"
marshaller =
Orville.annotateSqlMarshaller [Orville.stringToFieldName "id"] $
Orville.marshallField id (Orville.integerField "number")
orvilleState =
Orville.newOrvilleState errorDetailLevel pool
result <-
HH.evalIO $ do
E.try . Orville.runOrvilleWithState orvilleState $ do
Orville.executeAndDecode Orville.SelectQuery sql marshaller
case result of
Right _ -> do
HH.annotate "Expected decoding result set to fail, but it did not"
HH.failure
Left marshallError ->
show marshallError
=== MarshallError.renderMarshallError errorDetailLevel marshallError
generateErrorDetailLevel :: HH.Gen ErrorDetailLevel.ErrorDetailLevel
generateErrorDetailLevel =
ErrorDetailLevel.ErrorDetailLevel
<$> Gen.bool
<*> Gen.bool
<*> Gen.bool
<*> Gen.bool