content: {-# LANGUAGE OverloadedStrings #-}
  
  module CoreTests.CryptoFileTests (cryptoFileTests) where
  
  import AgentTests.FunctionalAPITests (runRight_)
  import Control.Concurrent.STM
  import Control.Monad.Except
  import Control.Monad.IO.Class
  import Crypto.Random (ChaChaDRG)
  import qualified Data.ByteString.Lazy as LB
  import GHC.IO.IOMode (IOMode (..))
  import qualified Simplex.Messaging.Crypto as C
  import Simplex.Messaging.Crypto.File (CryptoFile (..), FTCryptoError (..))
  import qualified Simplex.Messaging.Crypto.File as CF
  import System.Directory (getFileSize)
  import Test.Hspec hiding (fit, it)
  import Util
  
  cryptoFileTests :: Spec
  cryptoFileTests = do
    it "should write/read file" testWriteReadFile
    it "should put/get file" testPutGetFile
    it "should write/get file" testWriteGetFile
    it "should put/read file" testPutReadFile
    it "should fail reading empty or small file" testSmallFile
  
  testFilePath :: FilePath
  testFilePath = "tests/tmp/testcryptofile"
  
  testWriteReadFile :: IO ()
  testWriteReadFile = do
    g <- C.newRandom
    s <- atomically $ LB.fromStrict <$> C.randomBytes 100000 g
    file <- atomically $ mkCryptoFile g
    runRight_ $ do
      CF.writeFile file s
      liftIO $ CF.getFileContentsSize file `shouldReturn` 100000
      liftIO $ getFileSize testFilePath `shouldReturn` 100000 + fromIntegral C.authTagSize
      s' <- CF.readFile file
      liftIO $ s `shouldBe` s'
  
  testPutGetFile :: IO ()
  testPutGetFile = do
    g <- C.newRandom
    s <- atomically $ LB.fromStrict <$> C.randomBytes 50000 g
    s' <- atomically $ LB.fromStrict <$> C.randomBytes 50000 g
    file <- atomically $ mkCryptoFile g
    runRight_ $ do
      CF.withFile file WriteMode $ \h -> liftIO $ do
        CF.hPut h s
        CF.hPut h s'
        CF.hPutTag h
      liftIO $ CF.getFileContentsSize file `shouldReturn` 100000
      liftIO $ getFileSize testFilePath `shouldReturn` 100000 + fromIntegral C.authTagSize
      CF.withFile file ReadMode $ \h -> do
        s1 <- liftIO $ CF.hGet h 30000
        s2 <- liftIO $ CF.hGet h 40000
        s3 <- liftIO $ CF.hGet h 30000
        CF.hGetTag h
        liftIO $ (s <> s') `shouldBe` LB.fromStrict (s1 <> s2 <> s3)
  
  testWriteGetFile :: IO ()
  testWriteGetFile = do
    g <- C.newRandom
    s <- atomically $ LB.fromStrict <$> C.randomBytes 100000 g
    file <- atomically $ mkCryptoFile g
    runRight_ $ do
      CF.writeFile file s
      CF.withFile file ReadMode $ \h -> do
        s' <- liftIO $ CF.hGet h 50000
        s'' <- liftIO $ CF.hGet h 50000
        CF.hGetTag h
        liftIO $ runExceptT (CF.hGetTag h) `shouldReturn` Left FTCEInvalidAuthTag
        liftIO $ s `shouldBe` LB.fromStrict (s' <> s'')
  
  testPutReadFile :: IO ()
  testPutReadFile = do
    g <- C.newRandom
    s <- atomically $ LB.fromStrict <$> C.randomBytes 50000 g
    s' <- atomically $ LB.fromStrict <$> C.randomBytes 50000 g
    file <- atomically $ mkCryptoFile g
    runRight_ $ do
      CF.withFile file WriteMode $ \h -> liftIO $ do
        CF.hPut h s
        CF.hPut h s'
    runExceptT (CF.readFile file) `shouldReturn` Left FTCEInvalidAuthTag
    runRight_ $ do
      CF.withFile file WriteMode $ \h -> liftIO $ do
        CF.hPut h s
        CF.hPut h s'
        CF.hPutTag h
      s'' <- CF.readFile file
      liftIO $ (s <> s') `shouldBe` s''
  
  testSmallFile :: IO ()
  testSmallFile = do
    g <- C.newRandom
    file <- atomically $ mkCryptoFile g
    LB.writeFile testFilePath ""
    runExceptT (CF.readFile file) `shouldReturn` Left FTCEInvalidFileSize
    LB.writeFile testFilePath "123"
    runExceptT (CF.readFile file) `shouldReturn` Left FTCEInvalidFileSize
  
  mkCryptoFile :: TVar ChaChaDRG -> STM CryptoFile
  mkCryptoFile g = CryptoFile testFilePath . Just <$> CF.randomArgs g
  