packages feed

dojang-0.1.0: test/Dojang/Types/FileRouteSpec.hs

{-# LANGUAGE ImportQualifiedPost #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE OverloadedStrings #-}

module Dojang.Types.FileRouteSpec (monikerMap, paths, spec) where

import Data.HashMap.Strict (HashMap, fromList)
import Data.Text (Text)
import System.OsPath (encodeFS)
import Test.Hspec (Spec, describe, specify)
import Test.Hspec.Expectations.Pretty (shouldBe, shouldNotBe, shouldReturn)
import Test.Hspec.Hedgehog (forAll, hedgehog, (===))

import Dojang.MonadFileSystem (FileType (..))
import Dojang.Types.Environment (Environment (Environment), Kernel (Kernel))
import Dojang.Types.EnvironmentPredicate
  ( EnvironmentPredicate (Moniker, OperatingSystem, Or)
  )
import Dojang.Types.EnvironmentPredicate.Evaluate (EvaluationWarning (..))
import Dojang.Types.FilePathExpression (FilePathExpression (Root, Substitution))
import Dojang.Types.FilePathExpression.Expansion (ExpansionWarning (..))
import Dojang.Types.FileRoute
  ( FileRoute (monikerResolver, predicates)
  , RouteWarning (..)
  , dispatch
  , fileRoute
  , fileRoute'
  , routePath
  )
import Dojang.Types.Gen qualified as Gen
import Dojang.Types.MonikerName (MonikerName, parseMonikerName)


moniker :: Text -> MonikerName
moniker name = case parseMonikerName name of
  Right monikerName -> monikerName
  Left _ -> error "Invalid moniker name"


monikerMap :: HashMap MonikerName EnvironmentPredicate
monikerMap =
  fromList
    [ (moniker "linux", OperatingSystem "linux")
    , (moniker "macos", OperatingSystem "macos")
    , (moniker "windows", OperatingSystem "windows")
    ,
      ( moniker "posix"
      , Or [Moniker $ moniker "linux", Moniker $ moniker "macos"]
      )
    ]


paths :: [(MonikerName, Maybe FilePathExpression)]
paths =
  [ (moniker "posix", Just $ Substitution "HOME")
  ,
    ( moniker "windows"
    , Just $ Substitution "USERPROFILE" -- cSpell:disable-line
    )
  ]


spec :: Spec
spec = do
  let route = fileRoute monikerMap paths Directory
  describe "FileRoute" $ do
    specify "monikerResolver" $ do
      route.monikerResolver (moniker "linux")
        `shouldBe` Just (OperatingSystem "linux")
      route.monikerResolver (moniker "non-existent") `shouldBe` Nothing

    describe "Eq" $ do
      specify "properties" $ hedgehog $ do
        x <- forAll Gen.fileRoute
        y <- forAll Gen.fileRoute
        (x == x) === True
        (x == y) === (y == x)
        (x /= y) === (y /= x)

      specify "examples" $ do
        route `shouldBe` route
        route
          `shouldBe` fileRoute
            monikerMap
            [ (moniker "posix", Just $ Substitution "HOME")
            ,
              ( moniker "windows"
              , Just $ Substitution "USERPROFILE" -- cSpell:disable-line
              )
            ]
            Directory
        route
          `shouldNotBe` fileRoute
            monikerMap
            [ (moniker "posix", Just $ Substitution "HOME")
            , (moniker "windows", Just $ Substitution "HOME")
            ]
            Directory
        route
          `shouldNotBe` fileRoute'
            (const Nothing)
            [ (Moniker $ moniker "posix", Just $ Substitution "HOME")
            ,
              ( Moniker $ moniker "windows"
              , Just $ Substitution "USERPROFILE" -- cSpell:disable-line
              )
            ]
            Directory

    specify "predicates" $ do
      route.predicates
        `shouldBe` ( [
                       ( Moniker $ moniker "windows"
                       , Just $ Substitution "USERPROFILE" -- cSpell:disable-line
                       )
                     , (Moniker $ moniker "posix", Just $ Substitution "HOME")
                     ]
                      :: [(EnvironmentPredicate, Maybe FilePathExpression)]
                   )

    specify "Show" $ do
      show route
        `shouldBe` ("FileRoute " ++ show (route.predicates) ++ " Directory")
      showList [route] ""
        `shouldBe` ("[FileRoute " ++ show (route.predicates) ++ " Directory]")

  specify "dispatch" $ do
    dispatch (Environment "linux" "x86_64" $ Kernel "Linux" "5.10.0-8") route
      `shouldBe` ([Just $ Substitution "HOME"], [])
    dispatch (Environment "macos" "aarch64" $ Kernel "Darwin" "23.1.0") route
      `shouldBe` ([Just $ Substitution "HOME"], [])
    dispatch
      ( Environment "windows" "x86_64"
          $ Kernel "Microsoft Windows" "10.0.23585.1001"
      )
      route
      `shouldBe` ([Just $ Substitution "USERPROFILE"], []) -- cSpell:disable-line
    dispatch
      ( Environment "windows" "x86_64"
          $ Kernel "Microsoft Windows" "10.0.23585.1001"
      )
      route
      `shouldBe` ([Just $ Substitution "USERPROFILE"], []) -- cSpell:disable-line
    let withWarnings =
          fileRoute
            monikerMap
            (paths ++ [(moniker "non-existent", Just $ Root Nothing)])
            Directory
    dispatch
      (Environment "linux" "x86_64" $ Kernel "Linux" "5.10.0-8")
      withWarnings
      `shouldBe` ( [Just $ Substitution "HOME"]
                 , [UndefinedMoniker (moniker "non-existent")]
                 )

  specify "routePath" $ do
    routePath
      route
      (Environment "freebsd" "x86_64" $ Kernel "FreeBSD" "13.2-RELEASE-p4")
      (const $ return Nothing)
      `shouldReturn` (Nothing, [])
    routePath
      route
      (Environment "linux" "x86_64" $ Kernel "Linux" "5.10.0-8")
      (const $ return Nothing)
      `shouldReturn` ( Just mempty
                     ,
                       [ FilePathExpressionWarning
                          (UndefinedEnvironmentVariable "HOME")
                       ]
                     )
    home <- encodeFS "/home/hong"
    routePath
      route
      (Environment "linux" "aarch64" $ Kernel "Linux" "5.10.0-8")
      (const $ return $ Just $ home)
      `shouldReturn` (Just home, [])