Skip to content

Commit

Permalink
unit test module for TeamInvitationSubsystem.
Browse files Browse the repository at this point in the history
  • Loading branch information
fisx committed Jan 14, 2025
1 parent f285fe0 commit 95e205c
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ data TeamInvitationSubsystemError
| TooManyTeamInvitations
| TeamInvitationBlacklistedEmail
| TeamInvitationEmailTaken
deriving (Show)
deriving (Eq, Show)

teamInvitationErrorToHttpError :: TeamInvitationSubsystemError -> HttpError
teamInvitationErrorToHttpError =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ spec = describe "AuthenticationSubsystem.Interpreter" do
mapM_ (uncurry (insertCookie uid)) cookiesWithTTL

createPasswordResetCode (mkEmailKey email)
(_, code) <- expect1ResetPasswordEmail email
resetPassword (PasswordResetEmailIdentity email) code newPassword
(_, resetCode) <- expect1ResetPasswordEmail email
resetPassword (PasswordResetEmailIdentity email) resetCode newPassword

(,) <$> lookupHashedPassword uid <*> listCookies uid
in mPreviousPassword /= Just newPassword ==>
Expand All @@ -121,8 +121,8 @@ spec = describe "AuthenticationSubsystem.Interpreter" do
mapM_ (uncurry (insertCookie uid)) cookiesWithTTL

createPasswordResetCode (mkEmailKey email)
(passwordResetKey, code) <- expect1ResetPasswordEmail email
resetPassword (PasswordResetIdentityKey passwordResetKey) code newPassword
(passwordResetKey, resetCode) <- expect1ResetPasswordEmail email
resetPassword (PasswordResetIdentityKey passwordResetKey) resetCode newPassword

(,) <$> lookupHashedPassword uid <*> listCookies uid
in mPreviousPassword /= Just newPassword ==>
Expand Down Expand Up @@ -189,12 +189,12 @@ spec = describe "AuthenticationSubsystem.Interpreter" do
Right (newPasswordHash, mCaughtException) =
runAllEffects localDomain [user] Nothing $ do
createPasswordResetCode (mkEmailKey email)
(_, code) <- expect1ResetPasswordEmail email
(_, resetCode) <- expect1ResetPasswordEmail email

mCaughtExc <- catchExpectedError $ createPasswordResetCode (mkEmailKey email)

-- Reset password still works with previously generated reset code
resetPassword (PasswordResetEmailIdentity email) code newPassword
resetPassword (PasswordResetEmailIdentity email) resetCode newPassword

(,mCaughtExc) <$> lookupHashedPassword uid
in (fmap (Password.verifyPassword newPassword) newPasswordHash === Just True)
Expand All @@ -214,11 +214,11 @@ spec = describe "AuthenticationSubsystem.Interpreter" do
runAllEffects localDomain [user] Nothing $ do
upsertHashedPassword uid =<< hashPassword oldPassword
createPasswordResetCode (mkEmailKey email)
(_, code) <- expect1ResetPasswordEmail email
(_, resetCode) <- expect1ResetPasswordEmail email

passTime (passwordResetCodeTtl + 1)

mCaughtExc <- catchExpectedError $ resetPassword (PasswordResetEmailIdentity email) code newPassword
mCaughtExc <- catchExpectedError $ resetPassword (PasswordResetEmailIdentity email) resetCode newPassword
(,mCaughtExc) <$> lookupHashedPassword uid
in resetPasswordResult === Just AuthenticationSubsystemInvalidPasswordResetCode
.&&. verifyPasswordProp oldPassword passwordInDB
Expand Down Expand Up @@ -311,7 +311,7 @@ spec = describe "AuthenticationSubsystem.Interpreter" do
runAllEffects localDomain [user] Nothing $ do
void $ createPasswordResetCode (mkEmailKey email)
mLookupRes <- internalLookupPasswordResetCode (mkEmailKey email)
for_ mLookupRes $ \(_, code) -> resetPassword (PasswordResetEmailIdentity email) code newPassword
for_ mLookupRes $ \(_, resetCode) -> resetPassword (PasswordResetEmailIdentity email) resetCode newPassword
lookupHashedPassword uid
in verifyPasswordProp newPassword passwordHashInDB

