packages feed

hledger-flow-0.16.3: test/Common/Integration.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}

module Common.Integration (tests) where

import qualified Data.List as List (sort)
import Data.Time.Clock (UTCTime(..), secondsToDiffTime)
import Data.Time.Calendar (fromGregorian)
import Hledger.Flow.Common
import Hledger.Flow.PathHelpers (TurtlePath)
import qualified System.Directory as Dir
import Test.HUnit
import TestHelpersTurtle
import Turtle

testHiddenFiles :: Test
testHiddenFiles =
  TestCase
    ( sh
        ( do
            let tmpbase = "." </> "test" </> "tmp"
            mktree tmpbase
            tmpdir <- using (mktempdir tmpbase "hlflowtest")
            let tmpJournals = map (tmpdir </>) journalFiles :: [TurtlePath]
            let tmpExtras = map (tmpdir </>) extraFiles :: [TurtlePath]
            let tmpHidden = map (tmpdir </>) hiddenFiles :: [TurtlePath]
            let onDisk = List.sort $ tmpJournals ++ tmpExtras ++ tmpHidden
            touchAll onDisk
            filtered <- fmap List.sort $ shellToList $ onlyFiles $ select onDisk
            let expected = List.sort $ tmpExtras ++ tmpJournals
            liftIO $ assertEqual "Hidden files should be excluded" expected filtered
        )
    )

testFilterPaths :: Test
testFilterPaths =
  TestCase
    ( sh
        ( do
            let tmpbase = "." </> "test" </> "tmp"
            mktree tmpbase
            tmpdir <- using (mktempdir tmpbase "hlflowtest")
            let tmpJournals = map (tmpdir </>) journalFiles :: [TurtlePath]
            let tmpExtras = map (tmpdir </>) extraFiles :: [TurtlePath]
            let tmpHidden = map (tmpdir </>) hiddenFiles :: [TurtlePath]
            let onDisk = List.sort $ tmpJournals ++ tmpExtras ++ tmpHidden
            touchAll onDisk

            let nonExistant = map (tmpdir </>) ["where", "is", "my", "mind"]
            let toFilter = nonExistant ++ onDisk
            filtered <- single $ filterPaths testfile toFilter
            let actual = List.sort filtered
            liftIO $ assertEqual "The filtered paths should exclude files not actually on disk" onDisk actual
        )
    )

testFirstExistingFile :: Test
testFirstExistingFile =
  TestCase
    ( sh
        ( do
            let tmpbase = "." </> "test" </> "tmp"
            mktree tmpbase
            tmpdir <- using (mktempdir tmpbase "hlflowtest")
            let missing = tmpdir </> "does-not-exist.txt"
            let existing1 = tmpdir </> "first.txt"
            let existing2 = tmpdir </> "second.txt"
            touchAll [existing1, existing2]

            found1 <- liftIO $ firstExistingFile [missing, existing1, existing2]
            liftIO $ assertEqual "Should return the first existing file in the list" (Just existing1) found1

            found2 <- liftIO $ firstExistingFile [missing]
            liftIO $ assertEqual "Should return Nothing if no files exist" Nothing found2
        )
    )

-- Helper to set mtime on a Turtle path
setMtime :: TurtlePath -> UTCTime -> IO ()
setMtime path time = Dir.setModificationTime path time

-- Fixed timestamps for deterministic tests
olderTime :: UTCTime
olderTime = UTCTime (fromGregorian 2020 1 1) (secondsToDiffTime 0)

newerTime :: UTCTime
newerTime = UTCTime (fromGregorian 2025 1 1) (secondsToDiffTime 0)

testNeedsRegeneration :: Test
testNeedsRegeneration =
  TestCase
    ( sh
        ( do
            let tmpbase = "." </> "test" </> "tmp"
            mktree tmpbase
            tmpdir <- using (mktempdir tmpbase "hlflowtest")
            let source = tmpdir </> "source.txt"
            let target = tmpdir </> "target.txt"

            -- Test case 1: target doesn't exist -> returns True
            touch source
            result1 <- liftIO $ needsRegeneration source target
            liftIO $ assertEqual "Should return True when target doesn't exist" True result1

            -- Test case 2: target exists, source is newer -> returns True
            touch target
            liftIO $ setMtime target olderTime
            liftIO $ setMtime source newerTime
            result2 <- liftIO $ needsRegeneration source target
            liftIO $ assertEqual "Should return True when source is newer than target" True result2

            -- Test case 3: target exists, target is newer -> returns False
            liftIO $ setMtime source olderTime
            liftIO $ setMtime target newerTime
            result3 <- liftIO $ needsRegeneration source target
            liftIO $ assertEqual "Should return False when target is newer than source" False result3

            -- Test case 4: equal mtimes -> returns False (boundary condition)
            liftIO $ setMtime source olderTime
            liftIO $ setMtime target olderTime
            result4 <- liftIO $ needsRegeneration source target
            liftIO $ assertEqual "Should return False when mtimes are equal" False result4
        )
    )

tests :: Test
tests = TestList [testHiddenFiles, testFilterPaths, testFirstExistingFile, testNeedsRegeneration]