packages feed

keiro-dsl-0.9.0.0: test/conformance-contract/Main.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}

-- | Conformance driver for language-4 prefix-indexed integration contracts.
-- Compiling this component proves distinct contract fields carry distinct
-- @KindID@ types; running it proves canonical JSON remains text and every frozen
-- admission failure is attributed to the owning field path.
module Main (main) where

import Control.Monad (forM_, unless)
import Data.Aeson (Value, object, (.=))
import Data.KindID (KindID)
import Data.Text (Text)
import qualified Data.Text as T
import Generated.HospitalCapacity.Emergency.Contract
import Keiro.Codec.IdDomain (parseKindIdV7Text)
import System.Exit (exitFailure)

canonicalSuffix :: Text
canonicalSuffix = "01h455vb4pex5vsknk084sn02q"

incidentIdValue :: KindID "inc"
incidentIdValue = either (error . show) id (parseKindIdV7Text @"inc" ("inc_" <> canonicalSuffix))

reservationIdValue :: KindID "rsv"
reservationIdValue = either (error . show) id (parseKindIdV7Text @"rsv" ("rsv_" <> canonicalSuffix))

hospitalIdValue :: KindID "hsp"
hospitalIdValue = either (error . show) id (parseKindIdV7Text @"hsp" ("hsp_" <> canonicalSuffix))

incidentPayload :: EmergencyPayload
incidentPayload =
  IncidentTransferNeedDeclared
    (IncidentTransferNeedDeclaredData incidentIdValue "tri-1" "north" 3)

reservationPayload :: EmergencyPayload
reservationPayload =
  TransferReservationAccepted
    (TransferReservationAcceptedData incidentIdValue reservationIdValue hospitalIdValue "2026-01-01")

incidentJson :: Text -> Value
incidentJson rawIncidentId =
  object
    [ "messageType" .= ("IncidentTransferNeedDeclared" :: Text),
      "incidentId" .= rawIncidentId,
      "triageRecordId" .= ("tri-1" :: Text),
      "region" .= ("north" :: Text),
      "redCount" .= (3 :: Int)
    ]

main :: IO ()
main = do
  let validIncidentJson = incidentJson ("inc_" <> canonicalSuffix)
      validReservationJson =
        object
          [ "messageType" .= ("TransferReservationAccepted" :: Text),
            "incidentId" .= ("inc_" <> canonicalSuffix),
            "reservationId" .= ("rsv_" <> canonicalSuffix),
            "hospitalId" .= ("hsp_" <> canonicalSuffix),
            "expirationDeadline" .= ("2026-01-01" :: Text)
          ]
      checks =
        [ ( "topic constants exported",
            incidentEventsTopic == "emergency.incident.events"
              && hospitalEventsTopic == "emergency.hospital.events"
          ),
          ( "IncidentTransferNeedDeclared round-trip",
            encodeEmergencyPayload incidentPayload == validIncidentJson
              && parseEmergencyPayload validIncidentJson == Right incidentPayload
          ),
          ( "TransferReservationAccepted round-trip",
            encodeEmergencyPayload reservationPayload == validReservationJson
              && parseEmergencyPayload validReservationJson == Right reservationPayload
          ),
          rejection "malformed $.incidentId" "inc-1" "malformed TypeID text",
          rejection "wrong-prefix $.incidentId" ("rsv_" <> canonicalSuffix) "prefix mismatch",
          rejection "non-canonical $.incidentId" "inc_01H455VB4PEX5VSKNK084SN02Q" "not canonical lowercase",
          rejection "non-v7 $.incidentId" "inc_00041061050r3gg28a1c60t3gf" "not UUIDv7"
        ]
  forM_ checks $ \(label, ok) -> putStrLn ((if ok then "PASS  " else "FAIL  ") <> label)
  let failed = [label | (label, ok) <- checks, not ok]
  unless (null failed) $ putStrLn ("typed contract: failed " <> show failed) >> exitFailure
  where
    rejection label raw expected =
      ( label,
        case parseEmergencyPayload (incidentJson raw) of
          Left problem -> "$.incidentId" `T.isInfixOf` problem && expected `T.isInfixOf` problem
          Right _ -> False
      )