Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8e8608d
Formatting
Aug 17, 2023
4090354
Test utilities for changing a conv name
Aug 25, 2023
3bf0a6e
Add a test confirming the bug report
Aug 24, 2023
bda51aa
An action to enqueue notifications concurrently
Aug 24, 2023
f5198b4
Enqueue member removal notification for remotes
Aug 24, 2023
574e1c1
Add a changelog
Aug 24, 2023
5c139fd
Test case formatting
Aug 31, 2023
585f7b4
Migrate test roleUpdateWithRemotesUnavailable
Aug 31, 2023
57e5dd6
Migrate test putReceiptModeWithRemotesOk
Aug 31, 2023
81b95f0
Migrate test putReceiptModeWithRemotesUnavailable
Aug 31, 2023
1dc64a6
Migrate test testRoleUpdateWithRemotesOk
Aug 31, 2023
7509feb
Migrate test roleUpdateRemoteMember
Aug 31, 2023
72b597b
Migrate test putQualifiedConvRenameWithRemotesUnavailable
Aug 31, 2023
a2e84f0
Migrate test putQualifiedConvRenameWithRemotesOk
Sep 1, 2023
7ddacbb
Migrate test deleteLocalMemberConvLocalQualifiedOk
Sep 1, 2023
fb83c25
Migrate test deleteRemoteMemberConvLocalQualifiedOk
Sep 1, 2023
e635647
Migrate test deleteUnavailableRemoteMemberConvLocalQualifiedOk
Sep 1, 2023
67cd3f2
Add the copyright header to a test module
Sep 4, 2023
b0d7027
Move a test utility (allPreds)
Sep 4, 2023
4e3cd8a
Test utility: create a team with members
Sep 4, 2023
a45cc50
Migrate test testAccessUpdateGuestRemoved
Sep 4, 2023
7af9ba8
Migrate test messageTimerChangeWithRemotes
Sep 5, 2023
cb80bb1
Migrate test messageTimerUnavailableRemotes
Sep 5, 2023
6ead07c
Migrate test testAccessUpdateGuestRemovedRemotesUnavailable
Sep 5, 2023
7bfc207
Migrate test accessUpdateWithRemotes
Sep 5, 2023
58cbde0
Migrate test testAddRemoteMember
Sep 5, 2023
e1fa430
Migrate test testDeleteTeamConversationWithRemoteMembers
Sep 5, 2023
9cd6803
Migrate test testDeleteTeamConversationWithUnavailableRemoteMembers
Sep 5, 2023
39ecafc
Move a test utility (assertLeaveNotification)
Sep 5, 2023
77ff14e
Migrate test "POST /federation/leave-conversation : Success"
Sep 5, 2023
1a40f5f
Migrate test "POST /federation/on-user-deleted-conversations : Remove…
Sep 14, 2023
ba3d2ad
Migrate test updateConversationByRemoteAdmin
Sep 14, 2023
e1f1806
Tests: support giving a role when adding
Sep 14, 2023
22933f6
Use cannon API for notifications when possible
pcapriotti Sep 18, 2023
df74c2d
Use startDynamicBackends when possible
pcapriotti Sep 18, 2023
7a65e39
Fix assertion
pcapriotti Sep 19, 2023
c1f700e
Migrate test testAddRemoteUsersToLocalConv
pcapriotti Sep 19, 2023
ef170df
Test add member endpoint at version 1
pcapriotti Sep 19, 2023
0aa5670
Add return value to enqueueNotification
pcapriotti Sep 19, 2023
a4bfacb
Use cannon assertions in offline backends test
pcapriotti Sep 19, 2023
91cc244
Check that remote notifications are received
pcapriotti Sep 19, 2023
16b74ff
Test removal of users from unreachable backends
pcapriotti Sep 19, 2023
5049497
Use correct domains for default backends
pcapriotti Sep 20, 2023
93519cf
fixup! Use cannon assertions in offline backends test
pcapriotti Sep 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This fixes a bug where a remote member is removed from a conversation while their backend is unreachable, and the backend does not receive the removal notification once it is reachable again.
2 changes: 2 additions & 0 deletions integration/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
, cql-io
, cryptonite
, data-default
, data-timeout
, directory
, errors
, exceptions
Expand Down Expand Up @@ -86,6 +87,7 @@ mkDerivation {
cql-io
cryptonite
data-default
data-timeout
directory
errors
exceptions
Expand Down
4 changes: 4 additions & 0 deletions integration/integration.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ library
Notifications
RunAllTests
SetupHelpers
Test.AccessUpdate
Test.AssetDownload
Test.B2B
Test.Brig
Expand All @@ -108,8 +109,10 @@ library
Test.Demo
Test.Federation
Test.Federator
Test.MessageTimer
Test.Notifications
Test.Presence
Test.Roles
Test.User
Testlib.App
Testlib.Assertions
Expand Down Expand Up @@ -146,6 +149,7 @@ library
, cql-io
, cryptonite
, data-default
, data-timeout
, directory
, errors
, exceptions
Expand Down
139 changes: 134 additions & 5 deletions integration/test/API/Galley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module API.Galley where
import Control.Lens hiding ((.=))
import Control.Monad.Reader
import Data.Aeson qualified as Aeson
import Data.Aeson.Types qualified as Aeson
import Data.ByteString.Lazy qualified as LBS
import Data.ProtoLens qualified as Proto
import Data.ProtoLens.Labels ()
Expand Down Expand Up @@ -80,6 +81,21 @@ postConversation user cc = do
ccv <- make cc
submit "POST" $ req & addJSON ccv

deleteTeamConversation ::
( HasCallStack,
MakesValue user,
MakesValue conv
) =>
String ->
conv ->
user ->
App Response
deleteTeamConversation tid qcnv user = do
cnv <- snd <$> objQid qcnv
let path = joinHttpPath ["teams", tid, "conversations", cnv]
req <- baseRequest user Galley Versioned path
submit "DELETE" req

putConversationProtocol ::
( HasCallStack,
MakesValue user,
Expand Down Expand Up @@ -217,12 +233,39 @@ getGroupInfo user conv = do
req <- baseRequest user Galley Versioned path
submit "GET" req

addMembers :: (HasCallStack, MakesValue user, MakesValue conv) => user -> conv -> [Value] -> App Response
addMembers usr qcnv newMembers = do
data AddMembers = AddMembers
{ users :: [Value],
role :: Maybe String,
version :: Maybe Int
}

instance Default AddMembers where
def = AddMembers {users = [], role = Nothing, version = Nothing}

addMembers ::
(HasCallStack, MakesValue user, MakesValue conv) =>
user ->
conv ->
AddMembers ->
App Response
addMembers usr qcnv opts = do
(convDomain, convId) <- objQid qcnv
qUsers <- mapM objQidObject newMembers
req <- baseRequest usr Galley Versioned (joinHttpPath ["conversations", convDomain, convId, "members"])
submit "POST" (req & addJSONObject ["qualified_users" .= qUsers])
qUsers <- mapM objQidObject opts.users
let path = case opts.version of
Just v | v <= 1 -> ["conversations", convId, "members", "v2"]
_ -> ["conversations", convDomain, convId, "members"]
req <-
baseRequest
usr
Galley
(maybe Versioned ExplicitVersion opts.version)
(joinHttpPath path)
submit "POST" $
req
& addJSONObject
( ["qualified_users" .= qUsers]
<> ["conversation_role" .= r | r <- toList opts.role]
)

removeMember :: (HasCallStack, MakesValue remover, MakesValue conv, MakesValue removed) => remover -> conv -> removed -> App Response
removeMember remover qcnv removed = do
Expand Down Expand Up @@ -263,3 +306,89 @@ getConversationCode user conv mbZHost = do
& addQueryParams [("cnv", convId)]
& maybe id zHost mbZHost
)

changeConversationName ::
(HasCallStack, MakesValue user, MakesValue conv, MakesValue name) =>
user ->
conv ->
name ->
App Response
changeConversationName user qcnv name = do
(convDomain, convId) <- objQid qcnv
let path = joinHttpPath ["conversations", convDomain, convId, "name"]
nameReq <- make name
req <- baseRequest user Galley Versioned path
submit "PUT" (req & addJSONObject ["name" .= nameReq])

updateRole ::
( HasCallStack,
MakesValue callerUser,
MakesValue targetUser,
MakesValue roleUpdate,
MakesValue qcnv
) =>
callerUser ->
targetUser ->
roleUpdate ->
qcnv ->
App Response
updateRole caller target role qcnv = do
(cnvDomain, cnvId) <- objQid qcnv
(tarDomain, tarId) <- objQid target
roleReq <- make role
req <-
baseRequest
caller
Galley
Versioned
( joinHttpPath ["conversations", cnvDomain, cnvId, "members", tarDomain, tarId]
)
submit "PUT" (req & addJSONObject ["conversation_role" .= roleReq])

updateReceiptMode ::
( HasCallStack,
MakesValue user,
MakesValue conv,
MakesValue mode
) =>
user ->
conv ->
mode ->
App Response
updateReceiptMode user qcnv mode = do
(cnvDomain, cnvId) <- objQid qcnv
modeReq <- make mode
let path = joinHttpPath ["conversations", cnvDomain, cnvId, "receipt-mode"]
req <- baseRequest user Galley Versioned path
submit "PUT" (req & addJSONObject ["receipt_mode" .= modeReq])

updateAccess ::
( HasCallStack,
MakesValue user,
MakesValue conv
) =>
user ->
conv ->
[Aeson.Pair] ->
App Response
updateAccess user qcnv update = do
(cnvDomain, cnvId) <- objQid qcnv
let path = joinHttpPath ["conversations", cnvDomain, cnvId, "access"]
req <- baseRequest user Galley Versioned path
submit "PUT" (req & addJSONObject update)

updateMessageTimer ::
( HasCallStack,
MakesValue user,
MakesValue conv
) =>
user ->
conv ->
Word64 ->
App Response
updateMessageTimer user qcnv update = do
(cnvDomain, cnvId) <- objQid qcnv
updateReq <- make update
let path = joinHttpPath ["conversations", cnvDomain, cnvId, "message-timer"]
req <- baseRequest user Galley Versioned path
submit "PUT" (addJSONObject ["message_timer" .= updateReq] req)
59 changes: 57 additions & 2 deletions integration/test/Notifications.hs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,63 @@ isMemberJoinNotif n = fieldEquals n "payload.0.type" "conversation.member-join"
isConvLeaveNotif :: MakesValue a => a -> App Bool
isConvLeaveNotif n = fieldEquals n "payload.0.type" "conversation.member-leave"

isNotifConv :: (MakesValue conv, MakesValue a) => conv -> a -> App Bool
isNotifConv :: (MakesValue conv, MakesValue a, HasCallStack) => conv -> a -> App Bool
isNotifConv conv n = fieldEquals n "payload.0.qualified_conversation" (objQidObject conv)

isNotifForUser :: (MakesValue user, MakesValue a) => user -> a -> App Bool
isNotifForUser :: (MakesValue user, MakesValue a, HasCallStack) => user -> a -> App Bool
isNotifForUser user n = fieldEquals n "payload.0.data.qualified_user_ids.0" (objQidObject user)

isNotifFromUser :: (MakesValue user, MakesValue a, HasCallStack) => user -> a -> App Bool
isNotifFromUser user n = fieldEquals n "payload.0.qualified_from" (objQidObject user)

isConvNameChangeNotif :: (HasCallStack, MakesValue a) => a -> App Bool
isConvNameChangeNotif n = fieldEquals n "payload.0.type" "conversation.rename"

isMemberUpdateNotif :: (HasCallStack, MakesValue n) => n -> App Bool
isMemberUpdateNotif n = fieldEquals n "payload.0.type" "conversation.member-update"

isReceiptModeUpdateNotif :: (HasCallStack, MakesValue n) => n -> App Bool
isReceiptModeUpdateNotif n =
fieldEquals n "payload.0.type" "conversation.receipt-mode-update"

isConvMsgTimerUpdateNotif :: (HasCallStack, MakesValue n) => n -> App Bool
isConvMsgTimerUpdateNotif n =
fieldEquals n "payload.0.type" "conversation.message-timer-update"

isConvAccessUpdateNotif :: (HasCallStack, MakesValue n) => n -> App Bool
isConvAccessUpdateNotif n =
fieldEquals n "payload.0.type" "conversation.access-update"

isConvCreateNotif :: MakesValue a => a -> App Bool
isConvCreateNotif n = fieldEquals n "payload.0.type" "conversation.create"

isConvDeleteNotif :: MakesValue a => a -> App Bool
isConvDeleteNotif n = fieldEquals n "payload.0.type" "conversation.delete"

assertLeaveNotification ::
( HasCallStack,
MakesValue fromUser,
MakesValue conv,
MakesValue user,
MakesValue kickedUser
) =>
fromUser ->
conv ->
user ->
String ->
kickedUser ->
App ()
assertLeaveNotification fromUser conv user client leaver =
void $
awaitNotification
user
client
noValue
2
( allPreds
[ isConvLeaveNotif,
isNotifConv conv,
isNotifForUser leaver,
isNotifFromUser fromUser
]
)
45 changes: 37 additions & 8 deletions integration/test/SetupHelpers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module SetupHelpers where

import API.Brig qualified as Brig
import API.BrigInternal qualified as Internal
import API.Common
import API.Galley
import Control.Concurrent (threadDelay)
import Control.Monad.Reader
Expand Down Expand Up @@ -32,15 +33,43 @@ deleteUser user = bindResponse (Brig.deleteUser user) $ \resp -> do
resp.status `shouldMatchInt` 200

-- | returns (user, team id)
createTeam :: (HasCallStack, MakesValue domain) => domain -> App (Value, String)
createTeam domain = do
createTeam :: (HasCallStack, MakesValue domain) => domain -> Int -> App (Value, String, [Value])
createTeam domain memberCount = do
res <- Internal.createUser domain def {Internal.team = True}
user <- res.json
tid <- user %. "team" & asString
-- TODO
-- SQS.assertTeamActivate "create team" tid
-- refreshIndex
pure (user, tid)
owner <- res.json
tid <- owner %. "team" & asString
members <- for [2 .. memberCount] $ \_ -> createTeamMember owner tid
pure (owner, tid, members)

createTeamMember ::
(HasCallStack, MakesValue inviter) =>
inviter ->
String ->
App Value
createTeamMember inviter tid = do
newUserEmail <- randomEmail
let invitationJSON = ["role" .= "member", "email" .= newUserEmail]
invitationReq <-
baseRequest inviter Brig Versioned $
joinHttpPath ["teams", tid, "invitations"]
invitation <- getJSON 201 =<< submit "POST" (addJSONObject invitationJSON invitationReq)
invitationId <- objId invitation
invitationCodeReq <-
rawBaseRequest inviter Brig Unversioned "/i/teams/invitation-code"
<&> addQueryParams [("team", tid), ("invitation_id", invitationId)]
invitationCode <- bindResponse (submit "GET" invitationCodeReq) $ \res -> do
res.status `shouldMatchInt` 200
res.json %. "code" & asString
let registerJSON =
[ "name" .= newUserEmail,
"email" .= newUserEmail,
"password" .= defPassword,
"team_code" .= invitationCode
]
registerReq <-
rawBaseRequest inviter Brig Versioned "/register"
<&> addJSONObject registerJSON
getJSON 201 =<< submit "POST" registerReq

connectUsers ::
( HasCallStack,
Expand Down
Loading