packages feed

clod-0.2.3: test/Clod/FileSystem/DetectionSpec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}

-- |
-- Module      : Clod.FileSystem.DetectionSpec
-- Description : Tests for file type detection
-- Copyright   : (c) Fuzz Leonard, 2025
-- License     : MIT
-- Maintainer  : fuzz@fuzz.ink
-- Stability   : experimental
--
-- This module contains tests for file type detection functionality 
-- using magic-based file type detection.

module Clod.FileSystem.DetectionSpec (spec) where

import Test.Hspec
import Control.Monad (forM_)
import System.FilePath
import System.IO.Temp (withSystemTempDirectory)
import System.Directory (createDirectoryIfMissing)
-- import qualified System.IO
import qualified Data.ByteString as BS

import Clod.Types (runClodM, fileReadCap, configDir, (&), (.~))
import Clod.FileSystem.Detection (safeIsTextFile, isTextDescription, needsTransformation)
import Clod.TestHelpers (defaultTestConfig)

-- | Our own FileType definition for testing purposes
data FileType = TextFile | BinaryFile
  deriving (Show, Eq)

-- | Test specification for FileSystem.Detection module
spec :: Spec
spec = do
  magicDetectionSpec
  mimeTypeSpec
  transformationSpec

-- | Tests for magic-based file type detection
magicDetectionSpec :: Spec
magicDetectionSpec = describe "Magic-based file type detection" $ do
  it "correctly detects text files using magic" $ do
    withSystemTempDirectory "clod-test" $ \tmpDir -> do
      -- Create a plain text file - this will reliably be detected as text/plain
      let textFile = tmpDir </> "text.txt"
      writeFile textFile "This is plain text content with multiple lines.\nSecond line\nThird line"
      
      -- Create a config and capabilities
      let config = defaultTestConfig tmpDir
          readCap = fileReadCap [tmpDir]
      
      -- Test the text file
      result <- runClodM config $ do
        isText <- safeIsTextFile readCap textFile
        return (if isText then TextFile else BinaryFile)
      
      case result of
        Left err -> expectationFailure $ "Error detecting text file: " ++ show err
        Right fileType -> fileType `shouldBe` TextFile

  it "correctly identifies binary files" $ do
    withSystemTempDirectory "clod-test" $ \tmpDir -> do
      -- Create binary files with appropriate content
      let pngFile = tmpDir </> "image.png"
      let exeFile = tmpDir </> "program.exe"
      
      -- Write some binary data (PNG header and some random bytes)
      BS.writeFile pngFile $ BS.pack [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D]
      
      -- Write EXE header
      BS.writeFile exeFile $ BS.pack [0x4D, 0x5A, 0x90, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00]
      
      -- Create a config and capabilities
      let config = defaultTestConfig tmpDir
          readCap = fileReadCap [tmpDir]
      
      -- Test each file
      forM_ [pngFile, exeFile] $ \file -> do
        result <- runClodM config $ do
          isText <- safeIsTextFile readCap file
          return (if isText then TextFile else BinaryFile)
        
        case result of
          Left err -> expectationFailure $ "Error detecting binary file: " ++ show err
          Right fileType -> fileType `shouldBe` BinaryFile

-- | Create a test environment for testing with embedded patterns
setupTestEnvironment :: FilePath -> IO ()
setupTestEnvironment tmpDir = do
  -- Create the necessary directory structure
  let resourceDir = tmpDir </> "resources"
  createDirectoryIfMissing True resourceDir
  
  -- No need to create a patterns file anymore as we're using
  -- the embedded version directly in the code

-- | Tests for text file detection through description
mimeTypeSpec :: Spec
mimeTypeSpec = describe "File description detection" $ do
  it "identifies text file descriptions correctly" $ do
    -- Prepare a test environment
    withSystemTempDirectory "clod-test" $ \tmpDir -> do
      -- Setup test environment
      setupTestEnvironment tmpDir
      
      -- Set config directory to our test directory
      let config = defaultTestConfig tmpDir & configDir .~ tmpDir
      
      -- Test various text file descriptions
      let textDescriptions = 
            [ "ASCII text"
            , "text/plain"
            , "UTF-8 text"
            , "JSON data" 
            , "XML document"
            , "HTML document"
            , "source code"
            , "script file"
            ]
      
      -- For each description, test in our monad
      forM_ textDescriptions $ \desc -> do
        result <- runClodM config $ isTextDescription desc
        case result of
          Left err -> expectationFailure $ "Error checking description: " ++ show err
          Right isText -> isText `shouldBe` True
  
  it "identifies non-text file descriptions correctly" $ do
    -- Prepare a test environment
    withSystemTempDirectory "clod-test" $ \tmpDir -> do
      -- Setup test environment
      setupTestEnvironment tmpDir
      
      -- Set config directory to our test directory
      let config = defaultTestConfig tmpDir & configDir .~ tmpDir
      
      -- Test various non-text file descriptions
      let nonTextDescriptions = 
            [ "PNG image data"
            , "JPEG image data"
            , "ELF executable"
            , "Zip archive data"
            , "PDF document"
            , "MPEG audio"
            , "binary data"
            ]
      
      -- For each description, test in our monad
      forM_ nonTextDescriptions $ \desc -> do
        result <- runClodM config $ isTextDescription desc
        case result of
          Left err -> expectationFailure $ "Error checking description: " ++ show err
          Right isText -> isText `shouldBe` False

-- | Tests for special file handling
transformationSpec :: Spec
transformationSpec = describe "Special file handling" $ do
  it "identifies files needing special transformation" $ do
    -- Test files that need special handling
    let specialFiles = 
          [ "/path/to/.gitignore"
          , "/path/to/.env"
          , "/path/to/logo.svg"
          ]
    
    forM_ specialFiles $ \file ->
      needsTransformation file `shouldBe` True
  
  it "identifies files not needing special transformation" $ do
    -- Test regular files
    let regularFiles = 
          [ "/path/to/file.txt"
          , "/path/to/image.png"
          , "/path/to/normal.html"
          ]
    
    forM_ regularFiles $ \file ->
      needsTransformation file `shouldBe` False