packages feed

keiki-0.1.0.0: test/Keiki/Render/MarkdownSpec.hs

-- | Tests for "Keiki.Render.Markdown" (EP-65 M2).
--
-- Exercises 'replaceMarkdownDiagramBlock': it rewrites exactly the span
-- between a matched pair of @\<!-- ns: id begin/end --\>@ markers with a
-- normalized fenced block, preserves every byte outside the markers,
-- reports a clear error when a marker is missing or duplicated, and is
-- idempotent.
--
-- See @docs/plans/65-mermaid-diagram-atlas-sections-and-markdown-marker-replacement-helper.md@.
module Keiki.Render.MarkdownSpec (spec) where

import Data.Text (Text)
import Data.Text qualified as T
import Keiki.Render.Markdown
  ( MarkdownDiagramBlock (..),
    MarkdownDiagramError (..),
    replaceMarkdownDiagramBlock,
  )
import Test.Hspec

spec :: Spec
spec = do
  describe "replaceMarkdownDiagramBlock (happy path)" $ do
    it "replaces only the marked span and preserves the markers" $
      replaceMarkdownDiagramBlock newBlock inputDoc
        `shouldBe` Right expectedDoc

    it "preserves the prose above and below byte-for-byte" $
      case replaceMarkdownDiagramBlock newBlock inputDoc of
        Right out -> do
          T.isPrefixOf (T.pack "# Architecture\n\nSome prose above.") out
            `shouldBe` True
          T.isSuffixOf (T.pack "Some prose below.") out `shouldBe` True
        Left err -> expectationFailure ("unexpected error: " <> show err)

    it "is idempotent: replacing the already-normalized block reproduces it" $
      (replaceMarkdownDiagramBlock newBlock expectedDoc)
        `shouldBe` Right expectedDoc

  describe "replaceMarkdownDiagramBlock (errors)" $ do
    it "reports the expected begin marker when it is absent" $
      replaceMarkdownDiagramBlock newBlock (T.pack "no markers here\n")
        `shouldBe` Left (MissingBeginMarker (T.pack "<!-- seihou: incident-command begin -->"))

    it "reports the expected end marker when only the begin is present" $
      replaceMarkdownDiagramBlock newBlock beginOnlyDoc
        `shouldBe` Left (MissingEndMarker (T.pack "<!-- seihou: incident-command end -->"))

    it "fails deterministically when the begin marker is duplicated" $
      replaceMarkdownDiagramBlock newBlock duplicateBeginDoc
        `shouldBe` Left (DuplicateMarker (T.pack "<!-- seihou: incident-command begin -->") 2)

    it "fails deterministically when the end marker is duplicated" $
      replaceMarkdownDiagramBlock newBlock duplicateEndDoc
        `shouldBe` Left (DuplicateMarker (T.pack "<!-- seihou: incident-command end -->") 2)

-- | The block to splice; its content ends in a newline to exercise the
-- trailing-newline stripping that makes the helper idempotent.
newBlock :: MarkdownDiagramBlock
newBlock =
  MarkdownDiagramBlock
    { blockNamespace = T.pack "seihou",
      blockId = T.pack "incident-command",
      blockLanguage = T.pack "mermaid",
      blockContent = T.pack "stateDiagram-v2\n    [*] --> Open\n"
    }

-- | A hand-maintained document with prose around a stale diagram.
inputDoc :: Text
inputDoc =
  T.intercalate
    (T.pack "\n")
    [ "# Architecture",
      "",
      "Some prose above.",
      "",
      "<!-- seihou: incident-command begin -->",
      "```mermaid",
      "OLD DIAGRAM",
      "```",
      "<!-- seihou: incident-command end -->",
      "",
      "Some prose below."
    ]

-- | 'inputDoc' after replacing the marked block with 'newBlock'. Only the
-- span between the markers changed; the markers and surrounding prose are
-- byte-identical, and the trailing newline in 'blockContent' is stripped
-- before the closing fence.
expectedDoc :: Text
expectedDoc =
  T.intercalate
    (T.pack "\n")
    [ "# Architecture",
      "",
      "Some prose above.",
      "",
      "<!-- seihou: incident-command begin -->",
      "```mermaid",
      "stateDiagram-v2",
      "    [*] --> Open",
      "```",
      "<!-- seihou: incident-command end -->",
      "",
      "Some prose below."
    ]

-- | A document with the begin marker but no matching end marker.
beginOnlyDoc :: Text
beginOnlyDoc =
  T.intercalate
    (T.pack "\n")
    [ "<!-- seihou: incident-command begin -->",
      "```mermaid",
      "OLD DIAGRAM",
      "```"
    ]

-- | A document whose begin marker appears twice.
duplicateBeginDoc :: Text
duplicateBeginDoc =
  T.intercalate
    (T.pack "\n")
    [ "<!-- seihou: incident-command begin -->",
      "<!-- seihou: incident-command begin -->",
      "<!-- seihou: incident-command end -->"
    ]

-- | A document whose end marker appears twice.
duplicateEndDoc :: Text
duplicateEndDoc =
  T.intercalate
    (T.pack "\n")
    [ "<!-- seihou: incident-command begin -->",
      "<!-- seihou: incident-command end -->",
      "<!-- seihou: incident-command end -->"
    ]