Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test sending receipts on events related to the root of a thread. #501

Merged
merged 1 commit into from
Oct 27, 2022
Merged
Changes from all commits
Commits
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
99 changes: 84 additions & 15 deletions tests/csapi/thread_notifications_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,15 @@ func syncHasThreadedReadReceipt(roomID, userID, eventID, threadID string) client
//
// This sends four messages as alice creating a timeline like:
//
// A<--B<--C [thread]
// A<--B<--C<--E [m.thread to A]
// ^
// +---D [main timeline]
// |
// +---D [main timeline]
// |
// +<--F [m.reference to A]
//
// |
// +<--G [m.annotation to F]
//
// Where C and D generate highlight notifications.
//
Expand Down Expand Up @@ -121,6 +127,43 @@ func TestThreadedReceipts(t *testing.T) {
},
})

// Send another event in the thread created above.
alice.SendEventSynced(t, roomID, b.Event{
Type: "m.room.message",
Content: map[string]interface{}{
"msgtype": "m.text",
"body": "End thread",
"m.relates_to": map[string]interface{}{
"event_id": eventA,
"rel_type": "m.thread",
},
},
})

// Create another event related to the root event via a reference (i.e. but
// not via a thread). Then create an event which is an annotation to it.
eventF := alice.SendEventSynced(t, roomID, b.Event{
Type: "m.room.message",
Content: map[string]interface{}{
"msgtype": "m.text",
"body": "Reference!",
"m.relates_to": map[string]interface{}{
"event_id": eventA,
"rel_type": "m.reference",
},
},
})
eventG := alice.SendEventSynced(t, roomID, b.Event{
Type: "m.room.reaction",
Content: map[string]interface{}{
"m.relates_to": map[string]interface{}{
"event_id": eventF,
"rel_type": "m.annotation",
"key": "test",
},
},
})

// A filter to get thread notifications.
threadFilter := `{"room":{"timeline":{"unread_thread_notifications":true}}}`

Expand All @@ -132,7 +175,7 @@ func TestThreadedReceipts(t *testing.T) {
return r.Get("event_id").Str == eventD
}),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
return r.Get("highlight_count").Num == 2 && r.Get("notification_count").Num == 4 && !t.Exists()
return r.Get("highlight_count").Num == 2 && r.Get("notification_count").Num == 6 && !t.Exists()
}),
)
bob.MustSyncUntil(
Expand All @@ -143,8 +186,8 @@ func TestThreadedReceipts(t *testing.T) {
}),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
threadNotifications := t.Get(client.GjsonEscape(eventA))
return r.Get("highlight_count").Num == 1 && r.Get("notification_count").Num == 2 &&
threadNotifications.Get("highlight_count").Num == 1 && threadNotifications.Get("notification_count").Num == 2
return r.Get("highlight_count").Num == 1 && r.Get("notification_count").Num == 3 &&
threadNotifications.Get("highlight_count").Num == 1 && threadNotifications.Get("notification_count").Num == 3
}),
)

Expand All @@ -159,7 +202,7 @@ func TestThreadedReceipts(t *testing.T) {
}),
syncHasThreadedReadReceipt(roomID, bob.UserID, eventA, "main"),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
return r.Get("highlight_count").Num == 2 && r.Get("notification_count").Num == 3 && !t.Exists()
return r.Get("highlight_count").Num == 2 && r.Get("notification_count").Num == 5 && !t.Exists()
}),
)
bob.MustSyncUntil(
Expand All @@ -171,8 +214,8 @@ func TestThreadedReceipts(t *testing.T) {
syncHasThreadedReadReceipt(roomID, bob.UserID, eventA, "main"),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
threadNotifications := t.Get(client.GjsonEscape(eventA))
return r.Get("highlight_count").Num == 1 && r.Get("notification_count").Num == 1 &&
threadNotifications.Get("highlight_count").Num == 1 && threadNotifications.Get("notification_count").Num == 2
return r.Get("highlight_count").Num == 1 && r.Get("notification_count").Num == 2 &&
threadNotifications.Get("highlight_count").Num == 1 && threadNotifications.Get("notification_count").Num == 3
}),
)

