atelier-core-0.7.3.0: test/Unit/Atelier/Effects/FileSystemSpec.hs
module Unit.Atelier.Effects.FileSystemSpec (test_FileSystem) where
import Control.Exception (IOException, evaluate, try)
import Effectful (runPureEff)
import Effectful.State.Static.Shared (State, evalState)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, (@?=))
import Data.ByteString.Lazy qualified as LBS
import Data.Map.Strict qualified as M
import Atelier.Effects.FileSystem
test_FileSystem :: TestTree
test_FileSystem =
testGroup
"FileSystem"
[ testGroup "runFileSystemState" testFileSystemState
]
testFileSystemState :: [TestTree]
testFileSystemState =
[ testGroup
"readFileBs"
[ testCase "reads bytes of a file present in the state" do
run (M.singleton "/a" "hello") (readFileBs "/a")
@?= "hello"
, testCase "errors when the file is absent" do
result <- try @IOException $ evaluate (run M.empty (readFileBs "/missing"))
isLeft result @?= True
]
, testGroup
"readFileLbs"
[ testCase "reads the entire file as lazy bytes" do
run (M.singleton "/a" "hello") (readFileLbs "/a")
@?= ("hello" :: LBS.ByteString)
, testCase "errors when the file is absent" do
result <- try @IOException $ evaluate (run M.empty (readFileLbs "/missing"))
isLeft result @?= True
]
, testGroup
"readFileLbsFrom"
[ testCase "returns full content when offset is 0" do
run (M.singleton "/a" "hello") (readFileLbsFrom "/a" 0)
@?= ("hello" :: LBS.ByteString)
, testCase "skips the leading bytes up to the given offset" do
run (M.singleton "/a" "hello") (readFileLbsFrom "/a" 2)
@?= ("llo" :: LBS.ByteString)
, testCase "returns empty when offset equals the file length" do
run (M.singleton "/a" "hello") (readFileLbsFrom "/a" 5)
@?= ("" :: LBS.ByteString)
, testCase "errors when the file is absent" do
result <- try @IOException $ evaluate (run M.empty (readFileLbsFrom "/missing" 0))
isLeft result @?= True
]
, testGroup
"doesFileExist"
[ testCase "returns True for a key present in the state" do
run (M.singleton "/a" "") (doesFileExist "/a")
@?= True
, testCase "returns False when the key is absent" do
run M.empty (doesFileExist "/a")
@?= False
]
, testGroup
"doesPathExist"
[ testCase "returns True when a key with the path as a string prefix exists" do
run (M.singleton "/dir/file" "") (doesPathExist "/dir")
@?= True
, testCase "returns False when no key shares the prefix" do
run M.empty (doesPathExist "/dir")
@?= False
, testCase "returns False for an exact-match key with no children" do
run (M.singleton "/dir" "") (doesPathExist "/dir")
@?= False
]
, testGroup
"listDirectory"
[ testCase "returns all keys that start with the given path" do
let fs = M.fromList [("/dir/a", ""), ("/dir/b", ""), ("/other/c", "")]
sort (run fs (listDirectory "/dir"))
@?= ["/dir/a", "/dir/b"]
, testCase "returns empty list when no keys share the prefix" do
run M.empty (listDirectory "/dir")
@?= []
, testCase "excludes an exact-match key" do
let fs = M.fromList [("/dir", ""), ("/dir/a", "")]
run fs (listDirectory "/dir")
@?= ["/dir/a"]
]
, testGroup
"removeFile"
[ testCase "removes the file from the state" do
let result = run (M.singleton "/a" "x") do
removeFile "/a"
doesFileExist "/a"
result @?= False
, testCase "leaves other files unaffected" do
let result = run (M.fromList [("/a", "x"), ("/b", "y")]) do
removeFile "/a"
doesFileExist "/b"
result @?= True
, testCase "is a no-op when the file is absent" do
let result = run M.empty do
removeFile "/missing"
doesFileExist "/missing"
result @?= False
]
, testGroup
"createDirectoryIfMissing"
[ testCase "is a no-op — does not add any entries to the state" do
let result = run M.empty do
createDirectoryIfMissing True "/new/dir"
doesPathExist "/new/dir"
result @?= False
]
, testGroup
"canonicalizePath"
[ testCase "returns the path unchanged" do
run M.empty (canonicalizePath "/some/./path")
@?= "/some/./path"
]
, testGroup
"getCurrentDirectory"
[ testCase "returns /" do
run M.empty getCurrentDirectory
@?= "/"
]
, testGroup
"getXdgRuntimeDir"
[ testCase "returns /tmp" do
run M.empty getXdgRuntimeDir
@?= "/tmp"
]
]
run
:: Map FilePath ByteString
-> Eff '[FileSystem, State (Map FilePath ByteString)] a
-> a
run fs = runPureEff . evalState fs . runFileSystemState