module Test.Migration.Budget (testBudgetMigration) where
import API.Brig (login)
import API.Common (defPassword)
import Control.Monad.Codensity
import Control.Monad.Reader
import qualified Data.ByteString.Char8 as BSChar8
import SetupHelpers
import Testlib.Prelude
import Testlib.ResourcePool
import Text.Read (readMaybe)
testBudgetMigration :: (HasCallStack) => App ()
testBudgetMigration :: HasCallStack => App ()
testBudgetMigration = do
resourcePool <- (Env -> ResourcePool BackendResource)
-> App (ResourcePool BackendResource)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (.resourcePool)
runCodensity (acquireResources 1 resourcePool) $ \[BackendResource
backend] -> do
let domain :: String
domain = BackendResource
backend.berDomain
u1 <-
Codensity App String -> forall b. (String -> App b) -> App b
forall k (m :: k -> *) a.
Codensity m a -> forall (b :: k). (a -> m b) -> m b
runCodensity (HasCallStack =>
BackendResource -> ServiceOverrides -> Codensity App String
BackendResource -> ServiceOverrides -> Codensity App String
startDynamicBackend BackendResource
backend (String -> ServiceOverrides
forall {b}. ToJSON b => b -> ServiceOverrides
conf String
"cassandra")) ((String -> App Value) -> App Value)
-> (String -> App Value) -> App Value
forall a b. (a -> b) -> a -> b
$ \String
_ -> do
u1 <- String -> CreateUser -> App Value
forall domain.
(HasCallStack, MakesValue domain) =>
domain -> CreateUser -> App Value
randomUser String
domain CreateUser
forall a. Default a => a
def
exhaustLogins domain u1
pure u1
u2 <-
runCodensity (startDynamicBackend backend (conf "migration-to-postgresql")) $ \String
_ -> do
HasCallStack => String -> Value -> App ()
String -> Value -> App ()
assertBlocked String
domain Value
u1
u2 <- String -> CreateUser -> App Value
forall domain.
(HasCallStack, MakesValue domain) =>
domain -> CreateUser -> App Value
randomUser String
domain CreateUser
forall a. Default a => a
def
exhaustLogins domain u2
pure u2
runCodensity (startDynamicBackend backend (conf "postgresql")) $ \String
_ -> do
HasCallStack => String -> Value -> App ()
String -> Value -> App ()
assertBlocked String
domain Value
u2
HasCallStack => String -> Value -> App ()
String -> Value -> App ()
assertUnblocked String
domain Value
u1
u3 <- String -> CreateUser -> App Value
forall domain.
(HasCallStack, MakesValue domain) =>
domain -> CreateUser -> App Value
randomUser String
domain CreateUser
forall a. Default a => a
def
exhaustLogins domain u3
where
conf :: b -> ServiceOverrides
conf b
db =
ServiceOverrides
forall a. Default a => a
def
{ brigCfg =
setField @_ @Int "optSettings.setLimitFailedLogins.timeout" 60
>=> setField @_ @Int "optSettings.setPasswordHashingRateLimit.userLimit.inverseRate" 0
>=> setField "postgresMigration.budget" db
}
exhaustLogins :: (HasCallStack) => String -> Value -> App ()
exhaustLogins :: HasCallStack => String -> Value -> App ()
exhaustLogins String
domain Value
u = do
email <- App Value -> App String
forall a. (HasCallStack, MakesValue a) => a -> App String
asString (Value
u Value -> String -> App Value
forall a. (HasCallStack, MakesValue a) => a -> String -> App Value
%. String
"email")
for_ [1 :: Int .. 5] $ \Int
_ ->
String -> String -> String -> App Response
forall domain.
(HasCallStack, MakesValue domain) =>
domain -> String -> String -> App Response
login String
domain String
email String
"wrong-password" App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
`bindResponse` \Response
resp -> do
Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
403
Response
resp.json Maybe Value -> String -> App Value
forall a. (HasCallStack, MakesValue a) => a -> String -> App Value
%. String
"label" App Value -> String -> App ()
forall a b.
(MakesValue a, MakesValue b, HasCallStack) =>
a -> b -> App ()
`shouldMatch` String
"invalid-credentials"
assertBlockedEmail domain email
assertBlocked :: (HasCallStack) => String -> Value -> App ()
assertBlocked :: HasCallStack => String -> Value -> App ()
assertBlocked String
domain Value
u = App Value -> App String
forall a. (HasCallStack, MakesValue a) => a -> App String
asString (Value
u Value -> String -> App Value
forall a. (HasCallStack, MakesValue a) => a -> String -> App Value
%. String
"email") App String -> (String -> App ()) -> App ()
forall a b. App a -> (a -> App b) -> App b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= HasCallStack => String -> String -> App ()
String -> String -> App ()
assertBlockedEmail String
domain
assertBlockedEmail :: (HasCallStack) => String -> String -> App ()
assertBlockedEmail :: HasCallStack => String -> String -> App ()
assertBlockedEmail String
domain String
email =
String -> String -> String -> App Response
forall domain.
(HasCallStack, MakesValue domain) =>
domain -> String -> String -> App Response
login String
domain String
email String
defPassword App Response -> (Response -> App ()) -> App ()
forall a.
HasCallStack =>
App Response -> (Response -> App a) -> App a
`bindResponse` \Response
resp -> do
Response
resp.status Int -> Int -> App ()
forall a. (MakesValue a, HasCallStack) => a -> Int -> App ()
`shouldMatchInt` Int
403
Response
resp.json Maybe Value -> String -> App Value
forall a. (HasCallStack, MakesValue a) => a -> String -> App Value
%. String
"label" App Value -> String -> App ()
forall a b.
(MakesValue a, MakesValue b, HasCallStack) =>
a -> b -> App ()
`shouldMatch` String
"client-error"
let Just (Int
_retryAfter :: Int) = String -> Maybe Int
forall a. Read a => String -> Maybe a
readMaybe (String -> Maybe Int)
-> (ByteString -> String) -> ByteString -> Maybe Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> String
BSChar8.unpack (ByteString -> Maybe Int) -> Maybe ByteString -> Maybe Int
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< HeaderName -> [(HeaderName, ByteString)] -> Maybe ByteString
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup (String -> HeaderName
forall a. IsString a => String -> a
fromString String
"Retry-After") Response
resp.headers
() -> App ()
forall a. a -> App a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
assertUnblocked :: (HasCallStack) => String -> Value -> App ()
assertUnblocked :: HasCallStack => String -> Value -> App ()
assertUnblocked String
domain Value
u = do
email <- App Value -> App String
forall a. (HasCallStack, MakesValue a) => a -> App String
asString (Value
u Value -> String -> App Value
forall a. (HasCallStack, MakesValue a) => a -> String -> App Value
%. String
"email")
login domain email defPassword >>= assertSuccess