seihou-core-0.16.0.0: test/Seihou/Core/PathSpec.hs
module Seihou.Core.PathSpec (tests) where
import Control.Monad (forM_)
import Data.Maybe (isJust)
import Data.Text qualified as T
import Seihou.Core.Path
import Seihou.Prelude
import Test.Hspec
import Test.Tasty
import Test.Tasty.Hspec (testSpec)
isControl :: ProjectPathClass -> Bool
isControl (ControlPath _) = True
isControl _ = False
tests :: IO TestTree
tests = testSpec "project path classification" $ do
describe "classifyProjectPath" $ do
it "treats paths outside .seihou and .git as ordinary" $
forM_ ["README.md", "docs/.seihou-notes", "src/.seihou/x", "seihou/manifest.json"] $ \path ->
classifyProjectPath path `shouldBe` OrdinaryProjectPath
it "treats everything under .git as Git control data" $
forM_ [".git", ".git/config", ".GIT/HEAD", "./.git/index"] $ \path ->
classifyProjectPath path `shouldBe` ControlPath "path targets Git control data"
it "treats Seihou's reserved entries as control data, in any case and either separator"
$ forM_
[ ".seihou/manifest.json",
".seihou/manifest.json.tmp",
".seihou/Manifest.JSON",
".SEIHOU/manifest.json",
".seihou/baselines/abc",
".seihou/transactions/x/journal.json",
".seihou/transactions-quarantine/x",
".seihou/modules/demo/module.dhall",
".seihou/context",
".seihou/config.dhall",
".seihou/trace.jsonl",
".seihou/.migrate-signal",
".seihou\\baselines\\abc",
".seihou//baselines/abc",
".seihou/./manifest.json"
]
$ \path -> classifyProjectPath path `shouldSatisfy` isControl
it "names the reserved entry as the path spells it" $
classifyProjectPath ".SEIHOU/Manifest.JSON"
`shouldBe` ControlPath "path targets Seihou's own state at .SEIHOU/Manifest.JSON"
it "reports other .seihou entries as module state areas" $ do
classifyProjectPath ".seihou/docs-mori-bootstrap/bootstrap-docs.py"
`shouldBe` ModuleStatePath "docs-mori-bootstrap" True
classifyProjectPath ".seihou/gcp-kube-env.conf" `shouldBe` ModuleStatePath "gcp-kube-env.conf" False
classifyProjectPath ".seihou/demo/" `shouldBe` ModuleStatePath "demo" False
it "recognises the .seihou directory itself" $
forM_ [".seihou", ".SEIHOU", ".seihou/", "./.seihou"] $ \path ->
classifyProjectPath path `shouldBe` SeihouRootDirectory
it "keeps the reserved list to names that are not valid module names, except legacy entries" $
-- Legacy entries predate the rule; new entries must not look like module names.
filter (T.all (\c -> (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-')) reservedSeihouEntries
`shouldBe` ["baselines", "transactions", "transactions-quarantine", "modules", "context"]
describe "control predicates" $ do
it "refuses the .seihou directory as a file but allows it as a directory" $ do
isControlFileTarget ".seihou" `shouldBe` True
isControlDirectory ".seihou" `shouldBe` False
it "refuses control data as both files and directories" $
forM_ [".git", ".seihou/baselines", ".seihou/manifest.json"] $ \path -> do
isControlFileTarget path `shouldBe` True
isControlDirectory path `shouldBe` True
it "allows ordinary and module-state paths" $
forM_ ["src/x", ".seihou/demo", ".seihou/demo/helper.txt"] $ \path -> do
isControlFileTarget path `shouldBe` False
isControlDirectory path `shouldBe` False
controlPathReason FileTarget path `shouldBe` Nothing
describe "ownStateDirectoryViolation" $ do
let violation = ownStateDirectoryViolation "demo"
it "accepts ordinary paths and files inside the module's own directory" $
forM_ [".seihou/demo/a/b.txt", ".seihou/demo/helper.txt", "src/x"] $ \path ->
violation FileTarget path `shouldBe` Nothing
it "refuses other .seihou file targets, naming the allowed directory" $ do
forM_ [".seihou/demo", ".seihou", ".seihou/other/x", ".Seihou/demo/x", ".seihou/Demo/x", ".seihou/demo.conf"] $ \path ->
violation FileTarget path `shouldSatisfy` isJust
violation FileTarget ".seihou/other/x"
`shouldBe` Just ".seihou/other/x is outside this module's state directory; a module may write under .seihou/ only inside .seihou/demo/"
it "refuses Seihou's own state with the control reason" $
violation FileTarget ".seihou/manifest.json"
`shouldBe` Just "path targets Seihou's own state at .seihou/manifest.json"
it "gives a module named after a reserved entry no state directory" $
ownStateDirectoryViolation "modules" FileTarget ".seihou/modules/x"
`shouldBe` Just "path targets Seihou's own state at .seihou/modules"
it "accepts the .seihou directory and the module's own directory as directory operands" $ do
violation DirectoryOperand ".seihou" `shouldBe` Nothing
violation DirectoryOperand ".seihou/demo" `shouldBe` Nothing
violation DirectoryOperand ".seihou/demo/sub" `shouldBe` Nothing
it "refuses other directories under .seihou" $ do
violation DirectoryOperand ".seihou/other" `shouldSatisfy` isJust
violation DirectoryOperand ".seihou/baselines" `shouldSatisfy` isJust