From 40000047af4d6086445cc39ceef953a13ef8e282 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Thu, 17 Nov 2022 19:43:01 +0000 Subject: [PATCH] server: refactor CLIs, tests (#564) * server: refactor CLIs, tests * add files, test * rename Executable -> Main --- apps/ntf-server/Main.hs | 74 +---------- apps/smp-server/Main.hs | 122 +---------------- package.yaml | 2 + simplexmq.cabal | 5 + .../Messaging/Notifications/Server/Main.hs | 76 +++++++++++ src/Simplex/Messaging/Server/Main.hs | 124 ++++++++++++++++++ tests/CLITests.hs | 76 +++++++++++ tests/Test.hs | 2 + 8 files changed, 289 insertions(+), 192 deletions(-) create mode 100644 src/Simplex/Messaging/Notifications/Server/Main.hs create mode 100644 src/Simplex/Messaging/Server/Main.hs create mode 100644 tests/CLITests.hs diff --git a/apps/ntf-server/Main.hs b/apps/ntf-server/Main.hs index 9c3790814..aa62d35d8 100644 --- a/apps/ntf-server/Main.hs +++ b/apps/ntf-server/Main.hs @@ -1,18 +1,7 @@ -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} - module Main where import Control.Logger.Simple -import Data.Functor (($>)) -import Data.Ini (lookupValue) -import Simplex.Messaging.Client.Agent (defaultSMPClientAgentConfig) -import Simplex.Messaging.Notifications.Server (runNtfServer) -import Simplex.Messaging.Notifications.Server.Env (NtfServerConfig (..)) -import Simplex.Messaging.Notifications.Server.Push.APNS (defaultAPNSPushClientConfig) -import Simplex.Messaging.Server.CLI (ServerCLIConfig (..), protocolServerCLI) -import System.FilePath (combine) +import Simplex.Messaging.Notifications.Server.Main cfgPath :: FilePath cfgPath = "/etc/opt/simplex-notifications" @@ -26,63 +15,4 @@ logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} main :: IO () main = do setLogLevel LogDebug -- change to LogError in production - withGlobalLogging logCfg $ protocolServerCLI ntfServerCLIConfig runNtfServer - -ntfServerCLIConfig :: ServerCLIConfig NtfServerConfig -ntfServerCLIConfig = - let caCrtFile = combine cfgPath "ca.crt" - serverKeyFile = combine cfgPath "server.key" - serverCrtFile = combine cfgPath "server.crt" - in ServerCLIConfig - { cfgDir = cfgPath, - logDir = logPath, - iniFile = combine cfgPath "ntf-server.ini", - storeLogFile = combine logPath "ntf-server-store.log", - caKeyFile = combine cfgPath "ca.key", - caCrtFile, - serverKeyFile, - serverCrtFile, - fingerprintFile = combine cfgPath "fingerprint", - defaultServerPort = "443", - executableName = "ntf-server", - serverVersion = "SMP notifications server v1.2.0", - mkIniFile = \enableStoreLog defaultServerPort -> - "[STORE_LOG]\n\ - \# The server uses STM memory for persistence,\n\ - \# that will be lost on restart (e.g., as with redis).\n\ - \# This option enables saving memory to append only log,\n\ - \# and restoring it when the server is started.\n\ - \# Log is compacted on start (deleted objects are removed).\n\ - \enable: " - <> (if enableStoreLog then "on" else "off") - <> "\n\ - \log_stats: off\n\n\ - \[TRANSPORT]\n\ - \port: " - <> defaultServerPort - <> "\n\ - \websockets: off\n", - mkServerConfig = \storeLogFile transports ini -> - let settingIsOn section name = if lookupValue section name ini == Right "on" then Just () else Nothing - logStats = settingIsOn "STORE_LOG" "log_stats" - in NtfServerConfig - { transports, - subIdBytes = 24, - regCodeBytes = 32, - clientQSize = 16, - subQSize = 64, - pushQSize = 128, - smpAgentCfg = defaultSMPClientAgentConfig, - apnsConfig = defaultAPNSPushClientConfig, - inactiveClientExpiration = Nothing, - storeLogFile, - resubscribeDelay = 50000, -- 50ms - caCertificateFile = caCrtFile, - privateKeyFile = serverKeyFile, - certificateFile = serverCrtFile, - logStatsInterval = logStats $> 86400, -- seconds - logStatsStartTime = 0, -- seconds from 00:00 UTC - serverStatsLogFile = combine logPath "ntf-server-stats.daily.log", - serverStatsBackupFile = logStats $> combine logPath "ntf-server-stats.log" - } - } + withGlobalLogging logCfg $ ntfServerCLI cfgPath logPath diff --git a/apps/smp-server/Main.hs b/apps/smp-server/Main.hs index 358f36a13..07dbc51d1 100644 --- a/apps/smp-server/Main.hs +++ b/apps/smp-server/Main.hs @@ -1,22 +1,7 @@ -{-# LANGUAGE DuplicateRecordFields #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE OverloadedStrings #-} - module Main where import Control.Logger.Simple -import Data.Functor (($>)) -import Data.Ini (lookupValue) -import Data.Maybe (fromMaybe) -import qualified Data.Text as T -import Data.Text.Encoding (encodeUtf8) -import Simplex.Messaging.Encoding.String -import Simplex.Messaging.Server (runSMPServer) -import Simplex.Messaging.Server.CLI (ServerCLIConfig (..), protocolServerCLI, readStrictIni) -import Simplex.Messaging.Server.Env.STM (ServerConfig (..), defaultInactiveClientExpiration, defaultMessageExpiration) -import Simplex.Messaging.Server.Expiration -import Simplex.Messaging.Transport (simplexMQVersion, supportedSMPServerVRange) -import System.FilePath (combine) +import Simplex.Messaging.Server.Main cfgPath :: FilePath cfgPath = "/etc/opt/simplex" @@ -30,107 +15,4 @@ logCfg = LogConfig {lc_file = Nothing, lc_stderr = True} main :: IO () main = do setLogLevel LogDebug - withGlobalLogging logCfg . protocolServerCLI smpServerCLIConfig $ \cfg@ServerConfig {inactiveClientExpiration} -> do - putStrLn $ case inactiveClientExpiration of - Just ExpirationConfig {ttl, checkInterval} -> "expiring clients inactive for " <> show ttl <> " seconds every " <> show checkInterval <> " seconds" - _ -> "not expiring inactive clients" - putStrLn $ - "creating new queues " - <> if allowNewQueues cfg - then maybe "allowed" (const "requires basic auth") $ newQueueBasicAuth cfg - else "NOT allowed" - runSMPServer cfg - -smpServerCLIConfig :: ServerCLIConfig ServerConfig -smpServerCLIConfig = - let caCrtFile = combine cfgPath "ca.crt" - serverKeyFile = combine cfgPath "server.key" - serverCrtFile = combine cfgPath "server.crt" - in ServerCLIConfig - { cfgDir = cfgPath, - logDir = logPath, - iniFile = combine cfgPath "smp-server.ini", - storeLogFile = combine logPath "smp-server-store.log", - caKeyFile = combine cfgPath "ca.key", - caCrtFile, - serverKeyFile, - serverCrtFile, - fingerprintFile = combine cfgPath "fingerprint", - defaultServerPort = "5223", - executableName = "smp-server", - serverVersion = "SMP server v" <> simplexMQVersion, - mkIniFile = \enableStoreLog defaultServerPort -> - "[STORE_LOG]\n\ - \# The server uses STM memory for persistence,\n\ - \# that will be lost on restart (e.g., as with redis).\n\ - \# This option enables saving memory to append only log,\n\ - \# and restoring it when the server is started.\n\ - \# Log is compacted on start (deleted objects are removed).\n" - <> ("enable: " <> (if enableStoreLog then "on" else "off") <> "\n") - <> "# Undelivered messages are optionally saved and restored when the server restarts,\n\ - \# they are preserved in the .bak file until the next restart.\n" - <> ("restore_messages: " <> (if enableStoreLog then "on" else "off") <> "\n") - <> "log_stats: off\n\n" - <> "[AUTH]\n" - <> "# Set new_queues option to off to completely prohibit creating new messaging queues.\n" - <> "# This can be useful when you want to decommission the server, but not all connections are switched yet.\n" - <> "new_queues: on\n\n" - <> "# Use create_password option to enable basic auth to create new messaging queues.\n" - <> "# The password should be used as part of server address in client configuration:\n" - <> "# smp://fingerprint:password@host1,host2\n" - <> "# The password will not be shared with the connecting contacts, you must share it only\n" - <> "# with the users who you want to allow creating messaging queues on your server.\n" - <> "# create_password: password to create new queues (any printable ASCII characters without whitespace, '@', ':' and '/')\n\n" - <> "[TRANSPORT]\n" - <> ("port: " <> defaultServerPort <> "\n") - <> "websockets: off\n\n" - <> "[INACTIVE_CLIENTS]\n\ - \# TTL and interval to check inactive clients\n\ - \disconnect: off\n" - <> ("# ttl: " <> show (ttl defaultInactiveClientExpiration) <> "\n") - <> ("# check_interval: " <> show (checkInterval defaultInactiveClientExpiration) <> "\n"), - mkServerConfig = \storeLogFile transports ini -> - let onOff section name = case lookupValue section name ini of - Right "on" -> Just True - Right "off" -> Just False - Right s -> error . T.unpack $ "invalid INI setting " <> name <> ": " <> s - _ -> Nothing - settingIsOn section name = if onOff section name == Just True then Just () else Nothing - logStats = settingIsOn "STORE_LOG" "log_stats" - in ServerConfig - { transports, - tbqSize = 16, - serverTbqSize = 64, - msgQueueQuota = 128, - queueIdBytes = 24, - msgIdBytes = 24, -- must be at least 24 bytes, it is used as 192-bit nonce for XSalsa20 - caCertificateFile = caCrtFile, - privateKeyFile = serverKeyFile, - certificateFile = serverCrtFile, - storeLogFile, - storeMsgsFile = - let messagesPath = combine logPath "smp-server-messages.log" - in case onOff "STORE_LOG" "restore_messages" of - Just True -> Just messagesPath - Just False -> Nothing - -- if the setting is not set, it is enabled when store log is enabled - _ -> storeLogFile $> messagesPath, - -- allow creating new queues by default - allowNewQueues = fromMaybe True $ onOff "AUTH" "new_queues", - newQueueBasicAuth = case lookupValue "AUTH" "create_password" ini of - Right auth -> either error Just . strDecode $ encodeUtf8 auth - _ -> Nothing, - messageExpiration = Just defaultMessageExpiration, - inactiveClientExpiration = - settingIsOn "INACTIVE_CLIENTS" "disconnect" - $> ExpirationConfig - { ttl = readStrictIni "INACTIVE_CLIENTS" "ttl" ini, - checkInterval = readStrictIni "INACTIVE_CLIENTS" "check_interval" ini - }, - logStatsInterval = logStats $> 86400, -- seconds - logStatsStartTime = 0, -- seconds from 00:00 UTC - serverStatsLogFile = combine logPath "smp-server-stats.daily.log", - serverStatsBackupFile = logStats $> combine logPath "smp-server-stats.log", - smpServerVRange = supportedSMPServerVRange - } - } + withGlobalLogging logCfg $ smpServerCLI cfgPath logPath diff --git a/package.yaml b/package.yaml index 8cc39465c..72f5819fe 100644 --- a/package.yaml +++ b/package.yaml @@ -121,6 +121,8 @@ tests: - hspec-core == 2.7.* - HUnit == 1.6.* - QuickCheck == 2.14.* + - silently == 1.2.* + - main-tester == 0.2.* - timeit == 2.0.* ghc-options: diff --git a/simplexmq.cabal b/simplexmq.cabal index bffda6a6f..3c2f09ba3 100644 --- a/simplexmq.cabal +++ b/simplexmq.cabal @@ -66,6 +66,7 @@ library Simplex.Messaging.Notifications.Protocol Simplex.Messaging.Notifications.Server Simplex.Messaging.Notifications.Server.Env + Simplex.Messaging.Notifications.Server.Main Simplex.Messaging.Notifications.Server.Push.APNS Simplex.Messaging.Notifications.Server.Stats Simplex.Messaging.Notifications.Server.Store @@ -78,6 +79,7 @@ library Simplex.Messaging.Server.CLI Simplex.Messaging.Server.Env.STM Simplex.Messaging.Server.Expiration + Simplex.Messaging.Server.Main Simplex.Messaging.Server.MsgStore Simplex.Messaging.Server.MsgStore.STM Simplex.Messaging.Server.QueueStore @@ -351,6 +353,7 @@ test-suite smp-server-test AgentTests.NotificationTests AgentTests.SchemaDump AgentTests.SQLiteTests + CLITests CoreTests.CryptoTests CoreTests.EncodingTests CoreTests.ProtocolErrorTests @@ -393,6 +396,7 @@ test-suite smp-server-test , http2 ==3.0.* , ini ==0.4.1 , iso8601-time ==0.1.* + , main-tester ==0.2.* , memory ==0.15.* , mtl ==2.2.* , network >=3.1.2.7 && <3.2 @@ -400,6 +404,7 @@ test-suite smp-server-test , optparse-applicative >=0.15 && <0.17 , process ==1.6.* , random >=1.1 && <1.3 + , silently ==1.2.* , simple-logger ==0.1.* , simplexmq , socks ==0.6.* diff --git a/src/Simplex/Messaging/Notifications/Server/Main.hs b/src/Simplex/Messaging/Notifications/Server/Main.hs new file mode 100644 index 000000000..d92d7a351 --- /dev/null +++ b/src/Simplex/Messaging/Notifications/Server/Main.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} + +module Simplex.Messaging.Notifications.Server.Main where + +import Data.Functor (($>)) +import Data.Ini (lookupValue) +import Simplex.Messaging.Client.Agent (defaultSMPClientAgentConfig) +import Simplex.Messaging.Notifications.Server (runNtfServer) +import Simplex.Messaging.Notifications.Server.Env (NtfServerConfig (..)) +import Simplex.Messaging.Notifications.Server.Push.APNS (defaultAPNSPushClientConfig) +import Simplex.Messaging.Server.CLI (ServerCLIConfig (..), protocolServerCLI) +import System.FilePath (combine) + +ntfServerCLI :: FilePath -> FilePath -> IO () +ntfServerCLI cfgPath logPath = protocolServerCLI (ntfServerCLIConfig cfgPath logPath) runNtfServer + +ntfServerCLIConfig :: FilePath -> FilePath -> ServerCLIConfig NtfServerConfig +ntfServerCLIConfig cfgPath logPath = + let caCrtFile = combine cfgPath "ca.crt" + serverKeyFile = combine cfgPath "server.key" + serverCrtFile = combine cfgPath "server.crt" + in ServerCLIConfig + { cfgDir = cfgPath, + logDir = logPath, + iniFile = combine cfgPath "ntf-server.ini", + storeLogFile = combine logPath "ntf-server-store.log", + caKeyFile = combine cfgPath "ca.key", + caCrtFile, + serverKeyFile, + serverCrtFile, + fingerprintFile = combine cfgPath "fingerprint", + defaultServerPort = "443", + executableName = "ntf-server", + serverVersion = "SMP notifications server v1.2.0", + mkIniFile = \enableStoreLog defaultServerPort -> + "[STORE_LOG]\n\ + \# The server uses STM memory for persistence,\n\ + \# that will be lost on restart (e.g., as with redis).\n\ + \# This option enables saving memory to append only log,\n\ + \# and restoring it when the server is started.\n\ + \# Log is compacted on start (deleted objects are removed).\n\ + \enable: " + <> (if enableStoreLog then "on" else "off") + <> "\n\ + \log_stats: off\n\n\ + \[TRANSPORT]\n\ + \port: " + <> defaultServerPort + <> "\n\ + \websockets: off\n", + mkServerConfig = \storeLogFile transports ini -> + let settingIsOn section name = if lookupValue section name ini == Right "on" then Just () else Nothing + logStats = settingIsOn "STORE_LOG" "log_stats" + in NtfServerConfig + { transports, + subIdBytes = 24, + regCodeBytes = 32, + clientQSize = 16, + subQSize = 64, + pushQSize = 128, + smpAgentCfg = defaultSMPClientAgentConfig, + apnsConfig = defaultAPNSPushClientConfig, + inactiveClientExpiration = Nothing, + storeLogFile, + resubscribeDelay = 50000, -- 50ms + caCertificateFile = caCrtFile, + privateKeyFile = serverKeyFile, + certificateFile = serverCrtFile, + logStatsInterval = logStats $> 86400, -- seconds + logStatsStartTime = 0, -- seconds from 00:00 UTC + serverStatsLogFile = combine logPath "ntf-server-stats.daily.log", + serverStatsBackupFile = logStats $> combine logPath "ntf-server-stats.log" + } + } diff --git a/src/Simplex/Messaging/Server/Main.hs b/src/Simplex/Messaging/Server/Main.hs new file mode 100644 index 000000000..a2633427c --- /dev/null +++ b/src/Simplex/Messaging/Server/Main.hs @@ -0,0 +1,124 @@ +{-# LANGUAGE DuplicateRecordFields #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} + +module Simplex.Messaging.Server.Main where + +import Data.Functor (($>)) +import Data.Ini (lookupValue) +import Data.Maybe (fromMaybe) +import qualified Data.Text as T +import Data.Text.Encoding (encodeUtf8) +import Simplex.Messaging.Encoding.String +import Simplex.Messaging.Server (runSMPServer) +import Simplex.Messaging.Server.CLI (ServerCLIConfig (..), protocolServerCLI, readStrictIni) +import Simplex.Messaging.Server.Env.STM (ServerConfig (..), defaultInactiveClientExpiration, defaultMessageExpiration) +import Simplex.Messaging.Server.Expiration +import Simplex.Messaging.Transport (simplexMQVersion, supportedSMPServerVRange) +import System.FilePath (combine) + +smpServerCLI :: FilePath -> FilePath -> IO () +smpServerCLI cfgPath logPath = protocolServerCLI (smpServerCLIConfig cfgPath logPath) $ \cfg@ServerConfig {inactiveClientExpiration} -> do + putStrLn $ case inactiveClientExpiration of + Just ExpirationConfig {ttl, checkInterval} -> "expiring clients inactive for " <> show ttl <> " seconds every " <> show checkInterval <> " seconds" + _ -> "not expiring inactive clients" + putStrLn $ + "creating new queues " + <> if allowNewQueues cfg + then maybe "allowed" (const "requires basic auth") $ newQueueBasicAuth cfg + else "NOT allowed" + runSMPServer cfg + +smpServerCLIConfig :: FilePath -> FilePath -> ServerCLIConfig ServerConfig +smpServerCLIConfig cfgPath logPath = + let caCrtFile = combine cfgPath "ca.crt" + serverKeyFile = combine cfgPath "server.key" + serverCrtFile = combine cfgPath "server.crt" + in ServerCLIConfig + { cfgDir = cfgPath, + logDir = logPath, + iniFile = combine cfgPath "smp-server.ini", + storeLogFile = combine logPath "smp-server-store.log", + caKeyFile = combine cfgPath "ca.key", + caCrtFile, + serverKeyFile, + serverCrtFile, + fingerprintFile = combine cfgPath "fingerprint", + defaultServerPort = "5223", + executableName = "smp-server", + serverVersion = "SMP server v" <> simplexMQVersion, + mkIniFile = \enableStoreLog defaultServerPort -> + "[STORE_LOG]\n\ + \# The server uses STM memory for persistence,\n\ + \# that will be lost on restart (e.g., as with redis).\n\ + \# This option enables saving memory to append only log,\n\ + \# and restoring it when the server is started.\n\ + \# Log is compacted on start (deleted objects are removed).\n" + <> ("enable: " <> (if enableStoreLog then "on" else "off") <> "\n") + <> "# Undelivered messages are optionally saved and restored when the server restarts,\n\ + \# they are preserved in the .bak file until the next restart.\n" + <> ("restore_messages: " <> (if enableStoreLog then "on" else "off") <> "\n") + <> "log_stats: off\n\n" + <> "[AUTH]\n" + <> "# Set new_queues option to off to completely prohibit creating new messaging queues.\n" + <> "# This can be useful when you want to decommission the server, but not all connections are switched yet.\n" + <> "new_queues: on\n\n" + <> "# Use create_password option to enable basic auth to create new messaging queues.\n" + <> "# The password should be used as part of server address in client configuration:\n" + <> "# smp://fingerprint:password@host1,host2\n" + <> "# The password will not be shared with the connecting contacts, you must share it only\n" + <> "# with the users who you want to allow creating messaging queues on your server.\n" + <> "# create_password: password to create new queues (any printable ASCII characters without whitespace, '@', ':' and '/')\n\n" + <> "[TRANSPORT]\n" + <> ("port: " <> defaultServerPort <> "\n") + <> "websockets: off\n\n" + <> "[INACTIVE_CLIENTS]\n\ + \# TTL and interval to check inactive clients\n\ + \disconnect: off\n" + <> ("# ttl: " <> show (ttl defaultInactiveClientExpiration) <> "\n") + <> ("# check_interval: " <> show (checkInterval defaultInactiveClientExpiration) <> "\n"), + mkServerConfig = \storeLogFile transports ini -> + let onOff section name = case lookupValue section name ini of + Right "on" -> Just True + Right "off" -> Just False + Right s -> error . T.unpack $ "invalid INI setting " <> name <> ": " <> s + _ -> Nothing + settingIsOn section name = if onOff section name == Just True then Just () else Nothing + logStats = settingIsOn "STORE_LOG" "log_stats" + in ServerConfig + { transports, + tbqSize = 16, + serverTbqSize = 64, + msgQueueQuota = 128, + queueIdBytes = 24, + msgIdBytes = 24, -- must be at least 24 bytes, it is used as 192-bit nonce for XSalsa20 + caCertificateFile = caCrtFile, + privateKeyFile = serverKeyFile, + certificateFile = serverCrtFile, + storeLogFile, + storeMsgsFile = + let messagesPath = combine logPath "smp-server-messages.log" + in case onOff "STORE_LOG" "restore_messages" of + Just True -> Just messagesPath + Just False -> Nothing + -- if the setting is not set, it is enabled when store log is enabled + _ -> storeLogFile $> messagesPath, + -- allow creating new queues by default + allowNewQueues = fromMaybe True $ onOff "AUTH" "new_queues", + newQueueBasicAuth = case lookupValue "AUTH" "create_password" ini of + Right auth -> either error Just . strDecode $ encodeUtf8 auth + _ -> Nothing, + messageExpiration = Just defaultMessageExpiration, + inactiveClientExpiration = + settingIsOn "INACTIVE_CLIENTS" "disconnect" + $> ExpirationConfig + { ttl = readStrictIni "INACTIVE_CLIENTS" "ttl" ini, + checkInterval = readStrictIni "INACTIVE_CLIENTS" "check_interval" ini + }, + logStatsInterval = logStats $> 86400, -- seconds + logStatsStartTime = 0, -- seconds from 00:00 UTC + serverStatsLogFile = combine logPath "smp-server-stats.daily.log", + serverStatsBackupFile = logStats $> combine logPath "smp-server-stats.log", + smpServerVRange = supportedSMPServerVRange + } + } diff --git a/tests/CLITests.hs b/tests/CLITests.hs new file mode 100644 index 000000000..4dcf3f027 --- /dev/null +++ b/tests/CLITests.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE OverloadedStrings #-} + +module CLITests where + +import Data.Ini (lookupValue, readIniFile) +import Data.List (isPrefixOf) +import Simplex.Messaging.Notifications.Server.Main +import Simplex.Messaging.Server.Main +import Simplex.Messaging.Util (catchAll_) +import System.Directory (doesFileExist) +import System.Environment (withArgs) +import System.IO.Silently (capture_) +import System.Timeout (timeout) +import Test.Hspec +import Test.Main (withStdin) + +cfgPath :: FilePath +cfgPath = "tests/tmp/cli/etc/opt/simplex" + +logPath :: FilePath +logPath = "tests/tmp/cli/etc/var/simplex" + +ntfCfgPath :: FilePath +ntfCfgPath = "tests/tmp/cli/etc/opt/simplex-notifications" + +ntfLogPath :: FilePath +ntfLogPath = "tests/tmp/cli/etc/var/simplex-notifications" + +cliTests :: Spec +cliTests = do + describe "SMP server CLI" $ do + it "should initialize, start and delete the server (no store log)" $ smpServerTest False + it "should initialize, start and delete the server (with store log)" $ smpServerTest True + describe "Ntf server CLI" $ do + it "should initialize, start and delete the server (no store log)" $ ntfServerTest False + it "should initialize, start and delete the server (with store log)" $ ntfServerTest True + +smpServerTest :: Bool -> IO () +smpServerTest enableStoreLog = do + capture_ (withArgs (["init"] <> ["-l" | enableStoreLog]) $ smpServerCLI cfgPath logPath) + >>= (`shouldSatisfy` (("Server initialized, you can modify configuration in " <> cfgPath <> "/smp-server.ini") `isPrefixOf`)) + Right ini <- readIniFile $ cfgPath <> "/smp-server.ini" + lookupValue "STORE_LOG" "enable" ini `shouldBe` Right (if enableStoreLog then "on" else "off") + lookupValue "STORE_LOG" "log_stats" ini `shouldBe` Right "off" + lookupValue "TRANSPORT" "port" ini `shouldBe` Right "5223" + lookupValue "TRANSPORT" "websockets" ini `shouldBe` Right "off" + lookupValue "AUTH" "new_queues" ini `shouldBe` Right "on" + lookupValue "INACTIVE_CLIENTS" "disconnect" ini `shouldBe` Right "off" + doesFileExist (cfgPath <> "/ca.key") `shouldReturn` True + r <- lines <$> capture_ (withArgs ["start"] $ (100000 `timeout` smpServerCLI cfgPath logPath) `catchAll_` pure (Just ())) + r `shouldContain` ["SMP server v3.4.0"] + r `shouldContain` (if enableStoreLog then ["Store log: " <> logPath <> "/smp-server-store.log"] else ["Store log disabled."]) + r `shouldContain` ["Listening on port 5223 (TLS)..."] + r `shouldContain` ["not expiring inactive clients"] + r `shouldContain` ["creating new queues allowed"] + capture_ (withStdin "Y" . withArgs ["delete"] $ smpServerCLI cfgPath logPath) + >>= (`shouldSatisfy` ("WARNING: deleting the server will make all queues inaccessible" `isPrefixOf`)) + doesFileExist (cfgPath <> "/ca.key") `shouldReturn` False + +ntfServerTest :: Bool -> IO () +ntfServerTest enableStoreLog = do + capture_ (withArgs (["init"] <> ["-l" | enableStoreLog]) $ ntfServerCLI ntfCfgPath ntfLogPath) + >>= (`shouldSatisfy` (("Server initialized, you can modify configuration in " <> ntfCfgPath <> "/ntf-server.ini") `isPrefixOf`)) + Right ini <- readIniFile $ ntfCfgPath <> "/ntf-server.ini" + lookupValue "STORE_LOG" "enable" ini `shouldBe` Right (if enableStoreLog then "on" else "off") + lookupValue "STORE_LOG" "log_stats" ini `shouldBe` Right "off" + lookupValue "TRANSPORT" "port" ini `shouldBe` Right "443" + lookupValue "TRANSPORT" "websockets" ini `shouldBe` Right "off" + doesFileExist (ntfCfgPath <> "/ca.key") `shouldReturn` True + r <- lines <$> capture_ (withArgs ["start"] $ (100000 `timeout` ntfServerCLI ntfCfgPath ntfLogPath) `catchAll_` pure (Just ())) + r `shouldContain` ["SMP notifications server v1.2.0"] + r `shouldContain` (if enableStoreLog then ["Store log: " <> ntfLogPath <> "/ntf-server-store.log"] else ["Store log disabled."]) + r `shouldContain` ["Listening on port 443 (TLS)..."] + capture_ (withStdin "Y" . withArgs ["delete"] $ ntfServerCLI ntfCfgPath ntfLogPath) + >>= (`shouldSatisfy` ("WARNING: deleting the server will make all queues inaccessible" `isPrefixOf`)) + doesFileExist (cfgPath <> "/ca.key") `shouldReturn` False diff --git a/tests/Test.hs b/tests/Test.hs index d3c52283c..fbf8a4af6 100644 --- a/tests/Test.hs +++ b/tests/Test.hs @@ -2,6 +2,7 @@ import AgentTests (agentTests) -- import Control.Logger.Simple +import CLITests import CoreTests.CryptoTests import CoreTests.EncodingTests import CoreTests.ProtocolErrorTests @@ -34,4 +35,5 @@ main = do describe "SMP server via WebSockets" $ serverTests (transport @WS) describe "Notifications server" $ ntfServerTests (transport @TLS) describe "SMP client agent" $ agentTests (transport @TLS) + describe "Server CLIs" cliTests removeDirectoryRecursive "tests/tmp"