Skip to content

Commit d6dea53

Browse files
authored
Merge branch 'main' into shay/no_redact
2 parents 3594bd9 + 1acfcf2 commit d6dea53

38 files changed

+2549
-184
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
matrix:
3939
include:
4040
- homeserver: Synapse
41-
tags: synapse_blacklist msc3083 msc3787 faster_joins
41+
tags: synapse_blacklist msc3083 msc3787 msc3874 faster_joins
4242
env: "COMPLEMENT_SHARE_ENV_PREFIX=PASS_ PASS_SYNAPSE_COMPLEMENT_DATABASE=sqlite"
4343
timeout: 20m
4444

internal/client/client.go

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (c *CSAPI) UploadContent(t *testing.T, fileBody []byte, fileName string, co
115115
func (c *CSAPI) DownloadContent(t *testing.T, mxcUri string) ([]byte, string) {
116116
t.Helper()
117117
origin, mediaId := SplitMxc(mxcUri)
118-
res := c.MustDo(t, "GET", []string{"_matrix", "media", "v3", "download", origin, mediaId}, struct{}{})
118+
res := c.MustDoFunc(t, "GET", []string{"_matrix", "media", "v3", "download", origin, mediaId})
119119
contentType := res.Header.Get("Content-Type")
120120
b, err := ioutil.ReadAll(res.Body)
121121
if err != nil {
@@ -127,7 +127,7 @@ func (c *CSAPI) DownloadContent(t *testing.T, mxcUri string) ([]byte, string) {
127127
// CreateRoom creates a room with an optional HTTP request body. Fails the test on error. Returns the room ID.
128128
func (c *CSAPI) CreateRoom(t *testing.T, creationContent interface{}) string {
129129
t.Helper()
130-
res := c.MustDo(t, "POST", []string{"_matrix", "client", "v3", "createRoom"}, creationContent)
130+
res := c.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "createRoom"}, WithJSONBody(t, creationContent))
131131
body := ParseJSON(t, res)
132132
return GetJSONFieldStr(t, body, "room_id")
133133
}
@@ -166,7 +166,7 @@ func (c *CSAPI) InviteRoom(t *testing.T, roomID string, userID string) {
166166
body := map[string]interface{}{
167167
"user_id": userID,
168168
}
169-
c.MustDo(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "invite"}, body)
169+
c.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "invite"}, WithJSONBody(t, body))
170170
}
171171

172172
func (c *CSAPI) GetGlobalAccountData(t *testing.T, eventType string) *http.Response {
@@ -186,7 +186,7 @@ func (c *CSAPI) SendEventSynced(t *testing.T, roomID string, e b.Event) string {
186186
if e.StateKey != nil {
187187
paths = []string{"_matrix", "client", "v3", "rooms", roomID, "state", e.Type, *e.StateKey}
188188
}
189-
res := c.MustDo(t, "PUT", paths, e.Content)
189+
res := c.MustDoFunc(t, "PUT", paths, WithJSONBody(t, e.Content))
190190
body := ParseJSON(t, res)
191191
eventID := GetJSONFieldStr(t, body, "event_id")
192192
t.Logf("SendEventSynced waiting for event ID %s", eventID)
@@ -333,7 +333,7 @@ func (c *CSAPI) RegisterUser(t *testing.T, localpart, password string) (userID,
333333
"username": localpart,
334334
"password": password,
335335
}
336-
res := c.MustDo(t, "POST", []string{"_matrix", "client", "v3", "register"}, reqBody)
336+
res := c.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "register"}, WithJSONBody(t, reqBody))
337337

338338
body, err := ioutil.ReadAll(res.Body)
339339
if err != nil {
@@ -411,21 +411,6 @@ func (c *CSAPI) GetDefaultRoomVersion(t *testing.T) gomatrixserverlib.RoomVersio
411411
return gomatrixserverlib.RoomVersion(defaultVersion.Str)
412412
}
413413

