packages feed

seihou-core-0.4.0.0: test/Seihou/Integration/ModuleLoadSpec.hs

module Seihou.Integration.ModuleLoadSpec (tests) where

import Data.Map.Strict qualified as Map
import Effectful
-- Re-use the real loader for end-to-end tests
import Seihou.Core.Module (loadModule)
import Seihou.Core.Types
import Seihou.Effect.DhallEval (evalModuleFile)
import Seihou.Effect.DhallEvalInterp (runDhallEvalPure)
import System.Directory (getCurrentDirectory)
import System.FilePath ((</>))
import Test.Hspec
import Test.Tasty
import Test.Tasty.Hspec (testSpec)

tests :: IO TestTree
tests = testSpec "Seihou.Integration.ModuleLoad" spec

fixtureDir :: IO FilePath
fixtureDir = do
  cwd <- getCurrentDirectory
  pure (cwd </> "test" </> "fixtures")

spec :: Spec
spec = do
  describe "haskell-base end-to-end" $ do
    it "loads with correct name, vars, steps, and prompt" $ do
      fixtures <- fixtureDir
      result <- loadModule [fixtures] "haskell-base"
      case result of
        Left err -> expectationFailure ("Expected Right, got: " <> show err)
        Right m -> do
          m.name `shouldBe` "haskell-base"
          m.description `shouldBe` Just "A Haskell project template"
          length (m.vars) `shouldBe` 3
          length (m.steps) `shouldBe` 5
          length (m.prompts) `shouldBe` 1
          m.dependencies `shouldBe` []

    it "has correct variable declarations" $ do
      fixtures <- fixtureDir
      result <- loadModule [fixtures] "haskell-base"
      case result of
        Left err -> expectationFailure ("Expected Right, got: " <> show err)
        Right m -> do
          let vars = m.vars
          let names = map ((.unVarName) . (.name)) vars
          names `shouldBe` ["project.name", "project.version", "license"]

          let (projectName : projectVersion : license : _) = vars
          projectName.required `shouldBe` True
          projectName.default_ `shouldBe` Nothing

          projectVersion.default_ `shouldBe` Just (VText "0.1.0.0")

          license.default_ `shouldBe` Just (VText "MIT")

    it "has a when expression on the LICENSE step" $ do
      fixtures <- fixtureDir
      result <- loadModule [fixtures] "haskell-base"
      case result of
        Left err -> expectationFailure ("Expected Right, got: " <> show err)
        Right m -> do
          let steps = m.steps
          let licenseStep = steps !! 2
          licenseStep.strategy `shouldBe` Copy
          licenseStep.condition `shouldBe` Just (ExprIsSet "license")

    it "has a dest with placeholder variable" $ do
      fixtures <- fixtureDir
      result <- loadModule [fixtures] "haskell-base"
      case result of
        Left err -> expectationFailure ("Expected Right, got: " <> show err)
        Right m -> do
          let cabalStep = m.steps !! 3
          cabalStep.dest `shouldBe` "{{project.name}}.cabal"

  describe "invalid-module" $ do
    it "produces ValidationError with multiple violations" $ do
      fixtures <- fixtureDir
      result <- loadModule [fixtures] "invalid-module"
      case result of
        Left (ValidationError _ errs) -> do
          length errs `shouldSatisfy` (>= 4)
        Left other -> expectationFailure ("Expected ValidationError, got: " <> show other)
        Right _ -> expectationFailure "Expected validation failure"

  describe "nonexistent module" $ do
    it "produces ModuleNotFound" $ do
      result <- loadModule ["/nonexistent"] "no-such-module"
      case result of
        Left (ModuleNotFound name _) ->
          name.unModuleName `shouldBe` "no-such-module"
        Left other -> expectationFailure ("Expected ModuleNotFound, got: " <> show other)
        Right _ -> expectationFailure "Expected Left"

  describe "pure DhallEval interpreter" $ do
    it "returns modules from the in-memory map" $ do
      let testModule =
            Module
              { name = "test",
                version = Nothing,
                description = Nothing,
                vars = [],
                exports = [],
                prompts = [],
                steps = [],
                commands = [],
                dependencies = [],
                removal = Nothing,
                migrations = []
              }
          modules = Map.fromList [("test/module.dhall", testModule)]
      result <- runEff $ runDhallEvalPure modules $ do
        evalModuleFile "test/module.dhall"
      case result of
        Left err -> expectationFailure ("Expected Right, got: " <> show err)
        Right m -> m.name `shouldBe` "test"