packages feed

moonlight-delta-0.1.0.0: test/epoch/ProjectionSpec.hs

{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}

module ProjectionSpec
  ( projectionTests,
  )
where

import Data.IntSet (IntSet)
import Moonlight.Core
  ( IsLawName (..),
    OrdSet (..),
    constructorLawName,
  )
import Moonlight.Delta.Epoch
import EpochSupport.Generators
import EpochSupport.Mapping
import EpochSupport.Types
import Moonlight.Delta.Normalize
import Moonlight.Delta.Support
import LawManifest
  ( lawManifestCase,
    lawProperty,
  )
import Test.QuickCheck
  ( Property,
    (===),
    (.&&.),
  )
import Test.Tasty (TestTree, testGroup)

data EpochProjectionLaw
  = EpochProjectionUnionAssociative
  | EpochProjectionUnionCommutative
  | EpochProjectionUnionIdempotent
  | EpochProjectionEmptyUnit
  | EpochProjectionMapIdentity
  | EpochProjectionMapComposition
  | EpochProjectionMapSemilatticeHomomorphism
  | EpochProjectionNullIffComponentsEmpty
  | EpochProjectionNormalizeCanonicalCarrier
  deriving stock (Bounded, Enum, Eq, Ord, Show)

instance IsLawName EpochProjectionLaw where
  lawNameText =
    constructorLawName . show

projectionTests :: TestTree
projectionTests =
  testGroup
    "projection"
    [ lawManifestCase "epoch projection" ([minBound .. maxBound] :: [EpochProjectionLaw]),
      lawProperty EpochProjectionUnionAssociative $
        projectionIntTripleProperty projectionUnionAssociative
          .&&. projectionGenericTripleProperty projectionUnionAssociative,
      lawProperty EpochProjectionUnionCommutative $
        projectionIntPairProperty projectionUnionCommutative
          .&&. projectionGenericPairProperty projectionUnionCommutative,
      lawProperty EpochProjectionUnionIdempotent $
        projectionIntProperty projectionUnionIdempotent
          .&&. projectionGenericProperty projectionUnionIdempotent,
      lawProperty EpochProjectionEmptyUnit $
        projectionIntProperty projectionEmptyUnit
          .&&. projectionGenericProperty projectionEmptyUnit,
      lawProperty EpochProjectionMapIdentity $
        projectionIntProperty projectionMapIdentityInt
          .&&. projectionGenericProperty projectionMapIdentityGeneric,
      lawProperty EpochProjectionMapComposition $
        projectionIntProperty projectionMapCompositionInt
          .&&. projectionGenericProperty projectionMapCompositionGeneric,
      lawProperty EpochProjectionMapSemilatticeHomomorphism $
        projectionIntPairProperty projectionMapSemilatticeHomomorphismInt
          .&&. projectionGenericPairProperty projectionMapSemilatticeHomomorphismGeneric,
      lawProperty EpochProjectionNullIffComponentsEmpty $
        projectionIntProperty projectionNullIffComponentsEmpty
          .&&. projectionGenericProperty projectionNullIffComponentsEmpty,
      lawProperty EpochProjectionNormalizeCanonicalCarrier $
        projectionIntProperty projectionNormalizeCanonicalCarrier
          .&&. projectionGenericProperty projectionNormalizeCanonicalCarrier
    ]
projectionUnionAssociative ::
  (Eq observed, Show observed, OrdSet observed) =>
  (ContextProjectionDelta observed, ContextProjectionDelta observed, ContextProjectionDelta observed) ->
  Property
projectionUnionAssociative (left, middle, right) =
  left <> (middle <> right) === (left <> middle) <> right

projectionUnionCommutative ::
  (Eq observed, Show observed, OrdSet observed) =>
  (ContextProjectionDelta observed, ContextProjectionDelta observed) ->
  Property
projectionUnionCommutative (left, right) =
  left <> right === right <> left

projectionUnionIdempotent ::
  (Eq observed, Show observed, OrdSet observed) =>
  ContextProjectionDelta observed ->
  Property
projectionUnionIdempotent deltaValue =
  deltaValue <> deltaValue === deltaValue

projectionEmptyUnit ::
  (Eq observed, Show observed, OrdSet observed) =>
  ContextProjectionDelta observed ->
  Property
projectionEmptyUnit deltaValue =
  (emptyContextProjectionDelta <> deltaValue, deltaValue <> emptyContextProjectionDelta)
    === (deltaValue, deltaValue)

projectionMapIdentityInt :: ContextProjectionDelta IntSet -> Property
projectionMapIdentityInt deltaValue =
  mapProjectionInt identityInt deltaValue === deltaValue

projectionMapIdentityGeneric :: ContextProjectionDelta GenericSet -> Property
projectionMapIdentityGeneric deltaValue =
  mapProjectionGeneric identityGenericKey deltaValue === deltaValue

projectionMapCompositionInt :: ContextProjectionDelta IntSet -> Property
projectionMapCompositionInt deltaValue =
  mapProjectionInt (incrementInt . doubleInt) deltaValue
    === mapProjectionInt incrementInt (mapProjectionInt doubleInt deltaValue)

projectionMapCompositionGeneric :: ContextProjectionDelta GenericSet -> Property
projectionMapCompositionGeneric deltaValue =
  mapProjectionGeneric (genericIncrement . genericDouble) deltaValue
    === mapProjectionGeneric genericIncrement (mapProjectionGeneric genericDouble deltaValue)

projectionMapSemilatticeHomomorphismInt ::
  (ContextProjectionDelta IntSet, ContextProjectionDelta IntSet) ->
  Property
projectionMapSemilatticeHomomorphismInt (left, right) =
  mapProjectionInt incrementInt (left <> right)
    === mapProjectionInt incrementInt left <> mapProjectionInt incrementInt right

projectionMapSemilatticeHomomorphismGeneric ::
  (ContextProjectionDelta GenericSet, ContextProjectionDelta GenericSet) ->
  Property
projectionMapSemilatticeHomomorphismGeneric (left, right) =
  mapProjectionGeneric genericIncrement (left <> right)
    === mapProjectionGeneric genericIncrement left <> mapProjectionGeneric genericIncrement right

projectionNullIffComponentsEmpty ::
  (Eq observed, Show observed, OrdSet observed) =>
  ContextProjectionDelta observed ->
  Property
projectionNullIffComponentsEmpty deltaValue =
  nullContextProjectionDelta deltaValue
    === (nullSet (dirtyBaseKeys deltaValue) && nullSet (dirtyResultKeys deltaValue))

projectionNormalizeCanonicalCarrier ::
  ( Eq observed,
    Show observed,
    OrdSet observed,
    Eq (ContextProjectionDelta observed),
    Show (ContextProjectionDelta observed)
  ) =>
  ContextProjectionDelta observed ->
  Property
projectionNormalizeCanonicalCarrier deltaValue =
  ( normalizeContextProjectionDelta deltaValue,
    normalizeDelta deltaValue,
    deltaSupport (normalizeDelta deltaValue),
    deltaNull (normalizeDelta deltaValue)
  )
    === ( deltaValue,
          deltaValue,
          deltaSupport deltaValue,
          deltaNull deltaValue
        )