packages feed

life-sync-1.1.1.0: test/Test/Configuration.hs

{-# LANGUAGE QuasiQuotes #-}

module Test.Configuration
    ( configurationSpec
    ) where

import Data.Foldable (foldr1)
import Hedgehog (Gen, PropertyT, forAll, tripping)
import Path (reldir, relfile)
import Path.Internal (Path (Path))
import Relude.Extra.Lens ((.~))
import System.FilePath (pathSeparator, (</>))
import Test.Hspec (Spec, describe, it, shouldBe)
import Test.Hspec.Hedgehog (hedgehog)

import Life.Configuration (LifeConfiguration (..), corpseConfiguationCodec, directoriesL, filesL,
                           renderLifeConfiguration, resurrect)
import Life.Core (master)

import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Hedgehog.Gen as Gen
import qualified Hedgehog.Range as Range
import qualified Toml


configurationSpec :: Spec
configurationSpec = describe "Configuration tests" $ do
    configurationRenderSpec
    configurationPropertySpec

configurationRenderSpec :: Spec
configurationRenderSpec = describe "Pretty rendering" $ do
    it "empty configuration" $
        render mempty `shouldBe` emptyTxt
    it "non-empty configuration" $
        render nonEmptyCfg `shouldBe` nonEmptyTxt
  where
    render :: LifeConfiguration -> Text
    render = renderLifeConfiguration True

    emptyTxt :: Text
    emptyTxt = T.intercalate "\n"
        [ "directories = []"
        , "files = []"
        ]

    nonEmptyCfg :: LifeConfiguration
    nonEmptyCfg = mempty
        & directoriesL .~ one [reldir|foo/|]
        & filesL .~ Set.fromList
            [ [relfile|bar.txt|]
            , [relfile|quux|]
            ]

    nonEmptyTxt :: Text
    nonEmptyTxt = T.intercalate "\n"
        [ "directories = [\"foo/\"]"
        , "files = "
        , "    [ \"bar.txt\""
        , "    , \"quux\""
        , "    ]"
        ]


configurationPropertySpec :: Spec
configurationPropertySpec = describe "Property Tests" $
    it "parseLifeConfiguration . renderLifeConfiguration cfg ≡ Just cfg"
        parseRenderSpec

parseRenderSpec :: PropertyT IO ()
parseRenderSpec = hedgehog $ do
    cfg <- forAll genLifeConfiguration
    tripping cfg
        (renderLifeConfiguration True)
        (\t -> rightToMaybe (Toml.decode corpseConfiguationCodec t) >>= resurrect)

genLifeConfiguration :: Gen LifeConfiguration
genLifeConfiguration = do
    lifeConfigurationFiles       <- genPathSet genFilePath
    lifeConfigurationDirectories <- genPathSet genDirPath
    let lifeConfigurationBranch = Last $ Just master
    pure LifeConfiguration{..}

-- it's safe to use 'Path' constructor here even if such things are not recommended by API
-- our generators should be safe; and if not - this will be caught by test later
genPathSet :: Gen FilePath -> Gen (Set (Path b t))
genPathSet gen = Set.fromList . fmap Path <$> Gen.list (Range.constant 0 30) gen

genDirPath :: Gen FilePath
genDirPath = (++ [pathSeparator]) <$> genFilePath

genFilePath :: Gen FilePath
genFilePath = foldr1 (</>) <$> Gen.nonEmpty (Range.constant 1 10) genFilePathPiece

genFilePathPiece :: Gen String
genFilePathPiece = Gen.string (Range.constant 1 10) Gen.alphaNum