diff --git a/src/Simplex/FileTransfer/Server.hs b/src/Simplex/FileTransfer/Server.hs index d7a3c70ad..3cd19e7c1 100644 --- a/src/Simplex/FileTransfer/Server.hs +++ b/src/Simplex/FileTransfer/Server.hs @@ -238,7 +238,7 @@ xftpServer cfg@XFTPServerConfig {xftpPort, transportConfig, inactiveClientExpira stopServer :: M s () stopServer = do - st <- asks store + st <- asks fileStore liftIO $ closeFileStore st saveServerStats logNote "Server stopped" @@ -366,13 +366,13 @@ xftpServer cfg@XFTPServerConfig {xftpPort, transportConfig, inactiveClientExpira XFTPServerConfig {controlPortUserAuth = user, controlPortAdminAuth = admin} = cfg CPStatsRTS -> E.tryAny getRTSStats >>= either (hPrint h) (hPrint h) CPDelete fileId -> withUserRole $ unliftIO u $ do - fs <- asks store + fs <- asks fileStore r <- runExceptT $ do (fr, _) <- ExceptT $ liftIO $ getFile fs SFRecipient fileId ExceptT $ deleteServerFile_ fr liftIO . hPutStrLn h $ either (\e -> "error: " <> show e) (\() -> "ok") r CPBlock fileId info -> withUserRole $ unliftIO u $ do - fs <- asks store + fs <- asks fileStore r <- runExceptT $ do (fr, _) <- ExceptT $ liftIO $ getFile fs SFRecipient fileId ExceptT $ blockServerFile fr info @@ -449,7 +449,7 @@ verifyXFTPTransmission thAuth (tAuth, authorized, (corrId, fId, cmd)) = where verifyCmd :: SFileParty p -> M s VerificationResult verifyCmd party = do - st <- asks store + st <- asks fileStore liftIO $ verify =<< getFile st party fId where verify = \case @@ -485,7 +485,7 @@ processXFTPRequest HTTP2Body {bodyPart} = \case noFile resp = pure (resp, Nothing) createFile :: FileInfo -> NonEmpty RcvPublicAuthKey -> M s FileResponse createFile file rks = do - st <- asks store + st <- asks fileStore r <- runExceptT $ do sizes <- asks $ allowedChunkSizes . config unless (size file `elem` sizes) $ throwE SIZE @@ -522,7 +522,7 @@ processXFTPRequest HTTP2Body {bodyPart} = \case r -> pure r addRecipients :: XFTPFileId -> NonEmpty RcvPublicAuthKey -> M s FileResponse addRecipients sId rks = do - st <- asks store + st <- asks fileStore r <- runExceptT $ do rcps <- mapM (ExceptT . addRecipientRetry st 3 sId) rks lift $ withFileLog $ \sl -> logAddRecipients sl sId rcps @@ -559,7 +559,7 @@ processXFTPRequest HTTP2Body {bodyPart} = \case receiveChunk (XFTPRcvChunkSpec fPath size digest) >>= \case Right () -> do stats <- asks serverStats - st <- asks store + st <- asks fileStore liftIO (setFilePath st senderId fPath) >>= \case Right () -> do withFileLog $ \sl -> logPutFile sl senderId fPath @@ -608,7 +608,7 @@ processXFTPRequest HTTP2Body {bodyPart} = \case ackFileReception :: RecipientId -> FileRec -> M s FileResponse ackFileReception rId fr = do withFileLog (`logAckFile` rId) - st <- asks store + st <- asks fileStore liftIO $ deleteRecipient st rId fr incFileStat fileDownloadAcks pure FROk @@ -629,7 +629,7 @@ deleteOrBlockServerFile_ FileRec {filePath, fileInfo} stat storeAction = runExce path <- readTVarIO filePath stats <- asks serverStats ExceptT $ first (\(_ :: SomeException) -> FILE_IO) <$> try (forM_ path $ \p -> whenM (doesFileExist p) (removeFile p >> deletedStats stats)) - st <- asks store + st <- asks fileStore ExceptT $ liftIO $ storeAction st forM_ path $ \_ -> do us <- asks usedStorage @@ -645,7 +645,7 @@ getFileTime = getRoundedSystemTime expireServerFiles :: FileStoreClass s => Maybe Int -> ExpirationConfig -> M s () expireServerFiles itemDelay expCfg = do - st <- asks store + st <- asks fileStore us <- asks usedStorage usedStart <- readTVarIO us old <- liftIO $ expireBeforeEpoch expCfg @@ -704,7 +704,7 @@ restoreServerStats = asks (serverStatsBackupFile . config) >>= mapM_ restoreStat liftIO (strDecode <$> B.readFile f) >>= \case Right d@FileServerStatsData {_filesCount = statsFilesCount, _filesSize = statsFilesSize} -> do s <- asks serverStats - st <- asks store + st <- asks fileStore _filesCount <- liftIO $ getFileCount st _filesSize <- readTVarIO =<< asks usedStorage liftIO $ setFileServerStats s d {_filesCount, _filesSize} diff --git a/src/Simplex/FileTransfer/Server/Env.hs b/src/Simplex/FileTransfer/Server/Env.hs index 5dd7ec56c..7d88bff80 100644 --- a/src/Simplex/FileTransfer/Server/Env.hs +++ b/src/Simplex/FileTransfer/Server/Env.hs @@ -1,6 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE RankNTypes #-} @@ -8,16 +9,23 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE StrictData #-} +{-# LANGUAGE TypeFamilies #-} module Simplex.FileTransfer.Server.Env ( XFTPServerConfig (..), XFTPStoreConfig (..), XFTPEnv (..), XFTPRequest (..), + XFTPStoreType, + FileStore (..), + AFStoreType (..), + fileStore, + fromFileStore, defaultInactiveClientExpiration, defFileExpirationHours, defaultFileExpiration, newXFTPServerEnv, + readFileStoreType, runWithStoreConfig, checkFileStoreMode, importToDatabase, @@ -111,9 +119,31 @@ data XFTPStoreConfig s where XSCDatabase :: PostgresFileStoreCfg -> XFTPStoreConfig PostgresFileStore #endif +type family XFTPStoreType (fs :: FSType) where + XFTPStoreType 'FSMemory = STMFileStore +#if defined(dbServerPostgres) + XFTPStoreType 'FSPostgres = PostgresFileStore +#endif + +data FileStore s where + StoreMemory :: STMFileStore -> FileStore STMFileStore +#if defined(dbServerPostgres) + StoreDatabase :: PostgresFileStore -> FileStore PostgresFileStore +#endif + +data AFStoreType = forall fs. FileStoreClass (XFTPStoreType fs) => AFSType (SFSType fs) + +fromFileStore :: FileStore s -> s +fromFileStore = \case + StoreMemory s -> s +#if defined(dbServerPostgres) + StoreDatabase s -> s +#endif +{-# INLINE fromFileStore #-} + data XFTPEnv s = XFTPEnv { config :: XFTPServerConfig s, - store :: s, + fileStore_ :: FileStore s, usedStorage :: TVar Int64, storeLog :: Maybe (StoreLog 'WriteMode), random :: TVar ChaChaDRG, @@ -123,6 +153,10 @@ data XFTPEnv s = XFTPEnv serverStats :: FileServerStats } +fileStore :: XFTPEnv s -> s +fileStore = fromFileStore . fileStore_ +{-# INLINE fileStore #-} + defFileExpirationHours :: Int64 defFileExpirationHours = 48 @@ -136,18 +170,18 @@ defaultFileExpiration = newXFTPServerEnv :: FileStoreClass s => XFTPServerConfig s -> IO (XFTPEnv s) newXFTPServerEnv config@XFTPServerConfig {serverStoreCfg, fileSizeQuota, xftpCredentials, httpCredentials} = do random <- C.newRandom - (store, storeLog) <- case serverStoreCfg of + (fileStore_, storeLog) <- case serverStoreCfg of XSCMemory storeLogPath -> do st <- newFileStore () sl <- mapM (`readWriteFileStore` st) storeLogPath atomically $ writeTVar (stmStoreLog st) sl - pure (st, sl) + pure (StoreMemory st, sl) #if defined(dbServerPostgres) XSCDatabase dbCfg -> do st <- newFileStore dbCfg - pure (st, Nothing) + pure (StoreDatabase st, Nothing) #endif - used <- getUsedStorage store + used <- getUsedStorage (fromFileStore fileStore_) usedStorage <- newTVarIO used forM_ fileSizeQuota $ \quota -> do logNote $ "Total / available storage: " <> tshow quota <> " / " <> tshow (quota - used) @@ -156,35 +190,45 @@ newXFTPServerEnv config@XFTPServerConfig {serverStoreCfg, fileSizeQuota, xftpCre httpServerCreds <- mapM loadServerCredential httpCredentials Fingerprint fp <- loadFingerprint xftpCredentials serverStats <- newFileServerStats =<< getCurrentTime - pure XFTPEnv {config, store, usedStorage, storeLog, random, tlsServerCreds, httpServerCreds, serverIdentity = C.KeyHash fp, serverStats} + pure XFTPEnv {config, fileStore_, usedStorage, storeLog, random, tlsServerCreds, httpServerCreds, serverIdentity = C.KeyHash fp, serverStats} data XFTPRequest = XFTPReqNew FileInfo (NonEmpty RcvPublicAuthKey) (Maybe BasicAuth) | XFTPReqCmd XFTPFileId FileRec FileCmd | XFTPReqPing --- | Select and run the store config based on INI settings. +readFileStoreType :: String -> Either String AFStoreType +readFileStoreType = \case + "memory" -> Right $ AFSType SFSMemory +#if defined(dbServerPostgres) + "database" -> Right $ AFSType SFSPostgres +#else + "database" -> Left "Error: server binary is compiled without support for PostgreSQL database.\nPlease re-compile with `cabal build -fserver_postgres`." +#endif + other -> Left $ "Invalid store_files value: " <> other + +-- | Dispatch store config from AFStoreType singleton and run the callback. -- CPP guards for Postgres are handled here so Main.hs stays CPP-free. runWithStoreConfig :: + AFStoreType -> Ini -> - String -> - Maybe FilePath -> FilePath -> MigrationConfirmation -> (forall s. FileStoreClass s => XFTPStoreConfig s -> IO ()) -> IO () -runWithStoreConfig _ini storeType storeLogFile_ _storeLogFilePath _confirmMigrations run = case storeType of - "memory" -> run $ XSCMemory storeLogFile_ +runWithStoreConfig (AFSType fs) ini storeLogFilePath confirmMigrations run = + run $ iniStoreCfg fs + where + enableStoreLog' = settingIsOn "STORE_LOG" "enable" ini + iniStoreCfg :: SFSType fs -> XFTPStoreConfig (XFTPStoreType fs) + iniStoreCfg SFSMemory = XSCMemory (enableStoreLog' $> storeLogFilePath) #if defined(dbServerPostgres) - "database" -> run $ XSCDatabase dbCfg - where - enableDbStoreLog' = settingIsOn "STORE_LOG" "db_store_log" _ini - dbStoreLogPath = enableDbStoreLog' $> _storeLogFilePath - dbCfg = PostgresFileStoreCfg {dbOpts = iniDBOptions _ini defaultXFTPDBOpts, dbStoreLogPath, confirmMigrations = _confirmMigrations} -#else - "database" -> error "Error: server binary is compiled without support for PostgreSQL database.\nPlease re-compile with `cabal build -fserver_postgres`." + iniStoreCfg SFSPostgres = XSCDatabase dbCfg + where + enableDbStoreLog' = settingIsOn "STORE_LOG" "db_store_log" ini + dbStoreLogPath = enableDbStoreLog' $> storeLogFilePath + dbCfg = PostgresFileStoreCfg {dbOpts = iniDBOptions ini defaultXFTPDBOpts, dbStoreLogPath, confirmMigrations} #endif - _ -> error $ "Invalid store_files value: " <> storeType -- | Validate startup config when store_files=database. checkFileStoreMode :: Ini -> String -> FilePath -> IO () diff --git a/src/Simplex/FileTransfer/Server/Main.hs b/src/Simplex/FileTransfer/Server/Main.hs index 9b31dcce5..838703d66 100644 --- a/src/Simplex/FileTransfer/Server/Main.hs +++ b/src/Simplex/FileTransfer/Server/Main.hs @@ -29,7 +29,7 @@ import Options.Applicative import Simplex.FileTransfer.Chunks import Simplex.FileTransfer.Description (FileSize (..)) import Simplex.FileTransfer.Server (runXFTPServer) -import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), XFTPStoreConfig, defFileExpirationHours, defaultFileExpiration, defaultInactiveClientExpiration, runWithStoreConfig, checkFileStoreMode, importToDatabase, exportFromDatabase) +import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), XFTPStoreConfig, AFStoreType (..), defFileExpirationHours, defaultFileExpiration, defaultInactiveClientExpiration, readFileStoreType, runWithStoreConfig, checkFileStoreMode, importToDatabase, exportFromDatabase) import Simplex.FileTransfer.Transport (alpnSupportedXFTPhandshakes, supportedFileServerVRange) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Encoding.String @@ -214,20 +214,22 @@ xftpServerCLI_ generateSite serveStaticFiles cfgPath logPath = do printSourceCode (sourceCode <$> information) let storeType = fromRight "memory" $ T.unpack <$> lookupValue "STORE_LOG" "store_files" ini checkFileStoreMode ini storeType storeLogFilePath - runWithStoreConfig ini storeType (enableStoreLog $> storeLogFilePath) storeLogFilePath confirmMigrations $ \storeCfg -> do - let cfg = serverConfig storeCfg - printXFTPConfig cfg - case webStaticPath' of - Just path -> do - let onionHost = - either (const Nothing) (find isOnion) $ - strDecode @(L.NonEmpty TransportHost) . encodeUtf8 =<< lookupValue "TRANSPORT" "host" ini - webHttpPort = eitherToMaybe (lookupValue "WEB" "http" ini) >>= readMaybe . T.unpack - generateSite cfg information onionHost path - when (isJust webHttpPort || isJust webHttpsParams') $ - serveStaticFiles EmbeddedWebParams {webStaticPath = path, webHttpPort, webHttpsParams = webHttpsParams'} - Nothing -> pure () - runXFTPServer cfg + case readFileStoreType storeType of + Left err -> error err + Right fsType -> runWithStoreConfig fsType ini storeLogFilePath confirmMigrations $ \storeCfg -> do + let cfg = serverConfig storeCfg + printXFTPConfig cfg + case webStaticPath' of + Just path -> do + let onionHost = + either (const Nothing) (find isOnion) $ + strDecode @(L.NonEmpty TransportHost) . encodeUtf8 =<< lookupValue "TRANSPORT" "host" ini + webHttpPort = eitherToMaybe (lookupValue "WEB" "http" ini) >>= readMaybe . T.unpack + generateSite cfg information onionHost path + when (isJust webHttpPort || isJust webHttpsParams') $ + serveStaticFiles EmbeddedWebParams {webStaticPath = path, webHttpPort, webHttpsParams = webHttpsParams'} + Nothing -> pure () + runXFTPServer cfg where isOnion = \case THOnionHost _ -> True; _ -> False enableStoreLog = settingIsOn "STORE_LOG" "enable" ini diff --git a/src/Simplex/FileTransfer/Server/Store.hs b/src/Simplex/FileTransfer/Server/Store.hs index 6b115ff20..8ed418f1a 100644 --- a/src/Simplex/FileTransfer/Server/Store.hs +++ b/src/Simplex/FileTransfer/Server/Store.hs @@ -1,5 +1,6 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} +{-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} @@ -7,7 +8,9 @@ {-# LANGUAGE TypeFamilies #-} module Simplex.FileTransfer.Server.Store - ( FileStoreClass (..), + ( FSType (..), + SFSType (..), + FileStoreClass (..), FileRec (..), FileRecipient (..), STMFileStore (..), @@ -16,6 +19,8 @@ module Simplex.FileTransfer.Server.Store ) where +import Data.Kind (Type) + import Control.Concurrent.STM import Control.Monad (forM, void) import qualified Data.Attoparsec.ByteString.Char8 as A @@ -38,6 +43,12 @@ import Simplex.Messaging.TMap (TMap) import qualified Simplex.Messaging.TMap as TM import Simplex.Messaging.Util (ifM) +data FSType = FSMemory | FSPostgres + +data SFSType :: FSType -> Type where + SFSMemory :: SFSType 'FSMemory + SFSPostgres :: SFSType 'FSPostgres + data FileRec = FileRec { senderId :: SenderId, fileInfo :: FileInfo, diff --git a/tests/Test.hs b/tests/Test.hs index 45e00287b..cb02fd918 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -34,7 +34,8 @@ import Test.Hspec hiding (fit, it) import Util import XFTPAgent import XFTPCLI (xftpCLIFileTests) -import XFTPClient (xftpMemoryServer, xftpMemoryServer2) +import Simplex.FileTransfer.Server.Env (AFStoreType (..)) +import Simplex.FileTransfer.Server.Store (SFSType (..)) import XFTPServerTests (xftpServerTests) import WebTests (webTests) import XFTPWebTests (xftpWebTests) @@ -54,7 +55,7 @@ import PostgresSchemaDump (postgresSchemaDumpTest) import SMPClient (testServerDBConnectInfo, testStoreDBOpts) import Simplex.Messaging.Notifications.Server.Store.Migrations (ntfServerMigrations) import Simplex.Messaging.Server.QueueStore.Postgres.Migrations (serverMigrations) -import XFTPClient (testXFTPDBConnectInfo, xftpPostgresServer, xftpPostgresServer2) +import XFTPClient (testXFTPDBConnectInfo) #endif #if defined(dbPostgres) || defined(dbServerPostgres) @@ -152,19 +153,22 @@ main = do before (pure $ ASType SQSMemory SMSJournal) smpProxyTests describe "XFTP" $ do describe "XFTP server" $ - before (pure xftpMemoryServer) xftpServerTests + before (pure $ AFSType SFSMemory) xftpServerTests describe "XFTP file description" fileDescriptionTests describe "XFTP CLI (memory)" $ - before (pure (xftpMemoryServer, xftpMemoryServer2)) xftpCLIFileTests - describe "XFTP agent" xftpAgentTests + before (pure (AFSType SFSMemory, AFSType SFSMemory)) xftpCLIFileTests + describe "XFTP agent" $ + before (pure $ AFSType SFSMemory) xftpAgentTests #if defined(dbServerPostgres) around_ (postgressBracket testXFTPDBConnectInfo) $ do describe "XFTP Postgres store operations" xftpStoreTests describe "XFTP migration round-trip" xftpMigrationTests describe "XFTP server (PostgreSQL)" $ - before (pure xftpPostgresServer) xftpServerTests + before (pure $ AFSType SFSPostgres) xftpServerTests + describe "XFTP agent (PostgreSQL)" $ + before (pure $ AFSType SFSPostgres) xftpAgentTests describe "XFTP CLI (PostgreSQL)" $ - before (pure (xftpPostgresServer, xftpPostgresServer2)) xftpCLIFileTests + before (pure (AFSType SFSPostgres, AFSType SFSPostgres)) xftpCLIFileTests #endif #if defined(dbPostgres) describe "XFTP Web Client" $ xftpWebTests (dropAllSchemasExceptSystem testDBConnectInfo) diff --git a/tests/XFTPAgent.hs b/tests/XFTPAgent.hs index 71d0f0b09..48bcce5bb 100644 --- a/tests/XFTPAgent.hs +++ b/tests/XFTPAgent.hs @@ -26,7 +26,7 @@ import SMPClient (xit'') import Simplex.FileTransfer.Client (XFTPClientConfig (..)) import Simplex.FileTransfer.Description (FileChunk (..), FileDescription (..), FileDescriptionURI (..), ValidFileDescription, fileDescriptionURI, kb, mb, qrSizeLimit, pattern ValidFileDescription) import Simplex.FileTransfer.Protocol (FileParty (..)) -import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..)) +import Simplex.FileTransfer.Server.Env (AFStoreType, XFTPServerConfig (..)) import Simplex.FileTransfer.Server.Store (STMFileStore) import Simplex.FileTransfer.Transport (XFTPErrorType (AUTH)) import Simplex.FileTransfer.Types (RcvFileId, SndFileId) @@ -55,44 +55,44 @@ import Fixtures import Simplex.Messaging.Agent.Store.Postgres.Util (dropAllSchemasExceptSystem) #endif -xftpAgentTests :: Spec +xftpAgentTests :: SpecWith AFStoreType xftpAgentTests = around_ testBracket #if defined(dbPostgres) . after_ (dropAllSchemasExceptSystem testDBConnectInfo) #endif . describe "agent XFTP API" $ do - it "should send and receive file" $ withXFTPServer testXFTPAgentSendReceive + it "should send and receive file" $ \fsType -> withXFTPServer fsType testXFTPAgentSendReceive -- uncomment CPP option slow_servers and run hpack to run this test - xit "should send and receive file with slow server responses" $ + xit "should send and receive file with slow server responses" $ \_ -> withXFTPServerCfg testXFTPServerConfig {responseDelay = 500000} $ \_ -> testXFTPAgentSendReceive - it "should send and receive with encrypted local files" testXFTPAgentSendReceiveEncrypted - it "should send and receive large file with a redirect" testXFTPAgentSendReceiveRedirect - it "should send and receive small file without a redirect" testXFTPAgentSendReceiveNoRedirect - describe "sending and receiving with version negotiation" testXFTPAgentSendReceiveMatrix - it "should resume receiving file after restart" testXFTPAgentReceiveRestore - it "should cleanup rcv tmp path after permanent error" testXFTPAgentReceiveCleanup - it "should resume sending file after restart" testXFTPAgentSendRestore - xit'' "should cleanup snd prefix path after permanent error" testXFTPAgentSendCleanup - it "should delete sent file on server" testXFTPAgentDelete - it "should resume deleting file after restart" testXFTPAgentDeleteRestore + it "should send and receive with encrypted local files" $ \fsType -> testXFTPAgentSendReceiveEncrypted fsType + it "should send and receive large file with a redirect" $ \fsType -> testXFTPAgentSendReceiveRedirect fsType + it "should send and receive small file without a redirect" $ \fsType -> testXFTPAgentSendReceiveNoRedirect fsType + describe "sending and receiving with version negotiation" $ beforeWith (const (pure ())) testXFTPAgentSendReceiveMatrix + it "should resume receiving file after restart" $ \_ -> testXFTPAgentReceiveRestore + it "should cleanup rcv tmp path after permanent error" $ \_ -> testXFTPAgentReceiveCleanup + it "should resume sending file after restart" $ \_ -> testXFTPAgentSendRestore + xit'' "should cleanup snd prefix path after permanent error" $ \_ -> testXFTPAgentSendCleanup + it "should delete sent file on server" $ \fsType -> testXFTPAgentDelete fsType + it "should resume deleting file after restart" $ \_ -> testXFTPAgentDeleteRestore -- TODO when server is fixed to correctly send AUTH error, this test has to be modified to expect AUTH error - it "if file is deleted on server, should limit retries and continue receiving next file" testXFTPAgentDeleteOnServer - it "if file is expired on server, should report error and continue receiving next file" testXFTPAgentExpiredOnServer - it "should request additional recipient IDs when number of recipients exceeds maximum per request" testXFTPAgentRequestAdditionalRecipientIDs + it "if file is deleted on server, should limit retries and continue receiving next file" $ \fsType -> testXFTPAgentDeleteOnServer fsType + it "if file is expired on server, should report error and continue receiving next file" $ \fsType -> testXFTPAgentExpiredOnServer fsType + it "should request additional recipient IDs when number of recipients exceeds maximum per request" $ \fsType -> testXFTPAgentRequestAdditionalRecipientIDs fsType describe "XFTP server test via agent API" $ do - it "should pass without basic auth" $ testXFTPServerTest Nothing (noAuthSrv testXFTPServer2) `shouldReturn` Nothing + it "should pass without basic auth" $ \_ -> testXFTPServerTest Nothing (noAuthSrv testXFTPServer2) `shouldReturn` Nothing let srv1 = testXFTPServer2 {keyHash = "1234"} - it "should fail with incorrect fingerprint" $ do + it "should fail with incorrect fingerprint" $ \_ -> do testXFTPServerTest Nothing (noAuthSrv srv1) `shouldReturn` Just (ProtocolTestFailure TSConnect $ BROKER (B.unpack $ strEncode srv1) $ NETWORK NEUnknownCAError) describe "server with password" $ do let auth = Just "abcd" srv = ProtoServerWithAuth testXFTPServer2 authErr = Just (ProtocolTestFailure TSCreateFile $ XFTP (B.unpack $ strEncode testXFTPServer2) AUTH) - it "should pass with correct password" $ testXFTPServerTest auth (srv auth) `shouldReturn` Nothing - it "should fail without password" $ testXFTPServerTest auth (srv Nothing) `shouldReturn` authErr - it "should fail with incorrect password" $ testXFTPServerTest auth (srv $ Just "wrong") `shouldReturn` authErr + it "should pass with correct password" $ \_ -> testXFTPServerTest auth (srv auth) `shouldReturn` Nothing + it "should fail without password" $ \_ -> testXFTPServerTest auth (srv Nothing) `shouldReturn` authErr + it "should fail with incorrect password" $ \_ -> testXFTPServerTest auth (srv $ Just "wrong") `shouldReturn` authErr testXFTPServerTest :: HasCallStack => Maybe BasicAuth -> XFTPServerWithAuth -> IO (Maybe ProtocolTestFailure) testXFTPServerTest newFileBasicAuth srv = @@ -143,8 +143,8 @@ testXFTPAgentSendReceive = do rfId <- runRight $ testReceive rcp rfd originalFilePath xftpDeleteRcvFile rcp rfId -testXFTPAgentSendReceiveEncrypted :: HasCallStack => IO () -testXFTPAgentSendReceiveEncrypted = withXFTPServer $ do +testXFTPAgentSendReceiveEncrypted :: HasCallStack => AFStoreType -> IO () +testXFTPAgentSendReceiveEncrypted fsType = withXFTPServer fsType $ do g <- C.newRandom filePath <- createRandomFile s <- LB.readFile filePath @@ -164,8 +164,8 @@ testXFTPAgentSendReceiveEncrypted = withXFTPServer $ do rfId <- runRight $ testReceiveCF rcp rfd cfArgs originalFilePath xftpDeleteRcvFile rcp rfId -testXFTPAgentSendReceiveRedirect :: HasCallStack => IO () -testXFTPAgentSendReceiveRedirect = withXFTPServer $ do +testXFTPAgentSendReceiveRedirect :: HasCallStack => AFStoreType -> IO () +testXFTPAgentSendReceiveRedirect fsType = withXFTPServer fsType $ do --- sender filePathIn <- createRandomFile let fileSize = mb 17 @@ -222,8 +222,8 @@ testXFTPAgentSendReceiveRedirect = withXFTPServer $ do inBytes <- B.readFile filePathIn B.readFile out `shouldReturn` inBytes -testXFTPAgentSendReceiveNoRedirect :: HasCallStack => IO () -testXFTPAgentSendReceiveNoRedirect = withXFTPServer $ do +testXFTPAgentSendReceiveNoRedirect :: HasCallStack => AFStoreType -> IO () +testXFTPAgentSendReceiveNoRedirect fsType = withXFTPServer fsType $ do --- sender let fileSize = mb 5 filePathIn <- createRandomFile_ fileSize "testfile" @@ -506,9 +506,9 @@ testXFTPAgentSendCleanup = withGlobalLogging logCfgNoLogs $ do doesDirectoryExist prefixPath `shouldReturn` False doesFileExist encPath `shouldReturn` False -testXFTPAgentDelete :: HasCallStack => IO () -testXFTPAgentDelete = withGlobalLogging logCfgNoLogs $ - withXFTPServer $ do +testXFTPAgentDelete :: HasCallStack => AFStoreType -> IO () +testXFTPAgentDelete fsType = withGlobalLogging logCfgNoLogs $ + withXFTPServer fsType $ do filePath <- createRandomFile -- send file @@ -576,9 +576,9 @@ testXFTPAgentDeleteRestore = withGlobalLogging logCfgNoLogs $ do rfGet rcp2 liftIO $ rfId' `shouldBe` rfId -testXFTPAgentDeleteOnServer :: HasCallStack => IO () -testXFTPAgentDeleteOnServer = withGlobalLogging logCfgNoLogs $ - withXFTPServer $ do +testXFTPAgentDeleteOnServer :: HasCallStack => AFStoreType -> IO () +testXFTPAgentDeleteOnServer fsType = withGlobalLogging logCfgNoLogs $ + withXFTPServer fsType $ do filePath1 <- createRandomFile' "testfile1" -- send file 1 @@ -614,10 +614,9 @@ testXFTPAgentDeleteOnServer = withGlobalLogging logCfgNoLogs $ -- receive file 2 testReceive' rcp rfd2 filePath2 -testXFTPAgentExpiredOnServer :: HasCallStack => IO () -testXFTPAgentExpiredOnServer = withGlobalLogging logCfgNoLogs $ do - let fastExpiration = ExpirationConfig {ttl = 2, checkInterval = 1} - withXFTPServerCfg testXFTPServerConfig {fileExpiration = Just fastExpiration} . const $ do +testXFTPAgentExpiredOnServer :: HasCallStack => AFStoreType -> IO () +testXFTPAgentExpiredOnServer fsType = withGlobalLogging logCfgNoLogs $ + withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {fileExpiration = Just fastExpiration}) . const $ do filePath1 <- createRandomFile' "testfile1" -- send file 1 @@ -652,9 +651,11 @@ testXFTPAgentExpiredOnServer = withGlobalLogging logCfgNoLogs $ do -- receive file 2 successfully runRight_ . void $ testReceive' rcp rfd2 filePath2 + where + fastExpiration = ExpirationConfig {ttl = 2, checkInterval = 1} -testXFTPAgentRequestAdditionalRecipientIDs :: HasCallStack => IO () -testXFTPAgentRequestAdditionalRecipientIDs = withXFTPServer $ do +testXFTPAgentRequestAdditionalRecipientIDs :: HasCallStack => AFStoreType -> IO () +testXFTPAgentRequestAdditionalRecipientIDs fsType = withXFTPServer fsType $ do filePath <- createRandomFile -- send file diff --git a/tests/XFTPCLI.hs b/tests/XFTPCLI.hs index 70da884eb..f62cbcf2a 100644 --- a/tests/XFTPCLI.hs +++ b/tests/XFTPCLI.hs @@ -11,17 +11,18 @@ import System.FilePath (()) import System.IO.Silently (capture_) import Test.Hspec hiding (fit, it) import Util -import XFTPClient (XFTPTestServer (..), testXFTPServerStr, testXFTPServerStr2, xftpServerFiles, xftpServerFiles2) +import Simplex.FileTransfer.Server.Env (AFStoreType) +import XFTPClient (cfgFS, cfgFS2, withXFTPServerConfigOn, testXFTPServerStr, testXFTPServerStr2, xftpServerFiles, xftpServerFiles2) -xftpCLIFileTests :: SpecWith (XFTPTestServer, XFTPTestServer) +xftpCLIFileTests :: SpecWith (AFStoreType, AFStoreType) xftpCLIFileTests = around_ testBracket $ do - it "should send and receive file" $ \(XFTPTestServer withSrv, _) -> - withSrv id testXFTPCLISendReceive_ - it "should send and receive file with 2 servers" $ \(XFTPTestServer withSrv1, XFTPTestServer withSrv2) -> - withSrv1 id $ withSrv2 id testXFTPCLISendReceive2servers_ - it "should delete file from 2 servers" $ \(XFTPTestServer withSrv1, XFTPTestServer withSrv2) -> - withSrv1 id $ withSrv2 id testXFTPCLIDelete_ - it "prepareChunkSizes should use 2 chunk sizes" $ \(_, _) -> testPrepareChunkSizes + it "should send and receive file" $ \(fsType, _) -> + withXFTPServerConfigOn (cfgFS fsType) $ \_ -> testXFTPCLISendReceive_ + it "should send and receive file with 2 servers" $ \(fsType1, fsType2) -> + withXFTPServerConfigOn (cfgFS fsType1) $ \_ -> withXFTPServerConfigOn (cfgFS2 fsType2) $ \_ -> testXFTPCLISendReceive2servers_ + it "should delete file from 2 servers" $ \(fsType1, fsType2) -> + withXFTPServerConfigOn (cfgFS fsType1) $ \_ -> withXFTPServerConfigOn (cfgFS2 fsType2) $ \_ -> testXFTPCLIDelete_ + it "prepareChunkSizes should use 2 chunk sizes" $ \_ -> testPrepareChunkSizes testBracket :: IO () -> IO () testBracket = diff --git a/tests/XFTPClient.hs b/tests/XFTPClient.hs index ba07ae050..bea2e9002 100644 --- a/tests/XFTPClient.hs +++ b/tests/XFTPClient.hs @@ -1,4 +1,5 @@ {-# LANGUAGE CPP #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} @@ -8,8 +9,6 @@ module XFTPClient where import Control.Concurrent (ThreadId, threadDelay) -import Control.Exception (SomeException, catch) -import System.Directory (removeFile) import Control.Monad (void) import Data.String (fromString) import Data.Time.Clock (getCurrentTime) @@ -18,8 +17,8 @@ import SMPClient (serverBracket) import Simplex.FileTransfer.Client import Simplex.FileTransfer.Description import Simplex.FileTransfer.Server (runXFTPServerBlocking) -import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), XFTPStoreConfig (..), defaultFileExpiration, defaultInactiveClientExpiration) -import Simplex.FileTransfer.Server.Store (FileStoreClass, STMFileStore) +import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..), XFTPStoreConfig (..), AFStoreType (..), defaultFileExpiration, defaultInactiveClientExpiration) +import Simplex.FileTransfer.Server.Store (FileStoreClass, SFSType (..), STMFileStore) import Simplex.FileTransfer.Transport (alpnSupportedXFTPhandshakes, supportedFileServerVRange) import Simplex.Messaging.Protocol (XFTPServer) import Simplex.Messaging.Transport.HTTP2 (httpALPN) @@ -33,27 +32,27 @@ import Simplex.Messaging.Agent.Store.Postgres.Options (DBOpts (..)) import Simplex.Messaging.Agent.Store.Shared (MigrationConfirmation (..)) #endif --- Parameterized server bracket +data AXFTPServerConfig = forall s. FileStoreClass s => AXFTPSrvCfg (XFTPServerConfig s) -newtype XFTPTestServer = XFTPTestServer - { runServer :: forall a. (forall s. XFTPServerConfig s -> XFTPServerConfig s) -> IO a -> IO a - } +updateXFTPCfg :: AXFTPServerConfig -> (forall s. XFTPServerConfig s -> XFTPServerConfig s) -> AXFTPServerConfig +updateXFTPCfg (AXFTPSrvCfg cfg) f = AXFTPSrvCfg (f cfg) --- Store-log-dependent agent tests need the bracket + a way to clear server state -type XFTPTestServerClear = (XFTPTestServer, IO ()) +cfgFS :: AFStoreType -> AXFTPServerConfig +cfgFS (AFSType fs) = case fs of + SFSMemory -> AXFTPSrvCfg testXFTPServerConfig +#if defined(dbServerPostgres) + SFSPostgres -> AXFTPSrvCfg testXFTPServerConfig {serverStoreCfg = XSCDatabase testXFTPPostgresCfg} +#endif -xftpMemoryServer :: XFTPTestServer -xftpMemoryServer = XFTPTestServer $ \cfgF test -> withXFTPServerCfg (cfgF testXFTPServerConfig) $ \_ -> test +cfgFS2 :: AFStoreType -> AXFTPServerConfig +cfgFS2 (AFSType fs) = case fs of + SFSMemory -> AXFTPSrvCfg testXFTPServerConfig2 +#if defined(dbServerPostgres) + SFSPostgres -> AXFTPSrvCfg testXFTPServerConfig2 {serverStoreCfg = XSCDatabase testXFTPPostgresCfg} +#endif -xftpMemoryServerWithLog :: XFTPTestServer -xftpMemoryServerWithLog = XFTPTestServer $ \cfgF test -> - withXFTPServerCfg (cfgF testXFTPServerConfig {serverStoreCfg = XSCMemory (Just testXFTPLogFile), storeLogFile = Just testXFTPLogFile, serverStatsBackupFile = Just testXFTPStatsBackupFile}) $ \_ -> test - -xftpMemoryServerClear :: XFTPTestServerClear -xftpMemoryServerClear = (xftpMemoryServerWithLog, removeFile testXFTPLogFile `catch` \(_ :: SomeException) -> pure ()) - -xftpMemoryServer2 :: XFTPTestServer -xftpMemoryServer2 = XFTPTestServer $ \cfgF test -> withXFTPServerCfg (cfgF testXFTPServerConfig2) $ \_ -> test +withXFTPServerConfigOn :: HasCallStack => AXFTPServerConfig -> (HasCallStack => ThreadId -> IO a) -> IO a +withXFTPServerConfigOn (AXFTPSrvCfg cfg) = withXFTPServerCfg cfg #if defined(dbServerPostgres) testXFTPDBConnectInfo :: ConnectInfo @@ -76,15 +75,6 @@ testXFTPPostgresCfg = confirmMigrations = MCYesUp } -xftpPostgresServer :: XFTPTestServer -xftpPostgresServer = XFTPTestServer $ \cfgF test -> withXFTPServerCfg (cfgF testXFTPServerConfig {serverStoreCfg = XSCDatabase testXFTPPostgresCfg}) $ \_ -> test - -xftpPostgresServer2 :: XFTPTestServer -xftpPostgresServer2 = XFTPTestServer $ \cfgF test -> withXFTPServerCfg (cfgF testXFTPServerConfig2 {serverStoreCfg = XSCDatabase testXFTPPostgresCfg}) $ \_ -> test - -xftpPostgresServerClear :: XFTPTestServerClear -xftpPostgresServerClear = (xftpPostgresServer, clearXFTPPostgresStore) - clearXFTPPostgresStore :: IO () clearXFTPPostgresStore = do let DBOpts {connstr} = dbOpts testXFTPPostgresCfg @@ -94,23 +84,23 @@ clearXFTPPostgresStore = do PSQL.close conn #endif -xftpTest :: HasCallStack => (HasCallStack => XFTPClient -> IO ()) -> XFTPTestServer -> Expectation -xftpTest test (XFTPTestServer withSrv) = withSrv id (testXFTPClient test) `shouldReturn` () +xftpTest :: HasCallStack => (HasCallStack => XFTPClient -> IO ()) -> AFStoreType -> Expectation +xftpTest test fsType = withXFTPServerConfigOn (cfgFS fsType) (\_ -> testXFTPClient test) `shouldReturn` () -xftpTestN :: HasCallStack => Int -> (HasCallStack => [XFTPClient] -> IO ()) -> XFTPTestServer -> Expectation -xftpTestN nClients test (XFTPTestServer withSrv) = withSrv id (run nClients []) `shouldReturn` () +xftpTestN :: HasCallStack => Int -> (HasCallStack => [XFTPClient] -> IO ()) -> AFStoreType -> Expectation +xftpTestN nClients test fsType = withXFTPServerConfigOn (cfgFS fsType) (\_ -> run nClients []) `shouldReturn` () where run :: Int -> [XFTPClient] -> IO () run 0 hs = test hs run n hs = testXFTPClient $ \h -> run (n - 1) (h : hs) -xftpTest2 :: HasCallStack => (HasCallStack => XFTPClient -> XFTPClient -> IO ()) -> XFTPTestServer -> Expectation +xftpTest2 :: HasCallStack => (HasCallStack => XFTPClient -> XFTPClient -> IO ()) -> AFStoreType -> Expectation xftpTest2 test = xftpTestN 2 _test where _test [h1, h2] = test h1 h2 _test _ = error "expected 2 handles" -xftpTest4 :: HasCallStack => (HasCallStack => XFTPClient -> XFTPClient -> XFTPClient -> XFTPClient -> IO ()) -> XFTPTestServer -> Expectation +xftpTest4 :: HasCallStack => (HasCallStack => XFTPClient -> XFTPClient -> XFTPClient -> XFTPClient -> IO ()) -> AFStoreType -> Expectation xftpTest4 test = xftpTestN 4 _test where _test [h1, h2, h3, h4] = test h1 h2 h3 h4 @@ -125,11 +115,11 @@ withXFTPServerCfg cfg = withXFTPServerCfgNoALPN :: (HasCallStack, FileStoreClass s) => XFTPServerConfig s -> (HasCallStack => ThreadId -> IO a) -> IO a withXFTPServerCfgNoALPN cfg = withXFTPServerCfg cfg {transportConfig = (transportConfig cfg) {serverALPN = Nothing}} -withXFTPServer :: HasCallStack => IO a -> IO a -withXFTPServer = withXFTPServerCfg testXFTPServerConfig . const +withXFTPServer :: HasCallStack => AFStoreType -> IO a -> IO a +withXFTPServer fsType = withXFTPServerConfigOn (cfgFS fsType) . const -withXFTPServer2 :: HasCallStack => IO a -> IO a -withXFTPServer2 = withXFTPServerCfg testXFTPServerConfig {xftpPort = xftpTestPort2, filesPath = xftpServerFiles2} . const +withXFTPServer2 :: HasCallStack => AFStoreType -> IO a -> IO a +withXFTPServer2 fsType = withXFTPServerConfigOn (cfgFS2 fsType) . const withXFTPServerStoreLogOn :: HasCallStack => (HasCallStack => ThreadId -> IO a) -> IO a withXFTPServerStoreLogOn = withXFTPServerCfg testXFTPServerConfig {serverStoreCfg = XSCMemory (Just testXFTPLogFile), storeLogFile = Just testXFTPLogFile, serverStatsBackupFile = Just testXFTPStatsBackupFile} diff --git a/tests/XFTPServerTests.hs b/tests/XFTPServerTests.hs index 10ac0d36c..d3d53e6b8 100644 --- a/tests/XFTPServerTests.hs +++ b/tests/XFTPServerTests.hs @@ -31,7 +31,7 @@ import ServerTests (logSize) import Simplex.FileTransfer.Client import Simplex.FileTransfer.Description (kb) import Simplex.FileTransfer.Protocol (FileInfo (..), XFTPFileId, xftpBlockSize) -import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..)) +import Simplex.FileTransfer.Server.Env (AFStoreType, XFTPServerConfig (..)) import Simplex.FileTransfer.Transport (XFTPClientHandshake (..), XFTPClientHello (..), XFTPErrorType (..), XFTPRcvChunkSpec (..), XFTPServerHandshake (..), pattern VersionXFTP) import Simplex.Messaging.Client (ProtocolClientError (..)) import qualified Simplex.Messaging.Crypto as C @@ -52,7 +52,7 @@ import UnliftIO.STM import Util import XFTPClient -xftpServerTests :: SpecWith XFTPTestServer +xftpServerTests :: SpecWith AFStoreType xftpServerTests = before_ (createDirectoryIfMissing False xftpServerFiles) . after_ (removeDirectoryRecursive xftpServerFiles) $ do describe "XFTP file chunk delivery" $ do @@ -103,10 +103,10 @@ createTestChunk fp = do readChunk :: XFTPFileId -> IO ByteString readChunk sId = B.readFile (xftpServerFiles B.unpack (B64.encode $ unEntityId sId)) -testFileChunkDelivery :: XFTPTestServer -> Expectation +testFileChunkDelivery :: AFStoreType -> Expectation testFileChunkDelivery = xftpTest $ \c -> runRight_ $ runTestFileChunkDelivery c c -testFileChunkDelivery2 :: XFTPTestServer -> Expectation +testFileChunkDelivery2 :: AFStoreType -> Expectation testFileChunkDelivery2 = xftpTest2 $ \s r -> runRight_ $ runTestFileChunkDelivery s r runTestFileChunkDelivery :: XFTPClient -> XFTPClient -> ExceptT XFTPClientError IO () @@ -129,7 +129,7 @@ runTestFileChunkDelivery s r = do downloadXFTPChunk g r rpKey rId $ XFTPRcvChunkSpec "tests/tmp/received_chunk1" chSize digest liftIO $ B.readFile "tests/tmp/received_chunk1" `shouldReturn` bytes -testFileChunkDeliveryAddRecipients :: XFTPTestServer -> Expectation +testFileChunkDeliveryAddRecipients :: AFStoreType -> Expectation testFileChunkDeliveryAddRecipients = xftpTest4 $ \s r1 r2 r3 -> runRight_ $ do g <- liftIO C.newRandom (sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g @@ -150,10 +150,10 @@ testFileChunkDeliveryAddRecipients = xftpTest4 $ \s r1 r2 r3 -> runRight_ $ do testReceiveChunk r2 rpKey2 rId2 "tests/tmp/received_chunk2" testReceiveChunk r3 rpKey3 rId3 "tests/tmp/received_chunk3" -testFileChunkDelete :: XFTPTestServer -> Expectation +testFileChunkDelete :: AFStoreType -> Expectation testFileChunkDelete = xftpTest $ \c -> runRight_ $ runTestFileChunkDelete c c -testFileChunkDelete2 :: XFTPTestServer -> Expectation +testFileChunkDelete2 :: AFStoreType -> Expectation testFileChunkDelete2 = xftpTest2 $ \s r -> runRight_ $ runTestFileChunkDelete s r runTestFileChunkDelete :: XFTPClient -> XFTPClient -> ExceptT XFTPClientError IO () @@ -179,10 +179,10 @@ runTestFileChunkDelete s r = do deleteXFTPChunk s spKey sId `catchError` (liftIO . (`shouldBe` PCEProtocolError AUTH)) -testFileChunkAck :: XFTPTestServer -> Expectation +testFileChunkAck :: AFStoreType -> Expectation testFileChunkAck = xftpTest $ \c -> runRight_ $ runTestFileChunkAck c c -testFileChunkAck2 :: XFTPTestServer -> Expectation +testFileChunkAck2 :: AFStoreType -> Expectation testFileChunkAck2 = xftpTest2 $ \s r -> runRight_ $ runTestFileChunkAck s r runTestFileChunkAck :: XFTPClient -> XFTPClient -> ExceptT XFTPClientError IO () @@ -206,7 +206,7 @@ runTestFileChunkAck s r = do ackXFTPChunk r rpKey rId `catchError` (liftIO . (`shouldBe` PCEProtocolError AUTH)) -testWrongChunkSize :: XFTPTestServer -> Expectation +testWrongChunkSize :: AFStoreType -> Expectation testWrongChunkSize = xftpTest $ \c -> do g <- C.newRandom (sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g @@ -218,8 +218,8 @@ testWrongChunkSize = xftpTest $ \c -> do void (createXFTPChunk c spKey file [rcvKey] Nothing) `catchError` (liftIO . (`shouldBe` PCEProtocolError SIZE)) -testFileChunkExpiration :: XFTPTestServer -> Expectation -testFileChunkExpiration _ = withXFTPServerCfg testXFTPServerConfig {fileExpiration} $ +testFileChunkExpiration :: AFStoreType -> Expectation +testFileChunkExpiration fsType = withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {fileExpiration}) $ \_ -> testXFTPClient $ \c -> runRight_ $ do g <- liftIO C.newRandom (sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g @@ -242,8 +242,8 @@ testFileChunkExpiration _ = withXFTPServerCfg testXFTPServerConfig {fileExpirati where fileExpiration = Just ExpirationConfig {ttl = 1, checkInterval = 1} -testInactiveClientExpiration :: XFTPTestServer -> Expectation -testInactiveClientExpiration _ = withXFTPServerCfg testXFTPServerConfig {inactiveClientExpiration} $ \_ -> runRight_ $ do +testInactiveClientExpiration :: AFStoreType -> Expectation +testInactiveClientExpiration fsType = withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {inactiveClientExpiration}) $ \_ -> runRight_ $ do disconnected <- newEmptyTMVarIO ts <- liftIO getCurrentTime c <- ExceptT $ getXFTPClient (1, testXFTPServer, Nothing) testXFTPClientConfig [] ts (\_ -> atomically $ putTMVar disconnected ()) @@ -258,8 +258,8 @@ testInactiveClientExpiration _ = withXFTPServerCfg testXFTPServerConfig {inactiv where inactiveClientExpiration = Just ExpirationConfig {ttl = 1, checkInterval = 1} -testFileStorageQuota :: XFTPTestServer -> Expectation -testFileStorageQuota _ = withXFTPServerCfg testXFTPServerConfig {fileSizeQuota = Just $ chSize * 2} $ +testFileStorageQuota :: AFStoreType -> Expectation +testFileStorageQuota fsType = withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {fileSizeQuota = Just $ chSize * 2}) $ \_ -> testXFTPClient $ \c -> runRight_ $ do g <- liftIO C.newRandom (sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g @@ -286,7 +286,7 @@ testFileStorageQuota _ = withXFTPServerCfg testXFTPServerConfig {fileSizeQuota = uploadXFTPChunk c spKey sId3 chunkSpec download rId3 -testFileLog :: XFTPTestServer -> Expectation +testFileLog :: AFStoreType -> Expectation testFileLog _ = do g <- C.newRandom bytes <- liftIO $ createTestChunk testChunkPath @@ -378,9 +378,9 @@ testFileLog _ = do downloadXFTPChunk g c rpKey rId $ XFTPRcvChunkSpec "tests/tmp/received_chunk1" chSize digest liftIO $ B.readFile "tests/tmp/received_chunk1" `shouldReturn` bytes -testFileBasicAuth :: Bool -> Maybe BasicAuth -> Maybe BasicAuth -> Bool -> XFTPTestServer -> IO () -testFileBasicAuth allowNewFiles newFileBasicAuth clntAuth success _ = - withXFTPServerCfg testXFTPServerConfig {allowNewFiles, newFileBasicAuth} $ +testFileBasicAuth :: Bool -> Maybe BasicAuth -> Maybe BasicAuth -> Bool -> AFStoreType -> IO () +testFileBasicAuth allowNewFiles newFileBasicAuth clntAuth success fsType = + withXFTPServerConfigOn (updateXFTPCfg (cfgFS fsType) $ \c -> c {allowNewFiles, newFileBasicAuth}) $ \_ -> testXFTPClient $ \c -> do g <- C.newRandom (sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g @@ -400,9 +400,9 @@ testFileBasicAuth allowNewFiles newFileBasicAuth clntAuth success _ = void (createXFTPChunk c spKey file [rcvKey] clntAuth) `catchError` (liftIO . (`shouldBe` PCEProtocolError AUTH)) -testFileSkipCommitted :: XFTPTestServer -> IO () -testFileSkipCommitted _ = - withXFTPServerCfg testXFTPServerConfig $ +testFileSkipCommitted :: AFStoreType -> IO () +testFileSkipCommitted fsType = + withXFTPServerConfigOn (cfgFS fsType) $ \_ -> testXFTPClient $ \c -> do g <- C.newRandom (sndKey, spKey) <- atomically $ C.generateAuthKeyPair C.SEd25519 g