packages feed

orville-postgresql-1.0.0.0: test/Test/Expr/Time.hs

module Test.Expr.Time
  ( timeTests
  )
where

import qualified Data.ByteString.Char8 as B8
import Data.Int (Int32)
import qualified Data.Maybe as Maybe
import qualified Data.Text as T
import qualified Data.Time as Time
import Hedgehog ((===))
import qualified Hedgehog as HH
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import qualified Text.Printf as Printf

import qualified Orville.PostgreSQL as Orville
import qualified Orville.PostgreSQL.Expr as Expr
import qualified Orville.PostgreSQL.Raw.RawSql as RawSql
import qualified Orville.PostgreSQL.Raw.SqlValue as SqlValue

import qualified Test.Property as Property

timeTests :: Orville.ConnectionPool -> Property.Group
timeTests pool =
  Property.group
    "Expr - Time"
    [ prop_now pool
    , prop_makeInterval pool
    ]

prop_now :: Property.NamedDBProperty
prop_now =
  Property.singletonNamedDBProperty "now()" $ \pool -> do
    let
      sql =
        Expr.queryExpr
          (Expr.selectClause (Expr.selectExpr Nothing))
          ( Expr.selectDerivedColumns
              [ Expr.deriveColumnAs Expr.now (Expr.columnName "result")
              ]
          )
          Nothing

      marshaller =
        Orville.annotateSqlMarshallerEmptyAnnotation $
          Orville.marshallField id (Orville.utcTimestampField "result")

    result <-
      HH.evalIO $
        Orville.runOrville pool $
          Orville.executeAndDecode Orville.SelectQuery sql marshaller

    today <- fmap Time.utctDay (HH.evalIO Time.getCurrentTime)
    fmap Time.utctDay result === [today]

prop_makeInterval :: Property.NamedDBProperty
prop_makeInterval =
  Property.namedDBProperty "make_interval()" $ \pool -> do
    intervalValues <- HH.forAllWith (T.unpack . renderExpectedValue) $ do
      years <- Gen.integral (Range.linear 1 100)
      months <- Gen.integral (Range.linear 1 11)
      weeks <- Gen.integral (Range.linear 1 3)
      days <- Gen.integral (Range.linear 1 6)
      hours <- Gen.integral (Range.linear 1 23)
      minutes <- Gen.integral (Range.linear 1 59)
      seconds <- Gen.integral (Range.linear 1 59)

      pure $
        [ (Expr.years, years)
        , (Expr.months, months)
        , (Expr.weeks, weeks)
        , (Expr.days, days)
        , (Expr.hours, hours)
        , (Expr.minutes, minutes)
        , (Expr.seconds, seconds)
        ]

    let
      intervalExpression =
        Expr.makeInterval $
          map
            (\(arg, value) -> (arg, Expr.valueExpression (SqlValue.fromInt32 value)))
            intervalValues

      sql =
        Expr.queryExpr
          (Expr.selectClause (Expr.selectExpr Nothing))
          ( Expr.selectDerivedColumns
              [ Expr.deriveColumnAs intervalExpression (Expr.columnName "result")
              ]
          )
          Nothing

      marshaller =
        Orville.annotateSqlMarshallerEmptyAnnotation $
          Orville.marshallField id (Orville.unboundedTextField "result")

    result <-
      HH.evalIO $
        Orville.runOrville pool $
          Orville.executeAndDecode Orville.SelectQuery sql marshaller

    result === [renderExpectedValue intervalValues]

renderExpectedValue :: [(Expr.IntervalArgument, Int32)] -> T.Text
renderExpectedValue values =
  let
    valuesByName =
      map
        (\(arg, value) -> (RawSql.toExampleBytes arg, value))
        values

    lookupInterval name =
      Maybe.fromMaybe 0 (lookup (B8.pack name) valuesByName)

    render :: Int32 -> String -> Maybe T.Text
    render value singularName =
      case value of
        0 -> Nothing
        1 -> Just (T.pack (Printf.printf "%d %s" value singularName))
        _ -> Just (T.pack (Printf.printf "%d %ss" value singularName))

    mbTime =
      case (lookupInterval "hours", lookupInterval "mins", lookupInterval "secs") of
        (0, 0, 0) -> Nothing
        (h, m, s) -> Just (T.pack (Printf.printf "%02d:%02d:%02d" h m s))
  in
    T.intercalate (T.pack " ") . Maybe.catMaybes $
      [ render (lookupInterval "years") "year"
      , render (lookupInterval "months") "mon"
      , render ((7 * lookupInterval "weeks") + lookupInterval "days") "day"
      , mbTime
      ]