module XFTPCLI where import Control.Exception (bracket_) import qualified Data.ByteString as LB import Data.List (isInfixOf, isPrefixOf, isSuffixOf) import Simplex.FileTransfer.Client.Main (prepareChunkSizes, xftpClientCLI) import Simplex.FileTransfer.Description (kb, mb) import System.Directory (createDirectoryIfMissing, getFileSize, listDirectory, removeDirectoryRecursive) import System.Environment (withArgs) import System.FilePath (()) import System.IO.Silently (capture_) import Test.Hspec hiding (fit, it) import Util import XFTPClient (testXFTPServerStr, testXFTPServerStr2, withXFTPServer, withXFTPServer2, xftpServerFiles, xftpServerFiles2) xftpCLITests :: Spec xftpCLITests = around_ testBracket . describe "XFTP CLI" $ do it "should send and receive file" testXFTPCLISendReceive it "should send and receive file with 2 servers" testXFTPCLISendReceive2servers it "should delete file from 2 servers" testXFTPCLIDelete it "prepareChunkSizes should use 2 chunk sizes" testPrepareChunkSizes testBracket :: IO () -> IO () testBracket = bracket_ (mapM_ (createDirectoryIfMissing False) testDirs) (mapM_ removeDirectoryRecursive testDirs) where testDirs = [xftpServerFiles, xftpServerFiles2, senderFiles, recipientFiles] senderFiles :: FilePath senderFiles = "tests/tmp/xftp-sender-files" recipientFiles :: FilePath recipientFiles = "tests/tmp/xftp-recipient-files" xftpCLI :: [String] -> IO [String] xftpCLI params = lines <$> capture_ (withArgs params xftpClientCLI) testXFTPCLISendReceive :: IO () testXFTPCLISendReceive = withXFTPServer $ do let filePath = senderFiles "testfile" xftpCLI ["rand", filePath, "17mb"] `shouldReturn` ["File created: " <> filePath] file <- LB.readFile filePath getFileSize filePath `shouldReturn` mb 17 let fdRcv1 = filePath <> ".xftp" "rcv1.xftp" fdRcv2 = filePath <> ".xftp" "rcv2.xftp" fdSnd = filePath <> ".xftp" "snd.xftp.private" progress : sendResult <- xftpCLI ["send", filePath, senderFiles, "-n", "2", "-s", testXFTPServerStr, "--tmp=tests/tmp"] progress `shouldSatisfy` uploadProgress sendResult `shouldBe` [ "Sender file description: " <> fdSnd, "Pass file descriptions to the recipient(s):", fdRcv1, fdRcv2 ] testInfoFile fdRcv1 "Recipient" testReceiveFile fdRcv1 "testfile" file testInfoFile fdRcv2 "Recipient" testReceiveFile fdRcv2 "testfile_1" file testInfoFile fdSnd "Sender" xftpCLI ["recv", fdSnd, recipientFiles, "--tmp=tests/tmp"] `shouldThrow` anyException where testInfoFile fd party = do xftpCLI ["info", fd] `shouldReturn` [party <> " file description", "File download size: 18mb", "File server(s):", testXFTPServerStr <> ": 18mb"] testReceiveFile fd fileName file = do progress : recvResult <- xftpCLI ["recv", fd, recipientFiles, "--tmp=tests/tmp", "-y"] progress `shouldSatisfy` downloadProgress fileName recvResult `shouldBe` ["File description " <> fd <> " is deleted."] LB.readFile (recipientFiles fileName) `shouldReturn` file testXFTPCLISendReceive2servers :: IO () testXFTPCLISendReceive2servers = withXFTPServer . withXFTPServer2 $ do let filePath = senderFiles "testfile" xftpCLI ["rand", filePath, "17mb"] `shouldReturn` ["File created: " <> filePath] file <- LB.readFile filePath getFileSize filePath `shouldReturn` mb 17 let fdRcv1 = filePath <> ".xftp" "rcv1.xftp" fdRcv2 = filePath <> ".xftp" "rcv2.xftp" fdSnd = filePath <> ".xftp" "snd.xftp.private" progress : sendResult <- xftpCLI ["send", filePath, senderFiles, "-n", "2", "-s", testXFTPServerStr <> ";" <> testXFTPServerStr2, "--tmp=tests/tmp"] progress `shouldSatisfy` uploadProgress sendResult `shouldBe` [ "Sender file description: " <> fdSnd, "Pass file descriptions to the recipient(s):", fdRcv1, fdRcv2 ] testReceiveFile fdRcv1 "testfile" file testReceiveFile fdRcv2 "testfile_1" file where testReceiveFile fd fileName file = do partyStr : sizeStr : srvStr : srvs <- xftpCLI ["info", fd] partyStr `shouldContain` "Recipient file description" sizeStr `shouldBe` "File download size: 18mb" srvStr `shouldBe` "File server(s):" case srvs of [srv1] -> any (`isInfixOf` srv1) [testXFTPServerStr, testXFTPServerStr2] `shouldBe` True [srv1, srv2] -> do srv1 `shouldContain` testXFTPServerStr srv2 `shouldContain` testXFTPServerStr2 _ -> print srvs >> error "more than 2 servers returned" progress : recvResult <- xftpCLI ["recv", fd, recipientFiles, "--tmp=tests/tmp", "-y"] progress `shouldSatisfy` downloadProgress fileName recvResult `shouldBe` ["File description " <> fd <> " is deleted."] LB.readFile (recipientFiles fileName) `shouldReturn` file testXFTPCLIDelete :: IO () testXFTPCLIDelete = withXFTPServer . withXFTPServer2 $ do let filePath = senderFiles "testfile" xftpCLI ["rand", filePath, "17mb"] `shouldReturn` ["File created: " <> filePath] file <- LB.readFile filePath getFileSize filePath `shouldReturn` mb 17 let fdRcv1 = filePath <> ".xftp" "rcv1.xftp" fdRcv2 = filePath <> ".xftp" "rcv2.xftp" fdSnd = filePath <> ".xftp" "snd.xftp.private" progress : sendResult <- xftpCLI ["send", filePath, senderFiles, "-n", "2", "-s", testXFTPServerStr <> ";" <> testXFTPServerStr2, "--tmp=tests/tmp"] progress `shouldSatisfy` uploadProgress sendResult `shouldBe` [ "Sender file description: " <> fdSnd, "Pass file descriptions to the recipient(s):", fdRcv1, fdRcv2 ] xftpCLI ["del", fdRcv1] `shouldThrow` anyException progress1 : recvResult <- xftpCLI ["recv", fdRcv1, recipientFiles, "--tmp=tests/tmp", "-y"] progress1 `shouldSatisfy` downloadProgress "testfile" recvResult `shouldBe` ["File description " <> fdRcv1 <> " is deleted."] LB.readFile (recipientFiles "testfile") `shouldReturn` file fs1 <- listDirectory xftpServerFiles fs2 <- listDirectory xftpServerFiles2 length fs1 + length fs2 `shouldBe` 6 xftpCLI ["del", fdSnd, "-y"] `shouldReturn` ["File deleted! \r", "File description " <> fdSnd <> " is deleted."] listDirectory xftpServerFiles >>= (`shouldBe` []) listDirectory xftpServerFiles2 >>= (`shouldBe` []) xftpCLI ["recv", fdRcv2, recipientFiles, "--tmp=tests/tmp"] `shouldThrow` anyException testPrepareChunkSizes :: IO () testPrepareChunkSizes = do prepareChunkSizes (mb 9 + kb 256) `shouldBe` [mb 4, mb 4, mb 1, mb 1] prepareChunkSizes (mb 9) `shouldBe` [mb 4, mb 4, mb 1] prepareChunkSizes (mb 7 + 1) `shouldBe` [mb 4, mb 4] prepareChunkSizes (mb 7) `shouldBe` [mb 4] <> r3 (mb 1) prepareChunkSizes (mb 3 + 1) `shouldBe` [mb 4] prepareChunkSizes (mb 3) `shouldBe` r3 (mb 1) prepareChunkSizes (mb 2 + 3 * kb 256 + 1) `shouldBe` r3 (mb 1) prepareChunkSizes (mb 2 + 3 * kb 256) `shouldBe` [mb 1, mb 1] <> r3 (kb 256) prepareChunkSizes (mb 2 + 1) `shouldBe` [mb 1, mb 1, kb 256] prepareChunkSizes (3 * kb 256 + 1) `shouldBe` [mb 1] prepareChunkSizes (3 * kb 256) `shouldBe` r3 (kb 256) prepareChunkSizes 1 `shouldBe` [kb 64] where r3 = replicate 3 uploadProgress :: String -> Bool uploadProgress s = "Encrypting file..." `isPrefixOf` s && "Uploading file..." `isInfixOf` s && "File uploaded!" `isInfixOf` s downloadProgress :: FilePath -> String -> Bool downloadProgress fileName s = "Downloading file..." `isPrefixOf` s && "Decrypting file..." `isInfixOf` s && ("File downloaded: " <> recipientFiles fileName <> "\r") `isSuffixOf` s