keiki-0.9.0.0: test/Keiki/WireSchemaSpec.hs
{-# LANGUAGE DuplicateRecordFields #-}
{-# OPTIONS_GHC -Wno-deprecations #-}
module Keiki.WireSchemaSpec (spec) where
import Control.Exception (evaluate)
import GHC.Generics (Generic)
import Keiki.Composition (leftWireCtor, rightWireCtor)
import Keiki.Core
import Keiki.Generics (FieldsOf, RegFieldsOf, mkInCtorVia, mkWireCtor, mkWireCtor0Via, mkWireCtorVia)
import Test.Hspec
data FirstPayload = FirstPayload
{ repeated :: Int,
trailing :: Bool
}
deriving stock (Eq, Show, Generic)
data SecondPayload = SecondPayload
{ repeated :: Int
}
deriving stock (Eq, Show, Generic)
data SchemaEvent
= First FirstPayload
| Second SecondPayload
| Empty
deriving stock (Eq, Show, Generic)
wireFirst :: WireCtor SchemaEvent (FieldsOf FirstPayload)
wireFirst = mkWireCtorVia @"First"
wireFirstAgain :: WireCtor SchemaEvent (FieldsOf FirstPayload)
wireFirstAgain = mkWireCtorVia @"First"
wireSecond :: WireCtor SchemaEvent (FieldsOf SecondPayload)
wireSecond = mkWireCtorVia @"Second"
wireEmpty :: WireCtor SchemaEvent ()
wireEmpty = mkWireCtor0Via @"Empty"
inputFirst :: InCtor SchemaEvent (RegFieldsOf FirstPayload)
inputFirst = mkInCtorVia @"First"
manualFirst :: WireCtor SchemaEvent (FieldsOf FirstPayload)
manualFirst =
unavailableWireCtor
"First"
( \case
First payload -> Just (payload.repeated, (payload.trailing, ()))
_ -> Nothing
)
(\(value, (flag, ())) -> First (FirstPayload value flag))
closureFirst :: WireCtor SchemaEvent (FieldsOf FirstPayload)
closureFirst =
mkWireCtor
"First"
(\case First payload -> Just payload; _ -> Nothing)
First
spec :: Spec
spec = do
describe "trusted Generic wire schemas" $ do
it "covers nullary, one-field, and multi-field constructors" $ do
map
someWireAvailability
[ SomeWire wireEmpty,
SomeWire wireSecond,
SomeWire wireFirst
]
`shouldBe` replicate 3 WireSchemaTrusted
it "aligns independently derived bindings for the same constructor" $
classifyWireHeads wireFirst wireFirstAgain
`shouldBe` WireHeadsStructurallyEqual
it "uses ordered field types rather than selector labels" $ do
let first = renameWireCtor "Repeated" wireFirst
second = renameWireCtor "Repeated" wireSecond
classifyWireHeads first second
`shouldBe` WireHeadsStructurallyDifferent
it "preserves trusted evidence and behavior when renamed" $ do
let renamed = renameWireCtor "RenamedFirst" wireFirst
renamed.wcName `shouldBe` "RenamedFirst"
wireSchemaAvailability renamed.wcSchema `shouldBe` WireSchemaTrusted
renamed.wcMatch (First (FirstPayload 7 True)) `shouldBe` Just (7, (True, ()))
renamed.wcBuild (7, (True, ())) `shouldBe` First (FirstPayload 7 True)
it "keeps match/build round trips unchanged" $ do
wcMatch wireFirst (First (FirstPayload 7 True))
`shouldBe` Just (7, (True, ()))
wcBuild wireSecond (9, ())
`shouldBe` Second (SecondPayload 9)
wcMatch wireEmpty Empty `shouldBe` Just ()
describe "unavailable schemas" $ do
it "marks manual constructors and closure-taking helpers unavailable" $ do
wireSchemaAvailability manualFirst.wcSchema
`shouldBe` WireSchemaUnavailable
wireSchemaAvailability closureFirst.wcSchema
`shouldBe` WireSchemaUnavailable
it "uses the legacy name fallback only for unavailable evidence" $ do
wireHeadsMayAliasForDefault manualFirst wireFirst `shouldBe` True
wireHeadsMayAliasForDefault
(renameWireCtor "Other" manualFirst)
wireFirst
`shouldBe` False
describe "checked Either composition" $ do
it "preserves schemas on a repeated arm" $ do
let leftA = leftWireCtor wireFirst :: WireCtor (Either SchemaEvent SchemaEvent) (FieldsOf FirstPayload)
leftB = leftWireCtor wireFirstAgain :: WireCtor (Either SchemaEvent SchemaEvent) (FieldsOf FirstPayload)
wireSchemaAvailability leftA.wcSchema `shouldBe` WireSchemaTrusted
classifyWireHeads leftA leftB `shouldBe` WireHeadsStructurallyEqual
it "prefixes opposite arms into structurally different paths" $ do
let left = leftWireCtor wireFirst :: WireCtor (Either SchemaEvent SchemaEvent) (FieldsOf FirstPayload)
right = rightWireCtor wireFirst :: WireCtor (Either SchemaEvent SchemaEvent) (FieldsOf FirstPayload)
classifyWireHeads left right `shouldBe` WireHeadsStructurallyDifferent
wireHeadsMayAliasForDefault left right `shouldBe` False
it "does not strengthen an unavailable schema" $ do
let lifted = leftWireCtor manualFirst :: WireCtor (Either SchemaEvent Bool) (FieldsOf FirstPayload)
wireSchemaAvailability lifted.wcSchema
`shouldBe` WireSchemaUnavailable
it "keeps proper-prefix trusted paths may-alias" $
wireSchemaPrefixRelationForTesting
`shouldBe` WireHeadsUnwitnessed
describe "classifyInputWireHeads" $ do
it "aligns trusted input and wire evidence for the same constructor" $
classifyInputWireHeads inputFirst wireFirst
`shouldBe` InputWireHeadsAligned
it "separates trusted evidence for different constructors" $
classifyInputWireHeads inputFirst wireSecond
`shouldBe` InputWireHeadsStructurallyDifferent
it "treats unavailable wire evidence as unwitnessed" $
classifyInputWireHeads inputFirst manualFirst
`shouldBe` InputWireHeadsUnwitnessed
it "pins every composition-only spine comparison arm" $
inputWireSpineRelationsForTesting
`shouldBe` [ ("root/root", InputWireHeadsAligned),
("left/left", InputWireHeadsAligned),
("right/right", InputWireHeadsAligned),
("left/right", InputWireHeadsStructurallyDifferent),
("root/left-prefixed", InputWireHeadsUnwitnessed),
("composition-only/trusted", InputWireHeadsUnwitnessed),
("trusted/composition-only", InputWireHeadsUnwitnessed),
("unavailable/composition-only", InputWireHeadsUnwitnessed)
]
describe "trusted construction capability" $
it "bottoms when the capability argument is bottom" $
evaluate
(trustedWireCtorInternal undefined "Forged" wireSchemaUnavailable (const Nothing) (\() -> Empty))
`shouldThrow` anyException
data SomeWire co where
SomeWire :: WireCtor co fields -> SomeWire co
someWireAvailability :: SomeWire co -> WireSchemaAvailability
someWireAvailability (SomeWire wire) = wireSchemaAvailability wire.wcSchema