graphql-engine/server/lib/pg-client/test/Spec.hs
Tom Harding cafd9cf9ae Add URI parsing functions to pg-client
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9746
GitOrigin-RevId: 1907b00e4cb1d05bf7f5dd7bb431a58863eb5168
2023-07-04 10:49:29 +00:00

171 lines
4.9 KiB
Haskell

{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use withAsync" #-}
{-# HLINT ignore "Use sleep" #-}
module Main (main) where
-------------------------------------------------------------------------------
import Control.Concurrent (forkIO, threadDelay)
import Control.Monad.Trans.Class (MonadTrans (lift))
import Control.Monad.Trans.Except (runExceptT)
import Data.ByteString.Char8 qualified as BS
import Data.String (fromString)
import Database.PG.Query
import Interrupt (specInterrupt)
import Jsonb (specJsonb)
import System.Environment qualified as Env
import Test.Hspec (describe, hspec, it, shouldBe, shouldReturn)
import Timeout (specTimeout)
import Prelude
-------------------------------------------------------------------------------
{-# ANN module ("HLint: ignore avoid Control.Concurrent.forkIO" :: String) #-}
{-# ANN module ("HLint: ignore avoid Control.Concurrent.threadDelay" :: String) #-}
{- Note [Running tests]
~~~~~~~~~~~~~~~~~~~~~~~
The tests in this module expect a postgres instance running. No setup is
required, these tests do not run any query on the database. The only requirement
is that the environment variable DATABASE_URL is set such that it is a valid
connection string to this instance (e.g.
"postgresql://user:pass@127.0.0.1:5432/instance?sslmode=disable").
TODO: run these tests as part of CI.
-}
main :: IO ()
main = hspec $ do
describe "acquiring connections" do
it "acquire a single available resource" do
simpleTest `shouldReturn` Nothing
it "error when no connections available" do
noConnectionAvailable `shouldReturn` Nothing
it "release and acquire works correctly" do
releaseAndAcquire `shouldReturn` Nothing
it "release and acquire works correctly" do
releaseAndAcquireWithTimeout `shouldReturn` Nothing
it "time out works correctly" do
releaseAndAcquireWithTimeoutNegative `shouldReturn` Nothing
let uriConnDetails :: ConnDetails
uriConnDetails =
CDDatabaseURI $
fromString
"postgresql://user:pass@127.0.0.1:5432/instance?sslmode=disable"
it "parses a host name correctly" do
extractHost uriConnDetails `shouldBe` Just "127.0.0.1"
it "parses connection options correctly" do
extractConnOptions uriConnDetails
`shouldBe` Just
ConnOptions
{ connHost = "127.0.0.1",
connPort = 5432,
connUser = "user",
connPassword = "pass",
connDatabase = "instance",
connOptions = Nothing
}
specInterrupt
specTimeout
specJsonb
mkPool :: IO PGPool
mkPool = do
dbUri <- BS.pack <$> Env.getEnv "DATABASE_URL"
initPGPool (connInfo dbUri) connParams logger
where
connInfo uri =
ConnInfo
{ ciRetries,
ciDetails = mkDetails uri
}
ciRetries = 0
mkDetails = CDDatabaseURI
logger = mempty
connParams = ConnParams 1 1 60 True Nothing (Just 3) False
withFreshPool ::
(FromPGConnErr e) =>
PGPool ->
IO a ->
IO (Either e a)
withFreshPool pool action =
runExceptT
. withConn pool
. const
$ lift action
err :: (Show a) => a -> IO (Maybe String)
err = pure . Just . show
nada :: IO ()
nada = mempty
simpleTest :: IO (Maybe String)
simpleTest = do
pool <- mkPool
withFreshPool pool nada >>= \case
Left (e :: PGExecErr) -> err e
Right _ -> mempty
noConnectionAvailable :: IO (Maybe String)
noConnectionAvailable = do
pool <- mkPool
withFreshPool pool (action pool) >>= \case
Left (e :: PGExecErr) -> err e
Right _ -> mempty
where
action pool =
withFreshPool pool nada >>= \case
Left (_ :: PGExecErr) -> mempty
Right _ -> err "connection acquisition expected to fail"
releaseAndAcquire :: IO (Maybe String)
releaseAndAcquire = do
pool <- mkPool
_ <-
withFreshPool pool nada >>= \case
Left (e :: PGExecErr) -> err e
Right _ -> mempty
withFreshPool pool nada >>= \case
Left (e :: PGExecErr) -> err e
Right _ -> mempty
releaseAndAcquireWithTimeout :: IO (Maybe String)
releaseAndAcquireWithTimeout = do
pool <- mkPool
_ <-
forkIO $
withFreshPool pool (threadDelay 300_000) >>= \case
Left (_ :: PGExecErr) -> error "unexpected error when acquiring connections"
Right _ -> mempty
threadDelay 100_000
withFreshPool pool nada >>= \case
Left (e :: PGExecErr) -> err e
Right _ -> mempty
releaseAndAcquireWithTimeoutNegative :: IO (Maybe String)
releaseAndAcquireWithTimeoutNegative = do
pool <- mkPool
_ <-
forkIO $
withFreshPool pool (threadDelay 10_000_000) >>= \case
Left (_ :: PGExecErr) -> error "unexpected error when acquiring connections"
Right _ -> mempty
threadDelay 1_000_000
withFreshPool pool nada >>= \case
Left (_ :: PGExecErr) -> mempty
Right _ -> err "Wat"