Expand All @@ -187,7 +230,7 @@ func TestThreadedReceipts(t *testing.T) {
syncHasThreadedReadReceipt(roomID, bob.UserID, eventA, "main"),
syncHasThreadedReadReceipt(roomID, bob.UserID, eventB, eventA),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
return r.Get("highlight_count").Num == 2 && r.Get("notification_count").Num == 2 && !t.Exists()
return r.Get("highlight_count").Num == 2 && r.Get("notification_count").Num == 4 && !t.Exists()
}),
)
bob.MustSyncUntil(
Expand All @@ -200,13 +243,12 @@ func TestThreadedReceipts(t *testing.T) {
syncHasThreadedReadReceipt(roomID, bob.UserID, eventB, eventA),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
threadNotifications := t.Get(client.GjsonEscape(eventA))
return r.Get("highlight_count").Num == 1 && r.Get("notification_count").Num == 1 &&
threadNotifications.Get("highlight_count").Num == 1 && threadNotifications.Get("notification_count").Num == 1
return r.Get("highlight_count").Num == 1 && r.Get("notification_count").Num == 2 &&
threadNotifications.Get("highlight_count").Num == 1 && threadNotifications.Get("notification_count").Num == 2
}),
)

// Mark the entire room as read by sending an unthreaded read receipt on the last
// event. This clears all notification counts.
// Use an unthreaded receipt to mark the second thread event and an unthreaded event as read.
bob.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "receipt", "m.read", eventD}, client.WithJSONBody(t, struct{}{}))
bob.MustSyncUntil(
t, client.SyncReq{Since: bobNextBatch},
Expand All @@ -215,7 +257,7 @@ func TestThreadedReceipts(t *testing.T) {
}),
syncHasUnthreadedReadReceipt(roomID, bob.UserID, eventD),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
return r.Get("highlight_count").Num == 0 && r.Get("notification_count").Num == 0 && !t.Exists()
return r.Get("highlight_count").Num == 0 && r.Get("notification_count").Num == 2 && !t.Exists()
}),
)
bob.MustSyncUntil(
Expand All @@ -226,7 +268,34 @@ func TestThreadedReceipts(t *testing.T) {
}),
syncHasUnthreadedReadReceipt(roomID, bob.UserID, eventD),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
return r.Get("highlight_count").Num == 0 && r.Get("notification_count").Num == 0 && !t.Exists()
threadNotifications := t.Get(client.GjsonEscape(eventA))
return r.Get("highlight_count").Num == 0 && r.Get("notification_count").Num == 1 &&
threadNotifications.Get("highlight_count").Num == 0 && threadNotifications.Get("notification_count").Num == 1
}),
)

// Finally, mark the entire thread as read, using the annotation.
//
// Note that this will *not* affect the main timeline.
bob.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "receipt", "m.read", eventG}, client.WithJSONBody(t, map[string]interface{}{"thread_id": eventA}))
bob.MustSyncUntil(
t, client.SyncReq{Since: bobNextBatch},
client.SyncTimelineHas(roomID, func(r gjson.Result) bool {
return r.Get("event_id").Str == eventD
}),
syncHasUnthreadedReadReceipt(roomID, bob.UserID, eventD),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
return r.Get("highlight_count").Num == 0 && r.Get("notification_count").Num == 1 && !t.Exists()
}),
)
bob.MustSyncUntil(
t,
client.SyncReq{Since: bobNextBatch, Filter: threadFilter}, client.SyncTimelineHas(roomID, func(r gjson.Result) bool {
return r.Get("event_id").Str == eventD
}),
syncHasUnthreadedReadReceipt(roomID, bob.UserID, eventD),
syncHasUnreadNotifs(roomID, func(r gjson.Result, t gjson.Result) bool {
return r.Get("highlight_count").Num == 0 && r.Get("notification_count").Num == 1 && !t.Exists()
}),
)
}