packages feed

headroom-0.4.2.0: test/Headroom/Command/RunSpec.hs

{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE LambdaCase            #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude     #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE QuasiQuotes           #-}
{-# LANGUAGE RecordWildCards       #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE StrictData            #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE ViewPatterns          #-}

module Headroom.Command.RunSpec
  ( spec
  )
where

import           Headroom.Command.Run
import           Headroom.Configuration.Types        ( CtPostProcessConfigs
                                                     , HeaderSyntax(..)
                                                     , PostProcessConfig(..)
                                                     , PostProcessConfigs(..)
                                                     , UpdateCopyrightConfig(..)
                                                     )
import           Headroom.Data.EnumExtra             ( EnumExtra(..) )
import           Headroom.Data.Has                   ( Has(..) )
import           Headroom.Data.Lens                  ( suffixLenses
                                                     , suffixLensesFor
                                                     )
import           Headroom.Data.Regex                 ( re )
import           Headroom.Data.Text                  ( fromLines )
import           Headroom.FileType.Types             ( FileType(..) )
import           Headroom.IO.FileSystem              ( FileSystem(..) )
import           Headroom.IO.Network                 ( Network(..) )
import           Headroom.Meta                       ( TemplateType )
import           Headroom.Template                   ( Template(..) )
import           Headroom.Template.Mustache          ( Mustache )
import           Headroom.Template.TemplateRef       ( TemplateRef(..) )
import           Headroom.Types                      ( CurrentYear(..) )
import           Headroom.Variables                  ( mkVariables )
import           RIO                          hiding ( assert )
import qualified RIO.Map                            as M
import qualified RIO.NonEmpty                       as NE
import qualified RIO.Text                           as T
import           Test.Hspec
import           Test.Hspec.QuickCheck               ( prop )
import           Test.QuickCheck              hiding ( sample )
import           Test.QuickCheck.Monadic
import           Text.URI.QQ                         ( uri )


data TestEnv = TestEnv
  { envLogFunc            :: LogFunc
  , envCurrentYear        :: CurrentYear
  , envFileSystem         :: FileSystem (RIO TestEnv)
  , envNetwork            :: Network (RIO TestEnv)
  , envPostProcessConfigs :: CtPostProcessConfigs
  }

suffixLenses ''TestEnv
suffixLensesFor ["fsFindFilesByExts", "fsLoadFile"] ''FileSystem
suffixLensesFor ["nDownloadContent"] ''Network

instance HasLogFunc TestEnv where
  logFuncL = envLogFuncL

instance Has CtPostProcessConfigs TestEnv where
  hasLens = envPostProcessConfigsL

instance Has CurrentYear TestEnv where
  hasLens = envCurrentYearL

instance Has (FileSystem (RIO TestEnv)) TestEnv where
  hasLens = envFileSystemL

instance Has (Network (RIO TestEnv)) TestEnv where
  hasLens = envNetworkL


spec :: Spec
spec = do

  describe "loadTemplateRefs" $ do
    it "should load templates from given references" $ do
      let env' =
            env
              & (envFileSystemL . fsFindFilesByExtsL .~ fsFindFilesByExts')
              & (envFileSystemL . fsLoadFileL .~ fsLoadFile')
              & (envNetworkL . nDownloadContentL .~ nDownloadContent')
          fsFindFilesByExts' = \path _ -> case path of
            "test-dir" -> pure ["haskell.mustache", "rust.mustache"]
            _          -> throwString "INVALID"
          fsLoadFile' = \case
            "haskell.mustache" -> pure "haskell local"
            "rust.mustache"    -> pure "rust\nlocal\n"
            _                  -> throwString "INVALID"
          nDownloadContent' = \case
            [uri|http://test.com/haskell.mustache|] -> pure "haskell URI"
            _ -> throwString "INVALID"
          refs =
            [ UriTemplateRef [uri|http://test.com/haskell.mustache|]
            , LocalTemplateRef "test-dir"
            ]
      templates <- runRIO env' $ loadTemplateRefs @Mustache refs
      M.size templates `shouldBe` 2
      M.member Haskell templates `shouldBe` True
      M.member Rust templates `shouldBe` True
      rawTemplate <$> M.lookup Haskell templates `shouldBe` Just "haskell local"
      rawTemplate <$> M.lookup Rust templates `shouldBe` Just "rust\nlocal"


  describe "typeOfTemplate" $ do
    let fileTypes = fmap (T.toLower . enumToText) (allValues @FileType)
        templateExt         = NE.head $ templateExtensions @TemplateType
        pathGen             = elements $ fmap (<> "." <> templateExt) fileTypes
        prop_typeOfTemplate = monadicIO $ do
          path   <- T.unpack <$> pick pathGen
          result <- run (runRIO env $ typeOfTemplate path)
          assert $ isJust result

    prop "should detect type of template from template path" prop_typeOfTemplate


  describe "postProcessHeader'" $ do
    it "should perform expected post-processing on license header" $ do
      let sample = fromLines
            [ "-- Copyright (c) 2018-2019 1st Author"
            , "Copyright (c) 2017 2nd Author"
            ]
          expected = fromLines
            [ "-- Copyright (c) 2018-2019 1st Author"
            , "-- Copyright (c) 2017-2020 2nd Author"
            ]
          vars   = mkVariables [("sndAuthor", "2nd Author")]
          syntax = LineComment [re|^--|] (Just "--")
      runRIO env (postProcessHeader' @Mustache syntax vars sample)
        `shouldReturn` expected


env :: TestEnv
env = TestEnv { .. }
 where
  envLogFunc     = mkLogFunc (\_ _ _ _ -> pure ())
  envCurrentYear = CurrentYear 2020
  envFileSystem  = FileSystem { fsCreateDirectory     = undefined
                              , fsDoesFileExist       = undefined
                              , fsFindFiles           = undefined
                              , fsFindFilesByExts     = undefined
                              , fsFindFilesByTypes    = undefined
                              , fsGetCurrentDirectory = undefined
                              , fsListFiles           = undefined
                              , fsLoadFile            = undefined
                              }
  envNetwork            = Network { nDownloadContent = undefined }
  envPostProcessConfigs = PostProcessConfigs
    { ppcsUpdateCopyright = PostProcessConfig
                              { ppcEnabled = True
                              , ppcConfig  = UpdateCopyrightConfig
                                               { uccSelectedAuthors =
                                                 Just $ "{{ sndAuthor }}" :| []
                                               }
                              }
    }