2023-08-31 17:06:44 +01:00
|
|
|
package syncv3_test
|
|
|
|
|
|
|
|
import (
|
2023-09-07 18:34:11 +01:00
|
|
|
"fmt"
|
2023-08-31 17:06:44 +01:00
|
|
|
"testing"
|
2023-09-11 20:16:23 +01:00
|
|
|
"time"
|
2023-08-31 17:06:44 +01:00
|
|
|
|
2023-10-11 12:23:46 +01:00
|
|
|
"github.com/matrix-org/complement/b"
|
|
|
|
"github.com/matrix-org/complement/client"
|
2023-08-31 17:06:44 +01:00
|
|
|
"github.com/matrix-org/sliding-sync/sync3"
|
|
|
|
"github.com/matrix-org/sliding-sync/testutils/m"
|
2023-09-07 10:16:00 +01:00
|
|
|
"github.com/tidwall/gjson"
|
2023-08-31 17:06:44 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestRedactionsAreRedactedWherePossible(t *testing.T) {
|
|
|
|
alice := registerNamedUser(t, "alice")
|
2023-10-11 12:23:46 +01:00
|
|
|
room := alice.MustCreateRoom(t, map[string]any{"preset": "public_chat"})
|
2023-08-31 17:06:44 +01:00
|
|
|
|
2023-10-11 12:23:46 +01:00
|
|
|
eventID := alice.SendEventSynced(t, room, b.Event{
|
2023-08-31 17:06:44 +01:00
|
|
|
Type: "m.room.message",
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"msgtype": "m.text",
|
|
|
|
"body": "I will be redacted",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
res := alice.SlidingSync(t, sync3.Request{
|
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
RoomSubscription: sync3.RoomSubscription{
|
|
|
|
TimelineLimit: 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
m.MatchResponse(t, res, m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
room: {MatchRoomTimelineMostRecent(1, []Event{{ID: eventID, Content: map[string]interface{}{
|
|
|
|
"msgtype": "m.text",
|
|
|
|
"body": "I will be redacted",
|
|
|
|
}}})},
|
|
|
|
}))
|
|
|
|
|
|
|
|
// redact the event
|
2023-10-11 12:23:46 +01:00
|
|
|
redactionEventID := alice.MustSendRedaction(t, room, map[string]interface{}{}, eventID)
|
2023-08-31 17:06:44 +01:00
|
|
|
|
|
|
|
// see the redaction
|
|
|
|
alice.SlidingSyncUntilEventID(t, res.Pos, room, redactionEventID)
|
|
|
|
|
|
|
|
// now resync from scratch, the event should be redacted this time around.
|
|
|
|
res = alice.SlidingSync(t, sync3.Request{
|
|
|
|
Lists: map[string]sync3.RequestList{
|
|
|
|
"a": {
|
|
|
|
Ranges: sync3.SliceRanges{{0, 20}},
|
|
|
|
RoomSubscription: sync3.RoomSubscription{
|
|
|
|
TimelineLimit: 2,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
m.MatchResponse(t, res, m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
|
|
|
|
room: {MatchRoomTimelineMostRecent(2, []Event{
|
|
|
|
{ID: eventID, Content: map[string]interface{}{}},
|
|
|
|
{ID: redactionEventID},
|
|
|
|
})},
|
|
|
|
}))
|
2023-09-07 10:16:00 +01:00
|
|
|
// introspect the unsigned key a bit more, we don't know all the fields so can't use a matcher
|
|
|
|
gotEvent := gjson.ParseBytes(res.Rooms[room].Timeline[len(res.Rooms[room].Timeline)-2])
|
|
|
|
redactedBecause := gotEvent.Get("unsigned.redacted_because")
|
|
|
|
if !redactedBecause.Exists() {
|
|
|
|
t.Fatalf("unsigned.redacted_because must exist, but it doesn't. Got: %v", gotEvent.Raw)
|
|
|
|
}
|
|
|
|
// assert basic fields
|
|
|
|
assertEqual(t, "event_id mismatch", redactedBecause.Get("event_id").Str, redactionEventID)
|
|
|
|
assertEqual(t, "sender mismatch", redactedBecause.Get("sender").Str, alice.UserID)
|
|
|
|
assertEqual(t, "type mismatch", redactedBecause.Get("type").Str, "m.room.redaction")
|
|
|
|
if !redactedBecause.Get("content").Exists() {
|
|
|
|
t.Fatalf("unsigned.redacted_because.content must exist, but it doesn't. Got: %v", gotEvent.Raw)
|
|
|
|
}
|
2023-08-31 17:06:44 +01:00
|
|
|
|
|
|
|
}
|
2023-09-07 18:34:11 +01:00
|
|
|
|
2023-09-11 20:16:23 +01:00
|
|
|
func TestRedactingRoomStateIsReflectedInNextSync(t *testing.T) {
|
2023-09-07 18:34:11 +01:00
|
|
|
alice := registerNamedUser(t, "alice")
|
2023-09-11 20:16:23 +01:00
|
|
|
bob := registerNamedUser(t, "bob")
|
2023-09-07 18:34:11 +01:00
|
|
|
|
2023-09-11 20:16:23 +01:00
|
|
|
t.Log("Alice creates a room, then sets a room alias and name.")
|
2023-10-11 12:23:46 +01:00
|
|
|
room := alice.MustCreateRoom(t, map[string]any{
|
2023-09-11 20:16:23 +01:00
|
|
|
"preset": "public_chat",
|
2023-09-08 18:16:52 +01:00
|
|
|
})
|
2023-09-11 20:16:23 +01:00
|
|
|
|
|
|
|
alias := fmt.Sprintf("#%s-%d:%s", t.Name(), time.Now().Unix(), alice.Domain)
|
2023-10-11 12:23:46 +01:00
|
|
|
alice.MustDo(t, "PUT", []string{"_matrix", "client", "v3", "directory", "room", alias},
|
|
|
|
client.WithJSONBody(t, map[string]any{"room_id": room}),
|
2023-09-11 20:16:23 +01:00
|
|
|
)
|
2023-10-11 12:23:46 +01:00
|
|
|
aliasID := alice.Unsafe_SendEventUnsynced(t, room, b.Event{
|
|
|
|
Type: "m.room.canonical_alias",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"alias": alias,
|
|
|
|
},
|
2023-09-11 20:16:23 +01:00
|
|
|
})
|
|
|
|
|
2023-09-07 18:34:11 +01:00
|
|
|
const naughty = "naughty room for naughty people"
|
2023-10-11 12:23:46 +01:00
|
|
|
nameID := alice.Unsafe_SendEventUnsynced(t, room, b.Event{
|
|
|
|
Type: "m.room.name",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"name": naughty,
|
|
|
|
},
|
2023-09-07 18:34:11 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Log("Alice sliding syncs, subscribing to that room explicitly.")
|
|
|
|
res := alice.SlidingSync(t, sync3.Request{
|
|
|
|
RoomSubscriptions: map[string]sync3.RoomSubscription{
|
|
|
|
room: {
|
|
|
|
TimelineLimit: 20,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Log("Alice should see her room appear with its name.")
|
|
|
|
m.MatchResponse(t, res, m.MatchRoomSubscription(room, m.MatchRoomName(naughty)))
|
|
|
|
|
|
|
|
t.Log("Alice redacts the room name.")
|
2023-10-11 12:23:46 +01:00
|
|
|
redactionID := alice.MustSendRedaction(t, room, map[string]interface{}{}, nameID)
|
2023-09-07 18:34:11 +01:00
|
|
|
|
|
|
|
t.Log("Alice syncs until she sees her redaction.")
|
2023-09-11 20:16:23 +01:00
|
|
|
res = alice.SlidingSyncUntil(t, res.Pos, sync3.Request{}, m.MatchRoomSubscription(
|
|
|
|
room,
|
|
|
|
MatchRoomTimelineMostRecent(1, []Event{{ID: redactionID}}),
|
|
|
|
))
|
2023-09-07 18:34:11 +01:00
|
|
|
|
|
|
|
t.Log("The room name should have been redacted, falling back to the canonical alias.")
|
|
|
|
m.MatchResponse(t, res, m.MatchRoomSubscription(room, m.MatchRoomName(alias)))
|
2023-09-11 20:16:23 +01:00
|
|
|
|
|
|
|
t.Log("Alice sets a room avatar.")
|
|
|
|
avatarURL := alice.UploadContent(t, smallPNG, "avatar.png", "image/png")
|
2023-10-11 12:23:46 +01:00
|
|
|
avatarID := alice.Unsafe_SendEventUnsynced(t, room, b.Event{
|
|
|
|
Type: "m.room.avatar",
|
|
|
|
StateKey: ptr(""),
|
|
|
|
Content: map[string]interface{}{
|
|
|
|
"url": avatarURL,
|
|
|
|
},
|
2023-09-11 20:16:23 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
t.Log("Alice waits to see the avatar.")
|
|
|
|
res = alice.SlidingSyncUntil(t, res.Pos, sync3.Request{}, m.MatchRoomSubscription(room, m.MatchRoomAvatar(avatarURL)))
|
|
|
|
|
|
|
|
t.Log("Alice redacts the avatar.")
|
2023-10-11 12:23:46 +01:00
|
|
|
redactionID = alice.MustSendRedaction(t, room, map[string]interface{}{}, avatarID)
|
2023-09-11 20:16:23 +01:00
|
|
|
|
|
|
|
t.Log("Alice sees the avatar revert to blank.")
|
|
|
|
res = alice.SlidingSyncUntil(t, res.Pos, sync3.Request{}, m.MatchRoomSubscription(room, m.MatchRoomUnsetAvatar()))
|
|
|
|
|
|
|
|
t.Log("Bob joins the room, with a custom displayname.")
|
2023-09-12 12:22:56 +01:00
|
|
|
const bobDisplayName = "bob mortimer"
|
|
|
|
bob.SetDisplayname(t, bobDisplayName)
|
2023-09-11 20:16:23 +01:00
|
|
|
bob.JoinRoom(t, room, nil)
|
|
|
|
|
|
|
|
t.Log("Alice sees Bob join.")
|
|
|
|
res = alice.SlidingSyncUntil(t, res.Pos, sync3.Request{}, m.MatchRoomSubscription(room,
|
|
|
|
MatchRoomTimelineMostRecent(1, []Event{{
|
|
|
|
StateKey: ptr(bob.UserID),
|
|
|
|
Type: "m.room.member",
|
|
|
|
Content: map[string]any{
|
|
|
|
"membership": "join",
|
2023-09-12 12:22:56 +01:00
|
|
|
"displayname": bobDisplayName,
|
2023-09-11 20:16:23 +01:00
|
|
|
},
|
|
|
|
}}),
|
|
|
|
))
|
2023-09-12 12:22:56 +01:00
|
|
|
// Extract Bob's join ID because https://github.com/matrix-org/matrix-spec-proposals/pull/2943 doens't exist grrr
|
|
|
|
timeline := res.Rooms[room].Timeline
|
|
|
|
bobJoinID := gjson.GetBytes(timeline[len(timeline)-1], "event_id").Str
|
2023-09-11 20:16:23 +01:00
|
|
|
|
|
|
|
t.Log("Alice redacts the alias.")
|
2023-10-11 12:23:46 +01:00
|
|
|
redactionID = alice.MustSendRedaction(t, room, map[string]interface{}{}, aliasID)
|
2023-09-11 20:16:23 +01:00
|
|
|
|
2023-09-12 12:22:56 +01:00
|
|
|
t.Log("Alice sees the room name reset to Bob's display name.")
|
|
|
|
res = alice.SlidingSyncUntil(t, res.Pos, sync3.Request{}, m.MatchRoomSubscription(room, m.MatchRoomName(bobDisplayName)))
|
2023-09-11 20:16:23 +01:00
|
|
|
|
|
|
|
t.Log("Bob redacts his membership")
|
2023-10-11 12:23:46 +01:00
|
|
|
redactionID = bob.MustSendRedaction(t, room, map[string]interface{}{}, bobJoinID)
|
2023-09-11 20:16:23 +01:00
|
|
|
|
|
|
|
t.Log("Alice sees the room name reset to Bob's username.")
|
2023-09-12 12:22:56 +01:00
|
|
|
res = alice.SlidingSyncUntil(t, res.Pos, sync3.Request{}, m.MatchRoomSubscription(room, m.MatchRoomName(bob.UserID)))
|
2023-09-07 18:34:11 +01:00
|
|
|
}
|