packages feed

restman-0.7.3.0: test/Props/TypesProps.hs

module Props.TypesProps (tests) where

-- hedgehog
import Hedgehog
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range

-- tasty / tasty-hedgehog
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.Hedgehog (testProperty)

-- restman
import Types
  (AppR(..), CustomHeaderColumn(..), CustomHeaderR(..), RangeInAlign(..))

tests :: TestTree
tests = testGroup "Props.Types"
  [ rangeInAlignProps
  , customHeaderColumnProps
  , customHeaderRProps
  , appRProps
  ]

-- ---------------------------------------------------------------------------
-- Generators
-- ---------------------------------------------------------------------------

genRangeInAlign :: Gen RangeInAlign
genRangeInAlign = Gen.element [Min, MidL, MidG, Max]

genCustomHeaderColumn :: Gen CustomHeaderColumn
genCustomHeaderColumn = Gen.element [ActiveToggle, NameEditor, ValueEditor]

genCustomHeaderR :: Gen CustomHeaderR
genCustomHeaderR = MkCustomHeaderR
  <$> Gen.int (Range.linear 0 100)
  <*> genCustomHeaderColumn

-- AppR without ExistingCustomHeader to keep generation simple.
genSimpleAppR :: Gen AppR
genSimpleAppR = Gen.element
  [ MethodEditor, UrlEditor, DefaultHeadersToggle
  , AddCustomHeader, ResponseBodyView, MethodSelector
  ]

-- ---------------------------------------------------------------------------
-- Ord law helpers
-- ---------------------------------------------------------------------------

-- Ord reflexivity: compare x x == EQ
prop_ord_reflexive :: (Show a, Ord a) => Gen a -> Property
prop_ord_reflexive gen = property $ do
  x <- forAll gen
  compare x x === EQ

-- Ord antisymmetry: compare x y == opposite of compare y x
prop_ord_antisymmetric :: (Show a, Ord a) => Gen a -> Property
prop_ord_antisymmetric gen = property $ do
  x <- forAll gen
  y <- forAll gen
  compare x y === flipOrd (compare y x)
 where
  flipOrd LT = GT
  flipOrd GT = LT
  flipOrd EQ = EQ

-- Ord transitivity: x <= y && y <= z implies x <= z
prop_ord_transitive :: (Show a, Ord a) => Gen a -> Property
prop_ord_transitive gen = property $ do
  x <- forAll gen
  y <- forAll gen
  z <- forAll gen
  if x <= y && y <= z
    then assert (x <= z)
    else success

-- Eq/Ord consistency: (x == y) iff (compare x y == EQ)
prop_eq_ord_consistent :: (Show a, Eq a) => Gen a -> Property
prop_eq_ord_consistent gen = property $ do
  x <- forAll gen
  y <- forAll gen
  (x == y) === (y == x)

-- ---------------------------------------------------------------------------
-- RangeInAlign
-- ---------------------------------------------------------------------------

rangeInAlignProps :: TestTree
rangeInAlignProps = testGroup "RangeInAlign Ord"
  [ testProperty "reflexive"             (prop_ord_reflexive     genRangeInAlign)
  , testProperty "antisymmetric"         (prop_ord_antisymmetric genRangeInAlign)
  , testProperty "transitive"            (prop_ord_transitive    genRangeInAlign)
  , testProperty "Eq/Ord consistent"     (prop_eq_ord_consistent genRangeInAlign)
  , testProperty "total: every pair is comparable" $ property $ do
      x <- forAll genRangeInAlign
      y <- forAll genRangeInAlign
      assert $ compare x y `elem` [LT, EQ, GT]
  ]

-- ---------------------------------------------------------------------------
-- CustomHeaderColumn
-- ---------------------------------------------------------------------------

customHeaderColumnProps :: TestTree
customHeaderColumnProps = testGroup "CustomHeaderColumn Ord"
  [ testProperty "reflexive"             (prop_ord_reflexive     genCustomHeaderColumn)
  , testProperty "antisymmetric"         (prop_ord_antisymmetric genCustomHeaderColumn)
  , testProperty "transitive"            (prop_ord_transitive    genCustomHeaderColumn)
  , testProperty "Eq/Ord consistent"     (prop_eq_ord_consistent genCustomHeaderColumn)
  ]

-- ---------------------------------------------------------------------------
-- CustomHeaderR
-- Ordering is lexicographic: first by listIndex, then by column.
-- ---------------------------------------------------------------------------

customHeaderRProps :: TestTree
customHeaderRProps = testGroup "CustomHeaderR Ord"
  [ testProperty "reflexive"             (prop_ord_reflexive     genCustomHeaderR)
  , testProperty "antisymmetric"         (prop_ord_antisymmetric genCustomHeaderR)
  , testProperty "transitive"            (prop_ord_transitive    genCustomHeaderR)
  , testProperty "Eq/Ord consistent"     (prop_eq_ord_consistent genCustomHeaderR)
  , testProperty "larger listIndex sorts later" $ property $ do
      i <- forAll $ Gen.int (Range.linear 0 99)
      col1 <- forAll genCustomHeaderColumn
      col2 <- forAll genCustomHeaderColumn
      let lo = MkCustomHeaderR i       col1
          hi = MkCustomHeaderR (i + 1) col2
      assert (lo < hi)
  , testProperty "same listIndex: column determines order" $ property $ do
      i    <- forAll $ Gen.int (Range.linear 0 100)
      col1 <- forAll genCustomHeaderColumn
      col2 <- forAll genCustomHeaderColumn
      let r1 = MkCustomHeaderR i col1
          r2 = MkCustomHeaderR i col2
      compare r1 r2 === compare col1 col2
  ]

-- ---------------------------------------------------------------------------
-- AppR (simple constructors only — ExistingCustomHeader wraps CustomHeaderR)
-- ---------------------------------------------------------------------------

appRProps :: TestTree
appRProps = testGroup "AppR Ord (simple constructors)"
  [ testProperty "reflexive"             (prop_ord_reflexive     genSimpleAppR)
  , testProperty "antisymmetric"         (prop_ord_antisymmetric genSimpleAppR)
  , testProperty "transitive"            (prop_ord_transitive    genSimpleAppR)
  , testProperty "Eq/Ord consistent"     (prop_eq_ord_consistent genSimpleAppR)
  ]