complement/tests/direct_messaging_test.go
kegsay e719bfb1d3
Make the federation package public (#686)
Marked with EXPERIMENTAL all over as the API may change without warning.
2023-10-26 15:34:45 +01:00

160 lines
5.8 KiB
Go

package tests
import (
"context"
"fmt"
"testing"
"time"
"github.com/matrix-org/complement"
"github.com/matrix-org/complement/client"
"github.com/matrix-org/complement/helpers"
"github.com/matrix-org/complement/federation"
"github.com/matrix-org/complement/match"
"github.com/matrix-org/complement/must"
"github.com/matrix-org/gomatrixserverlib/fclient"
"github.com/matrix-org/gomatrixserverlib/spec"
"github.com/matrix-org/gomatrixserverlib"
"github.com/tidwall/gjson"
)
// Test that a client can write `m.direct` account data and get told about updates to that event.
// Requires a functioning account data implementation.
func TestWriteMDirectAccountData(t *testing.T) {
deployment := complement.Deploy(t, 1)
defer func() {
// additional logging to debug https://github.com/matrix-org/synapse/issues/13334
t.Logf("%s: TestWriteMDirectAccountData complete: destroying HS deployment", time.Now())
deployment.Destroy(t)
}()
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
bob := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
roomID := alice.MustCreateRoom(t, map[string]interface{}{
"invite": []string{bob.UserID},
"is_direct": true,
})
alice.MustSetGlobalAccountData(t, "m.direct", map[string]interface{}{
bob.UserID: []string{roomID},
})
checkAccountData := func(r gjson.Result) bool {
if r.Get("type").Str != "m.direct" {
return false
}
must.MatchGJSON(t, r, match.JSONKeyEqual("content."+client.GjsonEscape(bob.UserID), []string{roomID}))
return true
}
t.Logf("%s: global account data set; syncing until it arrives", time.Now()) // synapse#13334
since := alice.MustSyncUntil(t, client.SyncReq{}, client.SyncGlobalAccountDataHas(checkAccountData))
// now update the DM room and test that incremental syncing also pushes new account data
roomID = alice.MustCreateRoom(t, map[string]interface{}{
"invite": []string{bob.UserID},
"is_direct": true,
})
alice.MustSetGlobalAccountData(t, "m.direct", map[string]interface{}{
bob.UserID: []string{roomID},
})
alice.MustSyncUntil(t, client.SyncReq{Since: since}, client.SyncGlobalAccountDataHas(checkAccountData))
// check that manually GETing the account data also works with the new updated value
must.MatchResponse(t, alice.MustGetGlobalAccountData(t, "m.direct"), match.HTTPResponse{
StatusCode: 200,
JSON: []match.JSON{
match.JSONKeyEqual(client.GjsonEscape(bob.UserID), []interface{}{roomID}),
},
})
}
// Test that the `is_direct` flag on m.room.member invites propagate to the target user. Both users
// are on the same homeserver.
func TestIsDirectFlagLocal(t *testing.T) {
deployment := complement.Deploy(t, 1)
defer deployment.Destroy(t)
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
bob := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
roomID := alice.MustCreateRoom(t, map[string]interface{}{
"invite": []string{bob.UserID},
"is_direct": true,
})
bob.MustSyncUntil(t, client.SyncReq{}, func(clientUserID string, topLevelSyncJSON gjson.Result) error {
inviteStateEvents := topLevelSyncJSON.Get("rooms.invite." + client.GjsonEscape(roomID) + ".invite_state.events").Array()
for _, ev := range inviteStateEvents {
if ev.Get("type").Str == "m.room.member" &&
ev.Get("state_key").Str == bob.UserID &&
ev.Get("content.membership").Str == "invite" {
t.Logf("Received invite: %v", ev.Raw)
if !ev.Get("content.is_direct").Exists() {
t.Logf("missing is_direct flag")
return fmt.Errorf("invite exists but missing is_direct")
}
if ev.Get("content.is_direct").Bool() {
return nil
}
return fmt.Errorf("is_direct is not true")
}
}
return fmt.Errorf("missing invite event")
})
}
// Test that the `is_direct` flag on m.room.member invites propagate to the target user. Users
// are on different homeservers.
func TestIsDirectFlagFederation(t *testing.T) {
deployment := complement.Deploy(t, 1)
defer deployment.Destroy(t)
srv := federation.NewServer(t, deployment,
federation.HandleKeyRequests(),
federation.HandleMakeSendJoinRequests(),
federation.HandleTransactionRequests(nil, nil),
)
srv.UnexpectedRequestsAreErrors = false // we expect to be pushed events
cancel := srv.Listen()
defer cancel()
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
roomVer := alice.GetDefaultRoomVersion(t)
bob := srv.UserID("bob")
room := srv.MustMakeRoom(t, roomVer, federation.InitialRoomEvents(roomVer, bob))
dmInviteEvent := srv.MustCreateEvent(t, room, federation.Event{
Type: "m.room.member",
StateKey: &alice.UserID,
Sender: bob,
Content: map[string]interface{}{
"membership": "invite",
"is_direct": true,
},
})
inviteReq, err := fclient.NewInviteV2Request(dmInviteEvent, []gomatrixserverlib.InviteStrippedState{})
if err != nil {
t.Fatalf("failed to make invite request: %s", err)
}
_, since := alice.MustSync(t, client.SyncReq{})
_, err = srv.FederationClient(deployment).SendInviteV2(context.Background(), spec.ServerName(srv.ServerName()), "hs1", inviteReq)
if err != nil {
t.Fatalf("failed to send invite v2: %s", err)
}
alice.MustSyncUntil(t, client.SyncReq{Since: since}, func(clientUserID string, topLevelSyncJSON gjson.Result) error {
inviteStateEvents := topLevelSyncJSON.Get("rooms.invite." + client.GjsonEscape(room.RoomID) + ".invite_state.events").Array()
for _, ev := range inviteStateEvents {
if ev.Get("type").Str == "m.room.member" &&
ev.Get("state_key").Str == alice.UserID &&
ev.Get("content.membership").Str == "invite" {
t.Logf("Received invite: %v", ev.Raw)
if !ev.Get("content.is_direct").Exists() {
t.Logf("missing is_direct flag")
return fmt.Errorf("invite exists but missing is_direct")
}
if ev.Get("content.is_direct").Bool() {
return nil
}
return fmt.Errorf("is_direct is not true")
}
}
return fmt.Errorf("missing invite event")
})
}