-
Notifications
You must be signed in to change notification settings - Fork 61
Convert more tests to Complement #353
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
Changes from all commits
517076c
e95787d
bced719
7d5b661
abd890e
6e6b895
31a00b1
f5fcf4a
fd714b5
4cd926c
a5b7bd6
acea6ce
d6d98fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package csapi_tests | ||
|
||
import ( | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/tidwall/gjson" | ||
"github.com/tidwall/sjson" | ||
|
||
"github.com/matrix-org/complement/internal/b" | ||
"github.com/matrix-org/complement/internal/client" | ||
"github.com/matrix-org/complement/internal/match" | ||
"github.com/matrix-org/complement/internal/must" | ||
) | ||
|
||
func TestRoomLevels(t *testing.T) { | ||
deployment := Deploy(t, b.BlueprintAlice) | ||
defer deployment.Destroy(t) | ||
alice := deployment.Client(t, "hs1", "@alice:hs1") | ||
|
||
// sytest: Both GET and PUT work | ||
t.Run("Parallel", func(t *testing.T) { | ||
// sytest: GET /rooms/:room_id/state/m.room.power_levels can fetch levels | ||
t.Run("GET /rooms/:room_id/state/m.room.power_levels can fetch levels", func(t *testing.T) { | ||
t.Parallel() | ||
roomID := alice.CreateRoom(t, map[string]interface{}{}) | ||
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(alice.UserID, roomID)) | ||
res := alice.MustDoFunc(t, "GET", []string{"_matrix", "client", "v3", "rooms", roomID, "state", "m.room.power_levels"}) | ||
|
||
body := gjson.ParseBytes(must.ParseJSON(t, res.Body)) | ||
requiredFields := []string{"ban", "kick", "redact", "state_default", "events_default", "events", "users"} | ||
for i := range requiredFields { | ||
if !body.Get(requiredFields[i]).Exists() { | ||
t.Fatalf("expected json field %s, but it does not exist", requiredFields[i]) | ||
} | ||
} | ||
users := body.Get("users").Map() | ||
alicePowerLevel, ok := users[alice.UserID] | ||
if !ok { | ||
t.Fatalf("Expected room creator (%s) to exist in user powerlevel list", alice.UserID) | ||
} | ||
|
||
userDefaults := body.Get("user_defaults").Int() | ||
|
||
if userDefaults > alicePowerLevel.Int() { | ||
t.Fatalf("Expected room creator to have a higher-than-default powerlevel") | ||
} | ||
}) | ||
// sytest: PUT /rooms/:room_id/state/m.room.power_levels can set levels | ||
t.Run("PUT /rooms/:room_id/state/m.room.power_levels can set levels", func(t *testing.T) { | ||
t.Parallel() | ||
roomID := alice.CreateRoom(t, map[string]interface{}{}) | ||
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(alice.UserID, roomID)) | ||
res := alice.MustDoFunc(t, "GET", []string{"_matrix", "client", "v3", "rooms", roomID, "state", "m.room.power_levels"}) | ||
|
||
powerLevels := gjson.ParseBytes(must.ParseJSON(t, res.Body)) | ||
changedUser := client.GjsonEscape("@random-other-user:their.home") | ||
alicePowerLevel := powerLevels.Get("users." + client.GjsonEscape(alice.UserID)).Int() | ||
pl := map[string]int64{ | ||
alice.UserID: alicePowerLevel, | ||
"@random-other-user:their.home": 20, | ||
} | ||
newPowerlevels, err := sjson.Set(powerLevels.Str, "users", pl) | ||
if err != nil { | ||
t.Fatalf("unable to update powerlevel JSON") | ||
} | ||
reqBody := client.WithRawBody([]byte(newPowerlevels)) | ||
alice.MustDoFunc(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "state", "m.room.power_levels"}, reqBody) | ||
res = alice.MustDoFunc(t, "GET", []string{"_matrix", "client", "v3", "rooms", roomID, "state", "m.room.power_levels"}) | ||
powerLevels = gjson.ParseBytes(must.ParseJSON(t, res.Body)) | ||
if powerLevels.Get("users."+changedUser).Int() != 20 { | ||
t.Fatal("Expected to have set other user's level to 20") | ||
} | ||
}) | ||
// sytest: PUT power_levels should not explode if the old power levels were empty | ||
t.Run("PUT power_levels should not explode if the old power levels were empty", func(t *testing.T) { | ||
t.Parallel() | ||
roomID := alice.CreateRoom(t, map[string]interface{}{}) | ||
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(alice.UserID, roomID)) | ||
|
||
// absence of an 'events' key | ||
reqBody := client.WithJSONBody(t, map[string]interface{}{ | ||
"users": map[string]int64{ | ||
alice.UserID: 100, | ||
}, | ||
}) | ||
alice.MustDoFunc(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "state", "m.room.power_levels"}, reqBody) | ||
// absence of a 'users' key | ||
reqBody = client.WithJSONBody(t, map[string]interface{}{}) | ||
alice.MustDoFunc(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "state", "m.room.power_levels"}, reqBody) | ||
// this should now give a 403 (not a 500) | ||
reqBody = client.WithJSONBody(t, map[string]interface{}{ | ||
"users": struct{}{}, | ||
}) | ||
res := alice.DoFunc(t, "PUT", []string{"_matrix", "client", "v3", "rooms", roomID, "state", "m.room.power_levels"}, reqBody) | ||
must.MatchResponse(t, res, match.HTTPResponse{ | ||
StatusCode: http.StatusForbidden, | ||
}) | ||
}) | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
package csapi_tests | ||
|
||
import ( | ||
"fmt" | ||
"net/url" | ||
"testing" | ||
|
||
"github.com/matrix-org/gomatrixserverlib" | ||
"github.com/tidwall/gjson" | ||
|
||
"github.com/matrix-org/complement/internal/b" | ||
"github.com/matrix-org/complement/internal/client" | ||
"github.com/matrix-org/complement/internal/must" | ||
) | ||
|
||
func TestRoomVersions(t *testing.T) { | ||
deployment := Deploy(t, b.BlueprintFederationTwoLocalOneRemote) | ||
defer deployment.Destroy(t) | ||
|
||
alice := deployment.Client(t, "hs1", "@alice:hs1") | ||
bob := deployment.Client(t, "hs1", "@bob:hs1") | ||
charlie := deployment.Client(t, "hs2", "@charlie:hs2") | ||
|
||
roomVersions := gomatrixserverlib.RoomVersions() | ||
|
||
// Query room versions the server supports | ||
capabilities := alice.GetCapabilities(t) | ||
availableRoomVersions := gjson.GetBytes(capabilities, `capabilities.m\.room_versions.available`).Map() | ||
t.Run("Parallel", func(t *testing.T) { | ||
// iterate over all room versions | ||
for v := range roomVersions { | ||
roomVersion := v | ||
// skip versions the server doesn't know about | ||
if _, ok := availableRoomVersions[string(roomVersion)]; !ok { | ||
t.Logf("Skipping unsupported room version %s", roomVersion) | ||
continue | ||
} | ||
// sytest: User can create and send/receive messages in a room with version $version | ||
t.Run(fmt.Sprintf("User can create and send/receive messages in a room with version %s", roomVersion), func(t *testing.T) { | ||
t.Parallel() | ||
roomID := createRoomSynced(t, alice, map[string]interface{}{ | ||
"room_version": roomVersion, | ||
}) | ||
|
||
res, _ := alice.MustSync(t, client.SyncReq{}) | ||
room := res.Get("rooms.join." + client.GjsonEscape(roomID)) | ||
ev0 := room.Get("timeline.events").Array()[0] | ||
must.EqualStr(t, ev0.Get("type").Str, "m.room.create", "not a m.room.create event") | ||
sendMessageSynced(t, alice, roomID) | ||
}) | ||
|
||
userTypes := map[string]*client.CSAPI{ | ||
"local": bob, | ||
"remote": charlie, | ||
} | ||
for typ, joiner := range userTypes { | ||
// Ensure to use the correct value and not only the last one. | ||
typ := typ | ||
joiner := joiner | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need comments to explain why you do this (else it'll take the last value only). |
||
|
||
// sytest: $user_type user can join room with version $version | ||
t.Run(fmt.Sprintf("%s user can join room with version %s", typ, roomVersion), func(t *testing.T) { | ||
t.Parallel() | ||
roomAlias := fmt.Sprintf("roomAlias_V%s%s", typ, roomVersion) | ||
t.Logf("RoomAlias: %s", roomAlias) | ||
roomID := createRoomSynced(t, alice, map[string]interface{}{ | ||
"room_version": roomVersion, | ||
"room_alias_name": roomAlias, | ||
"preset": "public_chat", | ||
}) | ||
joinRoomSynced(t, joiner, roomID, fmt.Sprintf("#%s:%s", roomAlias, "hs1")) | ||
_, nextBatch := joiner.MustSync(t, client.SyncReq{}) | ||
eventID := sendMessageSynced(t, alice, roomID) | ||
joiner.MustSyncUntil(t, client.SyncReq{Since: nextBatch}, client.SyncTimelineHas(roomID, func(result gjson.Result) bool { | ||
if len(result.Array()) > 1 { | ||
t.Fatal("Expected a single timeline event") | ||
} | ||
must.EqualStr(t, result.Array()[0].Get("event_id").Str, eventID, "wrong event id") | ||
return true | ||
})) | ||
}) | ||
|
||
// sytest: User can invite $user_type user to room with version $version | ||
t.Run(fmt.Sprintf("User can invite %s user to room with version %s", typ, roomVersion), func(t *testing.T) { | ||
t.Parallel() | ||
roomID := createRoomSynced(t, alice, map[string]interface{}{ | ||
"room_version": roomVersion, | ||
"preset": "private_chat", | ||
}) | ||
alice.InviteRoom(t, roomID, joiner.UserID) | ||
joiner.MustSyncUntil(t, client.SyncReq{}, client.SyncInvitedTo(joiner.UserID, roomID)) | ||
joinRoomSynced(t, joiner, roomID, "") | ||
_, nextBatch := joiner.MustSync(t, client.SyncReq{}) | ||
eventID := sendMessageSynced(t, alice, roomID) | ||
joiner.MustSyncUntil(t, client.SyncReq{Since: nextBatch}, client.SyncTimelineHas(roomID, func(result gjson.Result) bool { | ||
if len(result.Array()) > 1 { | ||
t.Fatal("Expected a single timeline event") | ||
} | ||
must.EqualStr(t, result.Array()[0].Get("event_id").Str, eventID, "wrong event id") | ||
return true | ||
})) | ||
}) | ||
|
||
} | ||
|
||
// sytest: Remote user can backfill in a room with version $version | ||
t.Run(fmt.Sprintf("Remote user can backfill in a room with version %s", roomVersion), func(t *testing.T) { | ||
t.Parallel() | ||
roomID := createRoomSynced(t, alice, map[string]interface{}{ | ||
"room_version": roomVersion, | ||
"invite": []string{charlie.UserID}, | ||
}) | ||
for i := 0; i < 20; i++ { | ||
sendMessageSynced(t, alice, roomID) | ||
} | ||
charlie.MustSyncUntil(t, client.SyncReq{}, client.SyncInvitedTo(charlie.UserID, roomID)) | ||
joinRoomSynced(t, charlie, roomID, "") | ||
|
||
queryParams := url.Values{} | ||
queryParams.Set("dir", "b") | ||
queryParams.Set("limit", "6") | ||
res := charlie.MustDoFunc(t, "GET", []string{"_matrix", "client", "v3", "rooms", roomID, "messages"}, client.WithQueries(queryParams)) | ||
body := gjson.ParseBytes(must.ParseJSON(t, res.Body)) | ||
defer res.Body.Close() | ||
if len(body.Get("chunk").Array()) != 6 { | ||
t.Fatal("Expected 6 messages") | ||
} | ||
}) | ||
|
||
// sytest: Can reject invites over federation for rooms with version $version | ||
t.Run(fmt.Sprintf("Can reject invites over federation for rooms with version %s", roomVersion), func(t *testing.T) { | ||
t.Parallel() | ||
roomID := createRoomSynced(t, alice, map[string]interface{}{ | ||
"room_version": roomVersion, | ||
"invite": []string{charlie.UserID}, | ||
}) | ||
charlie.MustSyncUntil(t, client.SyncReq{}, client.SyncInvitedTo(charlie.UserID, roomID)) | ||
charlie.LeaveRoom(t, roomID) | ||
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncLeftFrom(charlie.UserID, roomID)) | ||
}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No check for alice to see that the invite was rejected? |
||
|
||
// sytest: Can receive redactions from regular users over federation in room version $version | ||
t.Run(fmt.Sprintf("Can receive redactions from regular users over federation in room version %s", roomVersion), func(t *testing.T) { | ||
t.Parallel() | ||
roomID := createRoomSynced(t, alice, map[string]interface{}{ | ||
"room_version": roomVersion, | ||
"invite": []string{charlie.UserID}, | ||
}) | ||
charlie.MustSyncUntil(t, client.SyncReq{}, client.SyncInvitedTo(charlie.UserID, roomID)) | ||
joinRoomSynced(t, charlie, roomID, "") | ||
eventID := sendMessageSynced(t, charlie, roomID) | ||
// redact the message | ||
res := charlie.MustDoFunc(t, "POST", []string{"_matrix", "client", "v3", "rooms", roomID, "redact", eventID}, client.WithRawBody([]byte("{}"))) | ||
js := must.ParseJSON(t, res.Body) | ||
defer res.Body.Close() | ||
redactID := must.GetJSONFieldStr(t, js, "event_id") | ||
alice.MustSyncUntil(t, client.SyncReq{}, client.SyncTimelineHas(roomID, func(result gjson.Result) bool { | ||
return redactID == result.Get("event_id").Str | ||
})) | ||
// query messages | ||
queryParams := url.Values{} | ||
queryParams.Set("dir", "b") | ||
res = alice.MustDoFunc(t, "GET", []string{"_matrix", "client", "v3", "rooms", roomID, "messages"}, client.WithQueries(queryParams)) | ||
body := gjson.ParseBytes(must.ParseJSON(t, res.Body)) | ||
defer res.Body.Close() | ||
events := body.Get("chunk").Array() | ||
// first event should be the redaction | ||
must.EqualStr(t, events[0].Get("event_id").Str, redactID, "wrong event") | ||
must.EqualStr(t, events[0].Get("redacts").Str, eventID, "wrong event") | ||
// second event should be the original event | ||
must.EqualStr(t, events[1].Get("event_id").Str, eventID, "wrong event") | ||
must.EqualStr(t, events[1].Get("unsigned.redacted_by").Str, redactID, "wrong event") | ||
}) | ||
} | ||
}) | ||
} | ||
|
||
func sendMessageSynced(t *testing.T, cl *client.CSAPI, roomID string) (eventID string) { | ||
return cl.SendEventSynced(t, roomID, b.Event{ | ||
Type: "m.room.message", | ||
Content: map[string]interface{}{ | ||
"msgtype": "m.text", | ||
"body": "hello world", | ||
}, | ||
}) | ||
} | ||
|
||
func joinRoomSynced(t *testing.T, cl *client.CSAPI, roomID, alias string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These should probably go into the Client impl to be honest. For now leave them here though. |
||
joinRoom := roomID | ||
if alias != "" { | ||
joinRoom = alias | ||
} | ||
cl.JoinRoom(t, joinRoom, []string{}) | ||
cl.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(cl.UserID, roomID)) | ||
} | ||
|
||
func createRoomSynced(t *testing.T, c *client.CSAPI, content map[string]interface{}) (roomID string) { | ||
t.Helper() | ||
roomID = c.CreateRoom(t, content) | ||
c.MustSyncUntil(t, client.SyncReq{}, client.SyncJoinedTo(c.UserID, roomID)) | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't what the sytest is doing.
So the order is: