Skip to content

Commit 6846473

Browse files
authored
Merge branch 'main' into kegan/lock-create-room
2 parents 966fcc4 + 89b911f commit 6846473

9 files changed

Lines changed: 575 additions & 12 deletions

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ jobs:
4242
- homeserver: Synapse
4343
repo: element-hq/synapse
4444
tags: synapse_blacklist
45-
packages: ./tests/msc3874 ./tests/msc3902
45+
packages: ./tests/msc3874 ./tests/msc3902 ./tests/msc4306
4646
env: "COMPLEMENT_ENABLE_DIRTY_RUNS=1 COMPLEMENT_SHARE_ENV_PREFIX=PASS_ PASS_SYNAPSE_COMPLEMENT_DATABASE=sqlite"
4747
timeout: 20m
4848

client/client.go

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"encoding/json"
99
"fmt"
1010
"io"
11+
"log"
1112
"math/rand"
1213
"net/http"
1314
"net/http/httputil"
@@ -294,12 +295,12 @@ func (c *CSAPI) GetAllPushRules(t ct.TestLike) gjson.Result {
294295
return gjson.ParseBytes(pushRulesBytes)
295296
}
296297

297-
// GetPushRule queries the contents of a client's push rule by scope, kind and rule ID.
298+
// MustGetPushRule queries the contents of a client's push rule by scope, kind and rule ID.
298299
// A parsed gjson result is returned. Fails the test if the query to server returns a non-2xx status code.
299300
//
300301
// Example of checking that a global underride rule contains the expected actions:
301302
//
302-
// containsDisplayNameRule := c.GetPushRule(t, "global", "underride", ".m.rule.contains_display_name")
303+
// containsDisplayNameRule := c.MustGetPushRule(t, "global", "underride", ".m.rule.contains_display_name")
303304
// must.MatchGJSON(
304305
// t,
305306
// containsDisplayNameRule,
@@ -309,14 +310,23 @@ func (c *CSAPI) GetAllPushRules(t ct.TestLike) gjson.Result {
309310
// map[string]interface{}{"set_tweak": "highlight"},
310311
// }),
311312
// )
312-
func (c *CSAPI) GetPushRule(t ct.TestLike, scope string, kind string, ruleID string) gjson.Result {
313+
func (c *CSAPI) MustGetPushRule(t ct.TestLike, scope string, kind string, ruleID string) gjson.Result {
313314
t.Helper()
314315

315-
res := c.MustDo(t, "GET", []string{"_matrix", "client", "v3", "pushrules", scope, kind, ruleID})
316+
res := c.GetPushRule(t, scope, kind, ruleID)
317+
mustRespond2xx(t, res)
318+
316319
pushRuleBytes := ParseJSON(t, res)
317320
return gjson.ParseBytes(pushRuleBytes)
318321
}
319322

323+
// GetPushRule queries the contents of a client's push rule by scope, kind and rule ID.
324+
func (c *CSAPI) GetPushRule(t ct.TestLike, scope string, kind string, ruleID string) *http.Response {
325+
t.Helper()
326+
327+
return c.Do(t, "GET", []string{"_matrix", "client", "v3", "pushrules", scope, kind, ruleID})
328+
}
329+
320330
// SetPushRule creates a new push rule on the user, or modifies an existing one.
321331
// If `before` or `after` parameters are not set to an empty string, their values
322332
// will be set as the `before` and `after` query parameters respectively on the
@@ -343,6 +353,14 @@ func (c *CSAPI) SetPushRule(t ct.TestLike, scope string, kind string, ruleID str
343353
return c.MustDo(t, "PUT", []string{"_matrix", "client", "v3", "pushrules", scope, kind, ruleID}, WithJSONBody(t, body), WithQueries(queryParams))
344354
}
345355

356+
// MustDisablePushRule disables a push rule on the user.
357+
// Fails the test if response is non-2xx.
358+
func (c *CSAPI) MustDisablePushRule(t ct.TestLike, scope string, kind string, ruleID string) {
359+
c.MustDo(t, "PUT", []string{"_matrix", "client", "v3", "pushrules", scope, kind, ruleID, "enabled"}, WithJSONBody(t, map[string]interface{}{
360+
"enabled": false,
361+
}))
362+
}
363+
346364
// Unsafe_SendEventUnsynced sends `e` into the room. This function is UNSAFE as it does not wait
347365
// for the event to be fully processed. This can cause flakey tests. Prefer `SendEventSynced`.
348366
// Returns the event ID of the sent event.
@@ -768,6 +786,19 @@ func (t *loggedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error
768786
return res, err
769787
}
770788

789+
// Extracts a JSON object given a search key
790+
// Caller must check `result.Exists()` to see whether the object actually exists.
791+
func GetOptionalJSONFieldObject(t ct.TestLike, body []byte, wantKey string) gjson.Result {
792+
t.Helper()
793+
res := gjson.GetBytes(body, wantKey)
794+
if !res.Exists() {
795+
log.Printf("OptionalJSONFieldObject: key '%s' absent from %s", wantKey, string(body))
796+
} else if !res.IsObject() {
797+
ct.Fatalf(t, "OptionalJSONFieldObject: key '%s' is not an object, body: %s", wantKey, string(body))
798+
}
799+
return res
800+
}
801+
771802
// GetJSONFieldStr extracts a value from a byte-encoded JSON body given a search key
772803
func GetJSONFieldStr(t ct.TestLike, body []byte, wantKey string) string {
773804
t.Helper()

tests/csapi/thread_notifications_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ func syncHasThreadedReadReceipt(roomID, userID, eventID, threadID string) client
5151
})
5252
}
5353

54+
// Disables push rules that are introduced in MSC4306 (if present),
55+
// because they interfere with the normal semantics of notifications in threads.
56+
func disableMsc4306PushRules(t *testing.T, user *client.CSAPI) {
57+
rules := []string{".io.element.msc4306.rule.subscribed_thread", ".io.element.msc4306.rule.unsubscribed_thread"}
58+
for _, rule := range rules {
59+
res := user.GetPushRule(t, "global", "postcontent", rule)
60+
if res.StatusCode == 404 {
61+
// No push rule to disable
62+
continue
63+
}
64+
65+
user.MustDisablePushRule(t, "global", "postcontent", rule)
66+
}
67+
}
68+
5469
// Test behavior of threaded receipts and notifications.
5570
//
5671
// 1. Send a series of messages, some of which are in threads.
@@ -79,7 +94,9 @@ func TestThreadedReceipts(t *testing.T) {
7994

8095
// Create a room with alice and bob.
8196
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
97+
disableMsc4306PushRules(t, alice)
8298
bob := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
99+
disableMsc4306PushRules(t, bob)
83100

84101
roomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
85102
bob.MustJoinRoom(t, roomID, nil)
@@ -312,7 +329,9 @@ func TestThreadReceiptsInSyncMSC4102(t *testing.T) {
312329

313330
// Create a room with alice and bob.
314331
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{})
332+
disableMsc4306PushRules(t, alice)
315333
bob := deployment.Register(t, "hs2", helpers.RegistrationOpts{})
334+
disableMsc4306PushRules(t, bob)
316335
roomID := alice.MustCreateRoom(t, map[string]interface{}{"preset": "public_chat"})
317336
bob.MustJoinRoom(t, roomID, []spec.ServerName{
318337
deployment.GetFullyQualifiedHomeserverName(t, "hs1"),

tests/federation_acl_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,9 @@ func TestACLs(t *testing.T) {
5959
Content: map[string]interface{}{
6060
"allow": []string{"*"},
6161
"allow_ip_literals": true,
62-
"deny": []string{"hs2"},
62+
"deny": []string{
63+
string(deployment.GetFullyQualifiedHomeserverName(t, "hs2")),
64+
},
6365
},
6466
})
6567
// wait for the ACL to show up on hs2
@@ -111,7 +113,9 @@ func TestACLs(t *testing.T) {
111113
content := user.MustGetStateEventContent(t, roomID, "m.room.server_acl", "")
112114
must.MatchGJSON(t, content,
113115
match.JSONKeyEqual("allow", []string{"*"}),
114-
match.JSONKeyEqual("deny", []string{"hs2"}),
116+
match.JSONKeyEqual("deny", []string{
117+
string(deployment.GetFullyQualifiedHomeserverName(t, "hs2")),
118+
}),
115119
match.JSONKeyEqual("allow_ip_literals", true),
116120
)
117121
}

tests/federation_rooms_invite_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/matrix-org/gomatrixserverlib/spec"
1313
"github.com/tidwall/gjson"
1414

15+
"github.com/matrix-org/complement/b"
1516
"github.com/matrix-org/complement/client"
1617
"github.com/matrix-org/complement/helpers"
1718
"github.com/matrix-org/complement/match"
@@ -23,6 +24,7 @@ func TestFederationRoomsInvite(t *testing.T) {
2324
defer deployment.Destroy(t)
2425

2526
alice := deployment.Register(t, "hs1", helpers.RegistrationOpts{LocalpartSuffix: "alice"})
27+
alice2 := deployment.Register(t, "hs1", helpers.RegistrationOpts{LocalpartSuffix: "alice2"})
2628
bob := deployment.Register(t, "hs2", helpers.RegistrationOpts{LocalpartSuffix: "bob"})
2729
bob2 := deployment.Register(t, "hs2", helpers.RegistrationOpts{LocalpartSuffix: "bob2"})
2830

@@ -149,6 +151,73 @@ func TestFederationRoomsInvite(t *testing.T) {
149151
alice.MustSyncUntil(t, client.SyncReq{Filter: includeLeaveSyncFilter}, client.SyncLeftFrom(bob2.UserID, roomID))
150152
})
151153

154+
t.Run("Inviter user can rescind invite over federation", func(t *testing.T) {
155+
t.Parallel()
156+
roomID := alice.MustCreateRoom(t, map[string]interface{}{
157+
"preset": "private_chat",
158+
"invite": []string{bob.UserID},
159+
})
160+
bob.MustSyncUntil(t, client.SyncReq{}, client.SyncInvitedTo(bob.UserID, roomID))
161+
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "kick"},
162+
client.WithJSONBody(t, map[string]interface{}{
163+
"user_id": bob.UserID,
164+
"reason": "testing",
165+
}),
166+
)
167+
168+
bob.MustSyncUntil(t, client.SyncReq{Filter: includeLeaveSyncFilter}, client.SyncLeftFrom(bob.UserID, roomID))
169+
})
170+
171+
t.Run("Non-invitee user cannot rescind invite over federation", func(t *testing.T) {
172+
t.Parallel()
173+
174+
// First create a room that Bob is in. This is so that later we can
175+
// send a message to test that Bob doesn't see the rescission.
176+
roomID1 := alice.MustCreateRoom(t, map[string]interface{}{
177+
"preset": "private_chat",
178+
"invite": []string{bob.UserID},
179+
})
180+
since := bob.MustSyncUntil(t, client.SyncReq{}, client.SyncInvitedTo(bob.UserID, roomID1))
181+
bob.MustJoinRoom(t, roomID1, []spec.ServerName{})
182+
183+
// Second room which Alice and Alice2 join, Alice2 invites Bob and
184+
// then Alice kicks him.
185+
roomID2 := alice.MustCreateRoom(t, map[string]interface{}{
186+
"preset": "private_chat",
187+
"invite": []string{alice2.UserID},
188+
})
189+
alice2.MustSyncUntil(t, client.SyncReq{}, client.SyncInvitedTo(alice2.UserID, roomID2))
190+
alice2.MustJoinRoom(t, roomID2, []spec.ServerName{})
191+
192+
alice2.MustInviteRoom(t, roomID2, bob.UserID)
193+
bob.MustSyncUntil(t, client.SyncReq{}, client.SyncInvitedTo(bob.UserID, roomID2))
194+
195+
// Alice, not the original inviter, kicks bob. This does not result
196+
// in bob seeing the rescission.
197+
alice.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID2, "kick"},
198+
client.WithJSONBody(t, map[string]interface{}{
199+
"user_id": bob.UserID,
200+
"reason": "testing",
201+
}),
202+
)
203+
204+
// Check bob *doesn't* see the rescission. We do this by sending a
205+
// message in room1 and checking that once Bob receives that message
206+
// he still hasn't seen the leave.
207+
eventID := alice.SendEventSynced(t, roomID1, b.Event{
208+
Type: "m.room.message",
209+
Content: map[string]interface{}{
210+
"body": "1",
211+
"msgtype": "m.text",
212+
},
213+
Sender: alice.UserID,
214+
})
215+
bob.MustSyncUntil(t, client.SyncReq{Since: since}, client.SyncTimelineHasEventID(roomID1, eventID))
216+
217+
// Check bob is still invited by doing an initial sync
218+
bob.MustSyncUntil(t, client.SyncReq{}, client.SyncInvitedTo(bob.UserID, roomID2))
219+
})
220+
152221
t.Run("Invited user has 'is_direct' flag in prev_content after joining", func(t *testing.T) {
153222
roomID := alice.MustCreateRoom(t, map[string]interface{}{
154223
"preset": "private_chat",

tests/msc3930/msc3930_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func TestPollsLocalPushRules(t *testing.T) {
3939
// Request each of the push rule IDs defined by MSC3930 and verify their structure.
4040

4141
// This push rule silences all poll responses.
42-
pollResponseRule := alice.GetPushRule(t, "global", "override", pollResponseRuleID)
42+
pollResponseRule := alice.MustGetPushRule(t, "global", "override", pollResponseRuleID)
4343
must.MatchGJSON(
4444
t,
4545
pollResponseRule,
@@ -55,7 +55,7 @@ func TestPollsLocalPushRules(t *testing.T) {
5555
)
5656

5757
// This push rule creates a sound and notifies the user when a poll is started in a one-to-one room.
58-
pollStartOneToOneRule := alice.GetPushRule(t, "global", "underride", pollStartOneToOneRuleID)
58+
pollStartOneToOneRule := alice.MustGetPushRule(t, "global", "underride", pollStartOneToOneRuleID)
5959
must.MatchGJSON(
6060
t,
6161
pollStartOneToOneRule,
@@ -78,7 +78,7 @@ func TestPollsLocalPushRules(t *testing.T) {
7878
)
7979

8080
// This push rule creates a sound and notifies the user when a poll is ended in a one-to-one room.
81-
pollEndOneToOneRule := alice.GetPushRule(t, "global", "underride", pollEndOneToOneRuleID)
81+
pollEndOneToOneRule := alice.MustGetPushRule(t, "global", "underride", pollEndOneToOneRuleID)
8282
must.MatchGJSON(
8383
t,
8484
pollEndOneToOneRule,
@@ -101,7 +101,7 @@ func TestPollsLocalPushRules(t *testing.T) {
101101
)
102102

103103
// This push rule notifies the user when a poll is started in any room.
104-
pollStartRule := alice.GetPushRule(t, "global", "underride", pollStartRuleID)
104+
pollStartRule := alice.MustGetPushRule(t, "global", "underride", pollStartRuleID)
105105
must.MatchGJSON(
106106
t,
107107
pollStartRule,
@@ -118,7 +118,7 @@ func TestPollsLocalPushRules(t *testing.T) {
118118
)
119119

120120
// This push rule notifies the user when a poll is ended in any room.
121-
pollEndRule := alice.GetPushRule(t, "global", "underride", pollEndRuleID)
121+
pollEndRule := alice.MustGetPushRule(t, "global", "underride", pollEndRuleID)
122122
must.MatchGJSON(
123123
t,
124124
pollEndRule,

tests/msc4306/main_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package tests
2+
3+
import (
4+
"testing"
5+
6+
"github.com/matrix-org/complement"
7+
)
8+
9+
func TestMain(m *testing.M) {
10+
complement.TestMain(m, "msc4306")
11+
}

0 commit comments

Comments
 (0)