keiki-0.1.0.0: test/Keiki/Render/InspectorSpec.hs
-- | Golden tests for "Keiki.Render.Inspector": the Markdown edge-detail
-- renderer. Pins the exact document produced for the real multi-edge
-- fixture 'Keiki.Fixtures.UserRegistration.userReg' — the same
-- transducer the Mermaid golden renders — so a reviewer can diff the
-- inspector document against the diagram and see they describe the same
-- edges.
module Keiki.Render.InspectorSpec (spec) where
import Data.Text (Text)
import Data.Text qualified as T
import Keiki.Fixtures.UserRegistration (userReg)
import Keiki.Render.Inspector
( EdgeInspectorOptions (..),
defaultEdgeInspectorOptions,
renderEdgeInspector,
)
import Test.Hspec
spec :: Spec
spec = do
describe "renderEdgeInspector (default options)" $
it "renders userReg to the canonical Markdown inspector block" $
renderEdgeInspector defaultEdgeInspectorOptions userReg
`shouldBe` userRegInspectorCanonical
describe "renderEdgeInspector (includePrettyGuard)" $
it "shows both the structural and the domain-readable guard" $
renderEdgeInspector
(defaultEdgeInspectorOptions {includePrettyGuard = True})
userReg
`shouldBe` userRegInspectorPrettyGuardCanonical
describe "renderEdgeInspector (includeOutputFields)" $
it "lists each output field's term positionally" $
renderEdgeInspector
(defaultEdgeInspectorOptions {includeOutputFields = True})
userReg
`shouldBe` userRegInspectorOutputFieldsCanonical
-- | The default inspector document for @userReg@: edge index, structural
-- guard, and written slots on; pretty guard and output fields off.
-- @Deleted@ has no outgoing edges, so it produces no section; the
-- @FulfillGDPRRequest@ edge from @RequiresConfirmation@ emits no event,
-- so its output renders the literal ε (U+03B5). Edge indices are the
-- 0-based @edgesOut@ positions (note the self-loop at index 1 and the
-- delete edge at index 2).
userRegInspectorCanonical :: Text
userRegInspectorCanonical =
T.intercalate
(T.pack "\n")
[ "# Edge inspector",
"",
"### PotentialCustomer",
"",
"- **PotentialCustomer -> RequiresConfirmation**",
" - edge index: 0",
" - input: StartRegistration",
" - output: RegistrationStarted; ConfirmationEmailSent",
" - guard (structural): PInCtor",
" - written slots: registeredAt; confirmCode; email",
"",
"### RequiresConfirmation",
"",
"- **RequiresConfirmation -> Confirmed**",
" - edge index: 0",
" - input: ConfirmAccount",
" - output: AccountConfirmed",
" - guard (structural): PAnd PInCtor PEq",
" - written slots: confirmedAt",
"- **RequiresConfirmation -> RequiresConfirmation**",
" - edge index: 1",
" - input: ResendConfirmation",
" - output: ConfirmationResent",
" - guard (structural): PInCtor",
" - written slots: registeredAt; confirmCode",
"- **RequiresConfirmation -> Deleted**",
" - edge index: 2",
" - input: FulfillGDPRRequest",
" - output: \x03B5",
" - guard (structural): PInCtor",
" - written slots: deletedAt",
"",
"### Confirmed",
"",
"- **Confirmed -> Deleted**",
" - edge index: 0",
" - input: FulfillGDPRRequest",
" - output: AccountDeleted",
" - guard (structural): PInCtor",
" - written slots: deletedAt"
]
-- | With @includePrettyGuard = True@ each edge shows BOTH the structural
-- guard and the domain-readable guard (from
-- 'Keiki.Render.Pretty.prettyPred'). The contrast is the payoff: the
-- @ConfirmAccount@ edge reads @PAnd PInCtor PEq@ structurally and
-- @(ConfirmAccount && ConfirmAccount.confirmCode == confirmCode)@ pretty.
userRegInspectorPrettyGuardCanonical :: Text
userRegInspectorPrettyGuardCanonical =
T.intercalate
(T.pack "\n")
[ "# Edge inspector",
"",
"### PotentialCustomer",
"",
"- **PotentialCustomer -> RequiresConfirmation**",
" - edge index: 0",
" - input: StartRegistration",
" - output: RegistrationStarted; ConfirmationEmailSent",
" - guard (structural): PInCtor",
" - guard (pretty): StartRegistration",
" - written slots: registeredAt; confirmCode; email",
"",
"### RequiresConfirmation",
"",
"- **RequiresConfirmation -> Confirmed**",
" - edge index: 0",
" - input: ConfirmAccount",
" - output: AccountConfirmed",
" - guard (structural): PAnd PInCtor PEq",
" - guard (pretty): (ConfirmAccount && ConfirmAccount.confirmCode == confirmCode)",
" - written slots: confirmedAt",
"- **RequiresConfirmation -> RequiresConfirmation**",
" - edge index: 1",
" - input: ResendConfirmation",
" - output: ConfirmationResent",
" - guard (structural): PInCtor",
" - guard (pretty): ResendConfirmation",
" - written slots: registeredAt; confirmCode",
"- **RequiresConfirmation -> Deleted**",
" - edge index: 2",
" - input: FulfillGDPRRequest",
" - output: \x03B5",
" - guard (structural): PInCtor",
" - guard (pretty): FulfillGDPRRequest",
" - written slots: deletedAt",
"",
"### Confirmed",
"",
"- **Confirmed -> Deleted**",
" - edge index: 0",
" - input: FulfillGDPRRequest",
" - output: AccountDeleted",
" - guard (structural): PInCtor",
" - guard (pretty): FulfillGDPRRequest",
" - written slots: deletedAt"
]
-- | With @includeOutputFields = True@ each edge that emits at least one
-- output field gains an @output fields@ bullet listing each field's term
-- positionally (via 'Keiki.Render.Pretty.prettyTerm'), grouped by output
-- constructor. Fields are labelled by position only — 'WireCtor' carries
-- no field names. The ε-edge (no output) gets no such bullet. Note the
-- terms mix input-field reads (@StartRegistration.email@) and register
-- reads (@email@).
userRegInspectorOutputFieldsCanonical :: Text
userRegInspectorOutputFieldsCanonical =
T.intercalate
(T.pack "\n")
[ "# Edge inspector",
"",
"### PotentialCustomer",
"",
"- **PotentialCustomer -> RequiresConfirmation**",
" - edge index: 0",
" - input: StartRegistration",
" - output: RegistrationStarted; ConfirmationEmailSent",
" - output fields: RegistrationStarted[field 0: StartRegistration.email; field 1: StartRegistration.confirmCode; field 2: StartRegistration.at]; ConfirmationEmailSent[field 0: StartRegistration.email]",
" - guard (structural): PInCtor",
" - written slots: registeredAt; confirmCode; email",
"",
"### RequiresConfirmation",
"",
"- **RequiresConfirmation -> Confirmed**",
" - edge index: 0",
" - input: ConfirmAccount",
" - output: AccountConfirmed",
" - output fields: AccountConfirmed[field 0: email; field 1: ConfirmAccount.confirmCode; field 2: ConfirmAccount.at]",
" - guard (structural): PAnd PInCtor PEq",
" - written slots: confirmedAt",
"- **RequiresConfirmation -> RequiresConfirmation**",
" - edge index: 1",
" - input: ResendConfirmation",
" - output: ConfirmationResent",
" - output fields: ConfirmationResent[field 0: email; field 1: ResendConfirmation.code; field 2: ResendConfirmation.at]",
" - guard (structural): PInCtor",
" - written slots: registeredAt; confirmCode",
"- **RequiresConfirmation -> Deleted**",
" - edge index: 2",
" - input: FulfillGDPRRequest",
" - output: \x03B5",
" - guard (structural): PInCtor",
" - written slots: deletedAt",
"",
"### Confirmed",
"",
"- **Confirmed -> Deleted**",
" - edge index: 0",
" - input: FulfillGDPRRequest",
" - output: AccountDeleted",
" - output fields: AccountDeleted[field 0: email; field 1: FulfillGDPRRequest.at]",
" - guard (structural): PInCtor",
" - written slots: deletedAt"
]