414-
// MustDo will do the HTTP request and fail the test if the response is not 2xx
415-
//
416-
// Deprecated: Prefer MustDoFunc. MustDo is the older format which doesn't allow for vargs
417-
// and will be removed in the future. MustDoFunc also logs HTTP response bodies on error.
418-
func (c *CSAPI) MustDo(t *testing.T, method string, paths []string, jsonBody interface{}) *http.Response {
419-
t.Helper()
420-
res := c.DoFunc(t, method, paths, WithJSONBody(t, jsonBody))
421-
if res.StatusCode < 200 || res.StatusCode >= 300 {
422-
defer res.Body.Close()
423-
body, _ := ioutil.ReadAll(res.Body)
424-
t.Fatalf("CSAPI.MustDo %s %s returned HTTP %d : %s", method, res.Request.URL.String(), res.StatusCode, string(body))
425-
}
426-
return res
427-
}
428-
429414
// WithRawBody sets the HTTP request body to `body`
430415
func WithRawBody(body []byte) RequestOpt {
431416
return func(req *http.Request) {
@@ -482,7 +467,7 @@ func (c *CSAPI) MustDoFunc(t *testing.T, method string, paths []string, opts ...
482467
if res.StatusCode < 200 || res.StatusCode >= 300 {
483468
defer res.Body.Close()
484469
body, _ := ioutil.ReadAll(res.Body)
485-
t.Fatalf("CSAPI.MustDoFunc response return non-2xx code: %s - body: %s", res.Status, string(body))
470+
t.Fatalf("CSAPI.MustDoFunc %s %s returned non-2xx code: %s - body: %s", method, res.Request.URL.String(), res.Status, string(body))
486471
}
487472
return res
488473
}
@@ -860,3 +845,48 @@ func SplitMxc(mxcUri string) (string, string) {
860845

861846
return origin, mediaId
862847
}
848+
849+
// SendToDeviceMessages sends to-device messages over /sendToDevice/.
850+
//
851+
// The messages parameter is nested as follows:
852+
// user_id -> device_id -> content (map[string]interface{})
853+
func (c *CSAPI) SendToDeviceMessages(t *testing.T, evType string, messages map[string]map[string]map[string]interface{}) {
854+
t.Helper()
855+
c.txnID++
856+
c.MustDoFunc(
857+
t,
858+
"PUT",
859+
[]string{"_matrix", "client", "v3", "sendToDevice", evType, strconv.Itoa(c.txnID)},
860+
WithJSONBody(
861+
t,
862+
map[string]map[string]map[string]map[string]interface{}{
863+
"messages": messages,
864+
},
865+
),
866+
)
867+
}
868+
869+
// Check that sync has received a to-device message,
870+
// with optional user filtering.
871+
//
872+
// If fromUser == "", all messages will be passed through to the check function.
873+
// `check` will be called for all messages that have passed the filter.
874+
//
875+
// `check` gets passed the full event, including sender and type.
876+
func SyncToDeviceHas(fromUser string, check func(gjson.Result) bool) SyncCheckOpt {
877+
return func(clientUserID string, topLevelSyncJSON gjson.Result) error {
878+
err := loopArray(
879+
topLevelSyncJSON, "to_device.events", func(result gjson.Result) bool {
880+
if fromUser != "" && result.Get("sender").Str != fromUser {
881+
return false
882+
} else {
883+
return check(result)
884+
}
885+
},
886+
)
887+
if err == nil {
888+
return nil
889+
}
890+
return fmt.Errorf("SyncToDeviceHas(%v): %s", fromUser, err)
891+
}
892+
}

internal/federation/server.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,37 @@ func (s *Server) SendFederationRequest(
248248
return err
249249
}
250250

251+
// DoFederationRequest signs and sends an arbitrary federation request from this server, and returns the response.
252+
//
253+
// The requests will be routed according to the deployment map in `deployment`.
254+
func (s *Server) DoFederationRequest(
255+
ctx context.Context,
256+
t *testing.T,
257+
deployment *docker.Deployment,
258+
req gomatrixserverlib.FederationRequest) (*http.Response, error) {
259+
if err := req.Sign(gomatrixserverlib.ServerName(s.serverName), s.KeyID, s.Priv); err != nil {
260+
return nil, err
261+
}
262+
263+
httpReq, err := req.HTTPRequest()
264+
if err != nil {
265+
return nil, err
266+
}
267+
268+
httpClient := gomatrixserverlib.NewClient(gomatrixserverlib.WithTransport(&docker.RoundTripper{Deployment: deployment}))
269+
start := time.Now()
270+
271+
var resp *http.Response
272+
resp, err = httpClient.DoHTTPRequest(ctx, httpReq)
273+
274+
if httpError, ok := err.(gomatrix.HTTPError); ok {
275+
t.Logf("[SSAPI] %s %s%s => error(%d): %s (%s)", req.Method(), req.Destination(), req.RequestURI(), httpError.Code, err, time.Since(start))
276+
} else if err == nil {
277+
t.Logf("[SSAPI] %s %s%s => %d (%s)", req.Method(), req.Destination(), req.RequestURI(), resp.StatusCode, time.Since(start))
278+
}
279+
return resp, err
280+
}
281+
251282
// MustCreateEvent will create and sign a new latest event for the given room.
252283
// It does not insert this event into the room however. See ServerRoom.AddEvent for that.
253284
func (s *Server) MustCreateEvent(t *testing.T, room *ServerRoom, ev b.Event) *gomatrixserverlib.Event {
@@ -437,7 +468,7 @@ func (s *Server) Listen() (cancel func()) {
437468
}()
438469

439470
return func() {
440-
err := s.srv.Shutdown(context.Background())
471+
err := s.srv.Close()
441472
if err != nil {
442473
s.t.Fatalf("ListenFederationServer: failed to shutdown server: %s", err)
443474
}

internal/must/must.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,35 @@ func MatchFederationRequest(t *testing.T, fedReq *gomatrixserverlib.FederationRe
132132
}
133133
}
134134

135+
// MatchGJSON performs JSON assertions on a gjson.Result object.
136+
func MatchGJSON(t *testing.T, jsonResult gjson.Result, matchers ...match.JSON) {
137+
t.Helper()
138+
139+
MatchJSON(t, jsonResult.Str, matchers...)
140+
}
141+
142+
// MatchJSON performs JSON assertions on a raw JSON string.
143+
func MatchJSON(t *testing.T, json string, matchers ...match.JSON) {
144+
t.Helper()
145+
146+
MatchJSONBytes(t, []byte(json), matchers...)
147+
}
148+
149+
// MatchJSONBytes performs JSON assertions on a raw json byte slice.
150+
func MatchJSONBytes(t *testing.T, rawJson []byte, matchers ...match.JSON) {
151+
t.Helper()
152+
153+
if !gjson.ValidBytes(rawJson) {
154+
t.Fatalf("MatchJSONBytes: rawJson is not valid JSON")
155+
}
156+
157+
for _, jm := range matchers {
158+
if err := jm(rawJson); err != nil {
159+
t.Fatalf("MatchJSONBytes %s", err)
160+
}
161+
}
162+
}
163+
135164
// EqualStr ensures that got==want else logs an error.
136165
func EqualStr(t *testing.T, got, want, msg string) {
137166
t.Helper()

internal/web/server.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package web
2+
3+
import (
4+
"fmt"
5+
"net"
6+
"net/http"
7+
"testing"
8+
9+
"github.com/gorilla/mux"
10+
11+
"github.com/matrix-org/complement/internal/config"
12+
)
13+
14+
type Server struct {
15+
URL string
16+
Port int
17+
server *http.Server
18+
listener net.Listener
19+
}
20+
21+
func NewServer(t *testing.T, comp *config.Complement, configFunc func(router *mux.Router)) *Server {
22+
t.Helper()
23+
24+
listener, err := net.Listen("tcp", ":0")
25+
if err != nil {
26+
t.Fatalf("Could not create listener for web server: %s", err)
27+
}
28+
29+
port := listener.Addr().(*net.TCPAddr).Port
30+
31+
r := mux.NewRouter()
32+
33+
configFunc(r)
34+
35+
server := &http.Server{Addr: ":0", Handler: r}
36+
37+
go server.Serve(listener)
38+
39+
return &Server{
40+
URL: fmt.Sprintf("http://%s:%d", comp.HostnameRunningComplement, port),
41+
Port: port,
42+
server: server,
43+
listener: listener,
44+
}
45+
}
46+
47+
func (s *Server) Close() {
48+
s.server.Close()
49+
s.listener.Close()
50+
}

sytest.ignored.list

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ New room members see first user's profile information in per-room initialSync
3535
A departed room is still included in /initialSync (SPEC-216)
3636
Can get rooms/{roomId}/initialSync for a departed room (SPEC-216)
3737
initialSync sees my presence status
38-
Global initialSyncGlobal initialSync with limit=0 gives no messages
38+
Global initialSync
39+
Global initialSync with limit=0 gives no messages
3940
Room initialSync
4041
Room initialSync with limit=0 gives no messages
4142
Read receipts are visible to /initialSync

tests/csapi/apidoc_login_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@ func TestLogin(t *testing.T) {
4343
// sytest: POST /login can log in as a user
4444
t.Run("POST /login can login as user", func(t *testing.T) {
4545
t.Parallel()
46-
res := unauthedClient.MustDo(t, "POST", []string{"_matrix", "client", "v3", "login"}, json.RawMessage(`{
46+
res := unauthedClient.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "login"}, client.WithRawBody(json.RawMessage(`{
4747
"type": "m.login.password",
4848
"identifier": {
4949
"type": "m.id.user",
5050
"user": "@test_login_user:hs1"
5151
},
5252
"password": "superuser"
53-
}`))
53+
}`)))
5454

5555
must.MatchResponse(t, res, match.HTTPResponse{
5656
JSON: []match.JSON{
@@ -63,15 +63,15 @@ func TestLogin(t *testing.T) {
6363
t.Run("POST /login returns the same device_id as that in the request", func(t *testing.T) {
6464
t.Parallel()
6565
deviceID := "test_device_id"
66-
res := unauthedClient.MustDo(t, "POST", []string{"_matrix", "client", "v3", "login"}, json.RawMessage(`{
66+
res := unauthedClient.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "login"}, client.WithRawBody(json.RawMessage(`{
6767
"type": "m.login.password",
6868
"identifier": {
6969
"type": "m.id.user",
7070
"user": "@test_login_user:hs1"
7171
},
7272
"password": "superuser",
7373
"device_id": "`+deviceID+`"
74-
}`))
74+
}`)))
7575

7676
must.MatchResponse(t, res, match.HTTPResponse{
7777
JSON: []match.JSON{
@@ -84,14 +84,14 @@ func TestLogin(t *testing.T) {
8484
t.Run("POST /login can log in as a user with just the local part of the id", func(t *testing.T) {
8585
t.Parallel()
8686

87-
res := unauthedClient.MustDo(t, "POST", []string{"_matrix", "client", "v3", "login"}, json.RawMessage(`{
87+
res := unauthedClient.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "login"}, client.WithRawBody(json.RawMessage(`{
8888
"type": "m.login.password",
8989
"identifier": {
9090
"type": "m.id.user",
9191
"user": "test_login_user"
9292
},
9393
"password": "superuser"
94-
}`))
94+
}`)))
9595

9696
must.MatchResponse(t, res, match.HTTPResponse{
9797
JSON: []match.JSON{

tests/csapi/apidoc_room_state_test.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package csapi_tests
22

33
import (
4+
"net/http"
45
"net/url"
56
"testing"
67
"time"
@@ -11,9 +12,6 @@ import (
1112
"github.com/matrix-org/complement/internal/client"
1213
"github.com/matrix-org/complement/internal/match"
1314
"github.com/matrix-org/complement/internal/must"
14-
"github.com/matrix-org/complement/runtime"
15-
16-
"net/http"
1715
)
1816

1917
func TestRoomState(t *testing.T) {
@@ -330,7 +328,6 @@ func TestRoomState(t *testing.T) {
330328
})
331329
})
332330
t.Run("GET /rooms/:room_id/joined_members is forbidden after leaving room", func(t *testing.T) {
333-
runtime.SkipIf(t, runtime.Dendrite) // https://github.com/matrix-org/complement/pull/424
334331
t.Parallel()
335332
roomID := authedClient.CreateRoom(t, map[string]interface{}{})
336333
authedClient.LeaveRoom(t, roomID)

tests/csapi/apidoc_server_capabilities_test.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"github.com/matrix-org/complement/internal/b"
88
"github.com/matrix-org/complement/internal/match"
99
"github.com/matrix-org/complement/internal/must"
10-
"github.com/tidwall/gjson"
1110
)
1211

1312
func TestServerCapabilities(t *testing.T) {
@@ -19,13 +18,13 @@ func TestServerCapabilities(t *testing.T) {
1918

2019
// sytest: GET /capabilities is present and well formed for registered user
2120
data := authedClient.GetCapabilities(t)
22-
j := gjson.ParseBytes(data)
23-
if !j.Get(`capabilities.m\.room_versions`).Exists() {
24-
t.Fatal("expected m.room_versions not found")
25-
}
26-
if !j.Get(`capabilities.m\.change_password`).Exists() {
27-
t.Fatal("expected m.change_password not found")
28-
}
21+
22+
must.MatchJSONBytes(
23+
t,
24+
data,
25+
match.JSONKeyPresent(`capabilities.m\.room_versions`),
26+
match.JSONKeyPresent(`capabilities.m\.change_password`),
27+
)
2928

3029
// sytest: GET /v3/capabilities is not public
3130
res := unauthedClient.DoFunc(t, "GET", []string{"_matrix", "client", "v3", "capabilities"})

tests/csapi/device_lists_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ func TestDeviceListUpdates(t *testing.T) {
328328
t.Run("when remote user leaves a room", func(t *testing.T) { testOtherUserLeave(t, deployment, "hs1", "hs2") })
329329
t.Run("when leaving a room with a local user", func(t *testing.T) { testLeave(t, deployment, "hs1", "hs1") })
330330
t.Run("when leaving a room with a remote user", func(t *testing.T) {
331-
runtime.SkipIf(t, runtime.Synapse) // https://github.com/matrix-org/synapse/issues/13650
331+
runtime.SkipIf(t, runtime.Synapse) // FIXME: https://github.com/matrix-org/synapse/issues/13650
332332
testLeave(t, deployment, "hs1", "hs2")
333333
})
334334
t.Run("when local user rejoins a room", func(t *testing.T) { testOtherUserRejoin(t, deployment, "hs1", "hs1") })

0 commit comments

Comments
 (0)