module Test.Stan.Config ( configSpec ) where import Hedgehog (forAll, (===)) import Test.Hspec (Spec, describe, it, parallel, shouldBe) import Test.Hspec.Hedgehog (hedgehog) import Stan.Config (Check (..), CheckFilter (..), CheckType (..), Config, ConfigP (..), Scope (..), applyChecks, applyChecksFor, applyConfig, mkDefaultChecks) import Stan.Core.Id (Id (..)) import Stan.Inspection (Inspection) import Test.Stan.Gen (Property, genCheck, genFilePath, genMediumList, genSmallList) import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HS configSpec :: Spec configSpec = describe "Stan Configuration Tests" applyChecksSpec applyChecksSpec :: Spec applyChecksSpec = describe "Configuration checks aggregation" $ do applyChecksUnitSpec applyChecksPropertySpec applyConfigSpec applyChecksUnitSpec :: Spec applyChecksUnitSpec = describe "applyCheck: Unit Tests" $ do it "All inspections when empty Check list" $ applyChecks files [] `shouldBe` defMap it "Including all inspections" $ applyChecks files [Check Include CheckAll ScopeAll] `shouldBe` defMap it "All inspections are excluded" $ applyChecks files [Check Exclude CheckAll ScopeAll] `shouldBe` (mempty <$ defMap) it "Excluding single Inspection ID works" $ do let iId = Id "STAN-0001" applyChecks files [Check Exclude (CheckInspection iId) ScopeAll] `shouldBe` (HS.delete iId <$> defMap) it "Excluding single file works" $ applyChecks files [Check Exclude CheckAll (ScopeFile "baz.hs")] `shouldBe` HM.adjust (const mempty) "baz.hs" defMap it "Excluding a directory works" $ applyChecks files [Check Exclude CheckAll (ScopeDirectory "src/")] `shouldBe` HM.adjust (const mempty) "src/foo.hs" ( HM.adjust (const mempty) "src/bar.hs" defMap) it "Excluding inspection in a single file" $ do let iId = Id "STAN-0001" applyChecks files [Check Exclude (CheckInspection iId) (ScopeFile "baz.hs") ] `shouldBe` HM.adjust (HS.delete iId) "baz.hs" defMap applyChecksPropertySpec :: Spec applyChecksPropertySpec = describe "applyCheck: Property Tests" $ parallel $ do it "Idempotence: applyChecks . applyChecks ≡ applyChecks" idempotenceProperty it "CheckType Inversion: include c . exclude c ≡ id" inversionProperty idempotenceProperty :: Property idempotenceProperty = hedgehog $ do paths <- forAll $ genSmallList genFilePath checks <- forAll $ genMediumList genCheck let filesMap = mkDefaultChecks paths applyChecksFor (applyChecksFor filesMap checks) checks === applyChecksFor filesMap checks inversionProperty :: Property inversionProperty = hedgehog $ do paths <- forAll $ genSmallList genFilePath excludeCheck <- forAll $ genCheck <&> \c -> c { checkType = Exclude } let includeCheck = excludeCheck { checkType = Include } let filesMap = mkDefaultChecks paths applyChecksFor (applyChecksFor filesMap [excludeCheck]) [includeCheck] === filesMap applyConfigSpec :: Spec applyConfigSpec = describe "applyConfig tests" $ do it "'applyConfig' with no removed files is the same as 'applyChecks'" $ do let checks = [Check Exclude CheckAll ScopeAll] applyConfig files (emptyConfig{ configChecks = checks }) `shouldBe` applyChecks files checks it "Removes all files" $ applyConfig files removeAllConfig `shouldBe` mempty it "Removes a single file" $ applyConfig files removeFileConfig `shouldBe` HM.delete "baz.hs" defMap it "Removes all files from a directory" $ applyConfig files removeDirectoryConfig `shouldBe` HM.delete "src/foo.hs" (HM.delete "src/bar.hs" defMap) it "Removes 2 files" $ applyConfig files removeTwoFilesConfig `shouldBe` HM.delete "src/foo.hs" (HM.delete "baz.hs" defMap) it "Removes all files when they are specified explicitly" $ applyConfig files removeAllExplicitConfig `shouldBe` mempty where emptyConfig :: Config emptyConfig = ConfigP { configChecks = [] , configRemoved = [] , configIgnored = [] } removeAllConfig :: Config removeAllConfig = emptyConfig { configRemoved = [ScopeAll] } removeFileConfig :: Config removeFileConfig = emptyConfig { configRemoved = [ScopeFile "baz.hs"] } removeDirectoryConfig :: Config removeDirectoryConfig = emptyConfig { configRemoved = [ScopeDirectory "src/"] } removeTwoFilesConfig :: Config removeTwoFilesConfig = emptyConfig { configRemoved = [ScopeFile "baz.hs", ScopeFile "src/foo.hs"] } removeAllExplicitConfig :: Config removeAllExplicitConfig = emptyConfig { configRemoved = [ScopeFile "baz.hs", ScopeDirectory "src"] } files :: [FilePath] files = ["src/foo.hs", "src/bar.hs", "baz.hs"] defMap :: HashMap FilePath (HashSet (Id Inspection)) defMap = mkDefaultChecks files