Expand Down
2 changes: 0 additions & 2 deletions libs/wire-subsystems/test/unit/Wire/MiniBackend.hs
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ import Wire.IndexedUserStore
import Wire.InternalEvent hiding (DeleteUser)
import Wire.InvitationStore
import Wire.MockInterpreters
import Wire.MockInterpreters.ActivationCodeStore (inMemoryActivationCodeStoreInterpreter)
import Wire.MockInterpreters.InvitationStore (inMemoryInvitationStoreInterpreter)
import Wire.PasswordResetCodeStore
import Wire.PasswordStore
import Wire.Sem.Concurrency
Expand Down
6 changes: 5 additions & 1 deletion libs/wire-subsystems/test/unit/Wire/MockInterpreters.hs
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
module Wire.MockInterpreters (module MockInterpreters) where

-- Run this from project root to generate the imports:
-- ls libs/wire-subsystems/test/unit/Wire/MockInterpreters | sed 's|\(.*\)\.hs|import Wire.MockInterpreters.\1 as MockInterpreters|'
-- ls ./MockInterpreters | grep '\.hs' | sed 's|\(.*\)\.hs|import Wire.MockInterpreters.\1 as MockInterpreters|'

import Wire.MockInterpreters.ActivationCodeStore as MockInterpreters
import Wire.MockInterpreters.BlockListStore as MockInterpreters
import Wire.MockInterpreters.DomainRegistrationStore as MockInterpreters
import Wire.MockInterpreters.EmailSubsystem as MockInterpreters
import Wire.MockInterpreters.EnterpriseLoginSubsystem as MockInterpreters
import Wire.MockInterpreters.Error as MockInterpreters
import Wire.MockInterpreters.Events as MockInterpreters
import Wire.MockInterpreters.FederationConfigStore as MockInterpreters
import Wire.MockInterpreters.GalleyAPIAccess as MockInterpreters
import Wire.MockInterpreters.HashPassword as MockInterpreters
import Wire.MockInterpreters.IndexedUserStore as MockInterpreters
import Wire.MockInterpreters.InvitationStore as MockInterpreters
import Wire.MockInterpreters.Now as MockInterpreters
import Wire.MockInterpreters.PasswordResetCodeStore as MockInterpreters
import Wire.MockInterpreters.PasswordStore as MockInterpreters
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data SentMail = SentMail
data SentMailContent = PasswordResetMail PasswordResetPair
deriving (Show, Eq)

-- TODO: rename to 'emailSubsystemInterpreter'
emailSubsystemInterpreter :: (Member (State (Map EmailAddress [SentMail])) r) => InterpreterFor EmailSubsystem r
emailSubsystemInterpreter = interpret \case
SendPasswordResetMail email keyCodePair mLocale -> modify $ Map.insertWith (<>) email [SentMail mLocale $ PasswordResetMail keyCodePair]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Wire.MockInterpreters.EnterpriseLoginSubsystem where

import Imports
import Polysemy
import Wire.EnterpriseLoginSubsystem

-- HINT: This is used to test AuthenticationSubsystem, ...; not to test itself!
enterpriseLoginSubsystemTestInterpreter :: InterpreterFor EnterpriseLoginSubsystem r
enterpriseLoginSubsystemTestInterpreter =
interpret \case
LockDomain _ -> undefined -- :: Domain -> EnterpriseLoginSubsystem m ()
UnlockDomain _ -> undefined -- :: Domain -> EnterpriseLoginSubsystem m ()
PreAuthorizeDomain _ -> undefined -- :: Domain -> EnterpriseLoginSubsystem m ()
UnAuthorizeDomain _ -> undefined -- :: Domain -> EnterpriseLoginSubsystem m ()
UpdateDomainRegistration _ _ -> undefined -- :: Domain -> DomainRegistrationUpdate -> EnterpriseLoginSubsystem m ()
DeleteDomain _ -> undefined -- :: Domain -> EnterpriseLoginSubsystem m ()
GetDomainRegistration _ -> undefined -- :: Domain -> EnterpriseLoginSubsystem m DomainRegistration
GuardEmailDomainRegistrationState _ _ _ -> undefined -- :: InvitationFlow -> TeamId -> EmailAddress -> EnterpriseLoginSubsystem m ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}

