keiki-0.9.0.0: test/Keiki/InputSchemaSpec.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# OPTIONS_GHC -Wno-deprecations #-}
module Keiki.InputSchemaSpec (spec) where
import Control.Exception (evaluate)
import Data.Proxy (Proxy (..))
import GHC.Generics (Generic)
import Keiki.Composition (leftInCtor, rightInCtor)
import Keiki.Core
import Keiki.Generics (RegFieldsOf, mkInCtor, mkInCtorVia)
import Test.Hspec
data OnePayload = OnePayload
{ value :: Int
}
deriving stock (Eq, Show, Generic)
data ManyPayload = ManyPayload
{ value :: Int,
enabled :: Bool
}
deriving stock (Eq, Show, Generic)
data SchemaCommand
= Empty
| One OnePayload
| Many ManyPayload
deriving stock (Eq, Show, Generic)
inEmpty :: InCtor SchemaCommand '[]
inEmpty = mkInCtorVia @"Empty"
inOne :: InCtor SchemaCommand (RegFieldsOf OnePayload)
inOne = mkInCtorVia @"One"
inOneAgain :: InCtor SchemaCommand (RegFieldsOf OnePayload)
inOneAgain = mkInCtorVia @"One"
inMany :: InCtor SchemaCommand (RegFieldsOf ManyPayload)
inMany = mkInCtorVia @"Many"
manualOne :: InCtor SchemaCommand (RegFieldsOf OnePayload)
manualOne =
unavailableInCtor
"One"
( \case
One payload -> Just (RCons (Proxy @"value") payload.value RNil)
_ -> Nothing
)
(\(RCons _ field RNil) -> One (OnePayload field))
closureOne :: InCtor SchemaCommand (RegFieldsOf OnePayload)
closureOne =
mkInCtor
"One"
(\case One payload -> Just payload; _ -> Nothing)
One
spec :: Spec
spec = do
describe "trusted Generic input schemas" $ do
it "covers nullary, one-field, and multi-field constructors" $
map
someInputAvailability
[ SomeInput inEmpty,
SomeInput inOne,
SomeInput inMany
]
`shouldBe` replicate 3 InCtorSchemaTrusted
it "aligns independently derived bindings for the same constructor" $
classifyInputHeads inOne inOneAgain
`shouldBe` InputHeadsStructurallyEqual
it "uses structure rather than diagnostic names" $ do
let one = renameInCtor "Repeated" inOne
many = renameInCtor "Repeated" inMany
classifyInputHeads one many
`shouldBe` InputHeadsStructurallyDifferent
it "preserves trusted evidence and behavior when renamed" $ do
let renamed = renameInCtor "RenamedOne" inOne
renamed.icName `shouldBe` "RenamedOne"
inCtorSchemaAvailability renamed.icSchema `shouldBe` InCtorSchemaTrusted
case renamed.icMatch (One (OnePayload 7)) of
Just fields -> renamed.icBuild fields `shouldBe` One (OnePayload 7)
Nothing -> expectationFailure "renamed trusted input constructor did not match"
it "keeps match/build round trips unchanged" $ do
case icMatch inOne (One (OnePayload 7)) of
Just fields -> fields ! #value `shouldBe` 7
Nothing -> expectationFailure "trusted input constructor did not match"
icBuild
inMany
( RCons
(Proxy @"value")
9
(RCons (Proxy @"enabled") True RNil)
)
`shouldBe` Many (ManyPayload 9 True)
case icMatch inEmpty Empty of
Just RNil -> pure ()
Nothing -> expectationFailure "trusted nullary input constructor did not match"
describe "unavailable input schemas" $
it "marks manual constructors and closure-taking helpers unavailable" $ do
inCtorSchemaAvailability manualOne.icSchema
`shouldBe` InCtorSchemaUnavailable
inCtorSchemaAvailability closureOne.icSchema
`shouldBe` InCtorSchemaUnavailable
describe "checked Either composition" $ do
it "preserves schemas on a repeated arm" $ do
let leftA = leftInCtor inOne :: InCtor (Either SchemaCommand SchemaCommand) (RegFieldsOf OnePayload)
leftB = leftInCtor inOneAgain :: InCtor (Either SchemaCommand SchemaCommand) (RegFieldsOf OnePayload)
inCtorSchemaAvailability leftA.icSchema
`shouldBe` InCtorSchemaTrusted
classifyInputHeads leftA leftB
`shouldBe` InputHeadsStructurallyEqual
it "prefixes opposite arms into structurally different paths" $ do
let left = leftInCtor inOne :: InCtor (Either SchemaCommand SchemaCommand) (RegFieldsOf OnePayload)
right = rightInCtor inOne :: InCtor (Either SchemaCommand SchemaCommand) (RegFieldsOf OnePayload)
classifyInputHeads left right
`shouldBe` InputHeadsStructurallyDifferent
it "does not strengthen unavailable evidence" $ do
let lifted = leftInCtor manualOne :: InCtor (Either SchemaCommand Bool) (RegFieldsOf OnePayload)
inCtorSchemaAvailability lifted.icSchema
`shouldBe` InCtorSchemaUnavailable
it "keeps proper-prefix trusted paths may-alias" $
inCtorSchemaPrefixRelationForTesting
`shouldBe` InputHeadsUnwitnessed
describe "trusted construction capability" $
it "bottoms when the capability argument is bottom" $
evaluate
( trustedInCtorInternal undefined "Forged" inCtorSchemaUnavailable (const Nothing) (\RNil -> Empty) ::
InCtor SchemaCommand '[]
)
`shouldThrow` anyException
data SomeInput ci where
SomeInput :: InCtor ci fields -> SomeInput ci
someInputAvailability :: SomeInput ci -> InCtorSchemaAvailability
someInputAvailability (SomeInput inputCtor) =
inCtorSchemaAvailability inputCtor.icSchema