packages feed

stan-0.0.0.0: test/Test/Stan/Config.hs

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