module Wire.TeamInvitationSubsystem.InterpreterSpec (spec) where

import Data.Domain
import Data.Id
import Data.Misc (PlainTextPassword8)
import Data.Qualified
import Data.Text.Encoding (decodeUtf8)
import Data.Time
import Imports
import Polysemy
import Polysemy.Error
import Polysemy.Input
import Polysemy.State
import Polysemy.TinyLog
import System.Random (StdGen, mkStdGen)
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck
import Wire.API.Allowlists (AllowlistEmailDomains (AllowlistEmailDomains))
import Wire.API.Password as Password
import Wire.API.User
import Wire.API.User qualified as User
import Wire.API.User.Auth
import Wire.API.User.Password
import Wire.AuthenticationSubsystem
import Wire.AuthenticationSubsystem.Interpreter
import Wire.EmailSubsystem
import Wire.EnterpriseLoginSubsystem
import Wire.GalleyAPIAccess
import Wire.HashPassword
import Wire.InvitationStore
import Wire.MockInterpreters
import Wire.PasswordResetCodeStore
import Wire.PasswordStore
import Wire.Sem.Logger.TinyLog
import Wire.Sem.Now (Now)
import Wire.Sem.Random
import Wire.SessionStore
import Wire.StoredUser
import Wire.TeamInvitationSubsystem
import Wire.TeamInvitationSubsystem.Error
import Wire.TeamInvitationSubsystem.Interpreter
import Wire.UserKeyStore
import Wire.UserStore
import Wire.UserSubsystem

type AllEffects =
[ Error TeamInvitationSubsystemError,
TinyLog,
GalleyAPIAccess,
Random,
State StdGen,
InvitationStore,
State (Map (TeamId, InvitationId) StoredInvitation),
State (Map (InvitationCode) StoredInvitation),
Now,
State UTCTime,
EmailSubsystem,
State (Map EmailAddress [SentMail]),
UserSubsystem,
EnterpriseLoginSubsystem
]

runAllEffects :: Sem AllEffects a -> Either TeamInvitationSubsystemError a
runAllEffects =
run
. enterpriseLoginSubsystemTestInterpreter
. userSubsystemTestInterpreter []
. evalState mempty
. emailSubsystemInterpreter
. evalState defaultTime
. interpretNowAsState
. evalState mempty
. evalState mempty
. inMemoryInvitationStoreInterpreter
. evalState (mkStdGen 3) -- deterministic randomness, good for tests. :)
. randomToStatefulStdGen
. miniGalleyAPIAccess Nothing undefined
. discardTinyLogs
. runError

spec :: Spec
spec = do
describe "InviteUser" $ do
focus . prop "works (TODO: better description pls)" $
\() ->
let cfg =
TeamInvitationSubsystemConfig
{ maxTeamSize = 50,
teamInvitationTimeout = 3_000_000
}
outcome = runAllEffects . runTeamInvitationSubsystem cfg $ do
pure ()
in outcome === Right ()
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Polysemy.State
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck
import Wire.MockInterpreters
import Wire.MockInterpreters hiding (code)
import Wire.Sem.Now
import Wire.Sem.Random
import Wire.VerificationCode
Expand Down
2 changes: 2 additions & 0 deletions libs/wire-subsystems/wire-subsystems.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ test-suite wire-subsystems-tests
Wire.MockInterpreters.BlockListStore
Wire.MockInterpreters.DomainRegistrationStore
Wire.MockInterpreters.EmailSubsystem
Wire.MockInterpreters.EnterpriseLoginSubsystem
Wire.MockInterpreters.Error
Wire.MockInterpreters.Events
Wire.MockInterpreters.FederationConfigStore
Expand All @@ -268,6 +269,7 @@ test-suite wire-subsystems-tests
Wire.MockInterpreters.VerificationCodeStore
Wire.NotificationSubsystem.InterpreterSpec
Wire.PropertySubsystem.InterpreterSpec
Wire.TeamInvitationSubsystem.InterpreterSpec
Wire.UserSearch.TypesSpec
Wire.UserStoreSpec
Wire.UserSubsystem.InterpreterSpec
Expand Down

0 comments on commit 95e205c

Please sign in to comment.