packages feed

postgresql-types-0.1: src/unit-tests/PostgresqlTypes/DateSpec.hs

module PostgresqlTypes.DateSpec (spec) where

import Data.Data (Proxy (Proxy))
import Data.Maybe
import qualified Data.Time as Time
import qualified PostgresqlTypes.Date as Date
import Test.Hspec
import Test.QuickCheck
import qualified UnitTests.Scripts as Scripts
import Prelude

spec :: Spec
spec = do
  describe "Show/Read laws" do
    Scripts.testShowRead (Proxy @Date.Date)

  describe "IsScalar laws" do
    Scripts.testIsScalar (Proxy @Date.Date)

  describe "Constructors" do
    describe "normalizeFromDay" do
      it "clamps dates below minimum" do
        let tooEarly = Time.fromGregorian (-5000) 1 1
            date = Date.normalizeFromDay tooEarly
        date `shouldBe` minBound

      it "clamps dates above maximum" do
        let tooLate = Time.fromGregorian 6000000 12 31
            date = Date.normalizeFromDay tooLate
        date `shouldBe` maxBound

      it "accepts dates within range" do
        let day = Time.fromGregorian 2023 6 15
            date = Date.normalizeFromDay day
        Date.toDay date `shouldBe` day

      it "accepts minimum date" do
        let minDay = Time.fromGregorian (-4712) 1 1
            date = Date.normalizeFromDay minDay
        Date.toDay date `shouldBe` minDay

    describe "refineFromDay" do
      it "rejects dates below minimum" do
        let tooEarly = Time.fromGregorian (-5000) 1 1
        Date.refineFromDay tooEarly `shouldBe` Nothing

      it "rejects dates above maximum" do
        let tooLate = Time.fromGregorian 6000000 12 31
        Date.refineFromDay tooLate `shouldBe` Nothing

      it "accepts dates within range" do
        let day = Time.fromGregorian 2023 6 15
            result = Date.refineFromDay day
        result `shouldSatisfy` isJust
        fmap Date.toDay result `shouldBe` Just day

      it "accepts minimum date" do
        let minDay = Time.fromGregorian (-4712) 1 1
            result = Date.refineFromDay minDay
        result `shouldSatisfy` isJust

      it "accepts maximum date" do
        let maxDay = Time.fromGregorian 5874897 12 31
            result = Date.refineFromDay maxDay
        result `shouldSatisfy` isJust

  describe "Accessors" do
    describe "toDay" do
      it "extracts Day value" do
        let day = Time.fromGregorian 2023 6 15
            date = Date.normalizeFromDay day
        Date.toDay date `shouldBe` day

  describe "Property Tests" do
    it "normalizeFromDay is idempotent" do
      property \(day :: Time.Day) ->
        let normalized1 = Date.normalizeFromDay day
            normalized2 = Date.normalizeFromDay (Date.toDay normalized1)
         in normalized1 === normalized2

    it "refineFromDay succeeds for valid dates" do
      property \(date :: Date.Date) ->
        let day = Date.toDay date
            refined = Date.refineFromDay day
         in refined === Just date

    it "toDay . normalizeFromDay preserves valid dates" do
      property \(date :: Date.Date) ->
        let day = Date.toDay date
            restored = Date.normalizeFromDay day
         in restored === date