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 the updated MSC2946 room hierarchy endpoint. #187

Merged
merged 4 commits into from
Aug 10, 2021
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
211 changes: 209 additions & 2 deletions tests/msc2946_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
// +build msc2946

// This file includes tests for MSC2946, the spaces summary API.
//
// There are currently tests for two unstable versions of it for backwards
// compatibility:
//
// * The /spaces endpoint, which was the original version.
// * The /hierarchy endpoint, which is an updated version.
//
// Both endpoints return data from the same set of rooms / spaces, but have
// different API shapes.
//
// TODO When support for this is stable, the tests for /spaces should be removed.

package tests

import (
Expand Down Expand Up @@ -42,6 +55,7 @@ func eventKey(srcRoomID, dstRoomID, evType string) string {
// - the user is joined to all rooms except R4.
// - R2 <---> Root is a two-way link.
// - The remaining links are just children links.
// - R1 and R2 are suggested rooms.
//
// Tests that:
// - Querying the root returns the entire graph
Expand Down Expand Up @@ -125,7 +139,8 @@ func TestClientSpacesSummary(t *testing.T) {
Type: spaceChildEventType,
StateKey: &r1,
Content: map[string]interface{}{
"via": []string{"hs1"},
"via": []string{"hs1"},
"suggested": true,
},
})
rootToSS1 := eventKey(root, ss1, spaceChildEventType)
Expand All @@ -141,7 +156,8 @@ func TestClientSpacesSummary(t *testing.T) {
Type: spaceChildEventType,
StateKey: &r2,
Content: map[string]interface{}{
"via": []string{"hs1"},
"via": []string{"hs1"},
"suggested": true,
},
})
// Note that this link gets ignored since R2 is not a space.
Expand Down Expand Up @@ -220,6 +236,39 @@ func TestClientSpacesSummary(t *testing.T) {
}, nil),
},
})

res = alice.MustDo(t, "GET", []string{"_matrix", "client", "unstable", "org.matrix.msc2946", "rooms", root, "hierarchy"}, nil)
must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
match.JSONCheckOff("rooms", []interface{}{
root, r1, r2, r3, r4, ss1, ss2,
}, func(r gjson.Result) interface{} {
return r.Get("room_id").Str
}, func(roomInt interface{}, data gjson.Result) error {
roomID := roomInt.(string)
// check fields
if name, ok := roomNames[roomID]; ok {
if data.Get("name").Str != name {
return fmt.Errorf("room %s got name %s want %s", roomID, data.Get("name").Str, name)
}
}
if roomID == ss1 {
wantType := "m.space"
if data.Get("room_type").Str != wantType {
return fmt.Errorf("room %s got type %s want %s", roomID, data.Get("room_type").Str, wantType)
}
}
return nil
}),
// Check that the links from Root down to other rooms and spaces exist.
match.JSONCheckOff("rooms.#.children_state|@flatten", []interface{}{
rootToR1, rootToR2, rootToSS1,
ss1ToSS2, ss2ToR3, ss2ToR4,
}, func(r gjson.Result) interface{} {
return eventKey(r.Get("room_id").Str, r.Get("state_key").Str, r.Get("type").Str)
}, nil),
},
})
})

// - Setting max_rooms_per_space works correctly
Expand Down Expand Up @@ -251,6 +300,103 @@ func TestClientSpacesSummary(t *testing.T) {
}
})

// - Setting max_depth works correctly
t.Run("max_depth", func(t *testing.T) {
// Should only include R1, SS1, and R2.
query := make(url.Values, 1)
query.Set("max_depth", "1")
res := alice.MustDoFunc(
t,
"GET",
[]string{"_matrix", "client", "unstable", "org.matrix.msc2946", "rooms", root, "hierarchy"},
client.WithQueries(query),
)
must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
match.JSONCheckOff("rooms", []interface{}{
root, r1, r2, ss1,
}, func(r gjson.Result) interface{} {
return r.Get("room_id").Str
}, nil),
// All of the links are still there.
match.JSONCheckOff("rooms.#.children_state|@flatten", []interface{}{
rootToR1, rootToR2, rootToSS1, ss1ToSS2,
}, func(r gjson.Result) interface{} {
return eventKey(r.Get("room_id").Str, r.Get("state_key").Str, r.Get("type").Str)
}, nil),
},
})
})

// - Setting suggested_only works correctly
t.Run("suggested_only", func(t *testing.T) {
// Should only include R1, SS1, and R2.
query := make(url.Values, 1)
query.Set("suggested_only", "true")
res := alice.MustDoFunc(
t,
"GET",
[]string{"_matrix", "client", "unstable", "org.matrix.msc2946", "rooms", root, "hierarchy"},
client.WithQueries(query),
)
must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
match.JSONCheckOff("rooms", []interface{}{
root, r1, r2,
}, func(r gjson.Result) interface{} {
return r.Get("room_id").Str
}, nil),
// All of the links are still there.
match.JSONCheckOff("rooms.#.children_state|@flatten", []interface{}{
rootToR1, rootToR2,
}, func(r gjson.Result) interface{} {
return eventKey(r.Get("room_id").Str, r.Get("state_key").Str, r.Get("type").Str)
}, nil),
},
})
})

// - Setting max_depth works correctly
t.Run("pagination", func(t *testing.T) {
// The initial page should only include Root, R1, SS1, and SS2.
query := make(url.Values, 1)
query.Set("limit", "4")
res := alice.MustDoFunc(
t,
"GET",
[]string{"_matrix", "client", "unstable", "org.matrix.msc2946", "rooms", root, "hierarchy"},
client.WithQueries(query),
)
body := must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
match.JSONCheckOff("rooms", []interface{}{
root, r1, ss1, ss2,
}, func(r gjson.Result) interface{} {
return r.Get("room_id").Str
}, nil),
},
})

// The following page should include R3, R4, and R2.
query = make(url.Values, 1)
query.Set("from", client.GetJSONFieldStr(t, body, "next_token"))
res = alice.MustDoFunc(
t,
"GET",
[]string{"_matrix", "client", "unstable", "org.matrix.msc2946", "rooms", root, "hierarchy"},
client.WithQueries(query),
)
must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
match.JSONCheckOff("rooms", []interface{}{
r3, r4, r2,
}, func(r gjson.Result) interface{} {
return r.Get("room_id").Str
}, nil),
},
})
})

t.Run("redact link", func(t *testing.T) {
// Remove the root -> SS1 link
alice.SendEventSynced(t, root, b.Event{
Expand All @@ -273,6 +419,22 @@ func TestClientSpacesSummary(t *testing.T) {
}, nil),
},
})

res = alice.MustDo(t, "GET", []string{"_matrix", "client", "unstable", "org.matrix.msc2946", "rooms", root, "hierarchy"}, nil)
must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
match.JSONCheckOff("rooms", []interface{}{
root, r1, r2,
}, func(r gjson.Result) interface{} {
return r.Get("room_id").Str
}, nil),
match.JSONCheckOff("rooms.#.children_state|@flatten", []interface{}{
rootToR1, rootToR2,
}, func(r gjson.Result) interface{} {
return eventKey(r.Get("room_id").Str, r.Get("state_key").Str, r.Get("type").Str)
}, nil),
},
})
})
}

Expand Down Expand Up @@ -387,6 +549,21 @@ func TestClientSpacesSummaryJoinRules(t *testing.T) {
}, nil),
},
})
res = bob.MustDo(t, "GET", []string{"_matrix", "client", "unstable", "org.matrix.msc2946", "rooms", root, "hierarchy"}, nil)
must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
match.JSONCheckOff("rooms", []interface{}{
root,
}, func(r gjson.Result) interface{} {
return r.Get("room_id").Str
}, nil),
match.JSONCheckOff("rooms.#.children_state|@flatten", []interface{}{
rootToR1, rootToSS1,
}, func(r gjson.Result) interface{} {
return eventKey(r.Get("room_id").Str, r.Get("state_key").Str, r.Get("type").Str)
}, nil),
},
})

// Invite to R1 and R3, querying again should only show R1 (since SS1 is not visible).
alice.InviteRoom(t, r1, bob.UserID)
Expand All @@ -407,6 +584,21 @@ func TestClientSpacesSummaryJoinRules(t *testing.T) {
}, nil),
},
})
res = bob.MustDo(t, "GET", []string{"_matrix", "client", "unstable", "org.matrix.msc2946", "rooms", root, "hierarchy"}, nil)
must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
match.JSONCheckOff("rooms", []interface{}{
root, r1,
}, func(r gjson.Result) interface{} {
return r.Get("room_id").Str
}, nil),
match.JSONCheckOff("rooms.#.children_state|@flatten", []interface{}{
rootToR1, rootToSS1,
}, func(r gjson.Result) interface{} {
return eventKey(r.Get("room_id").Str, r.Get("state_key").Str, r.Get("type").Str)
}, nil),
},
})

// Invite to SS1 and it now appears, as well as the rooms under it.
alice.InviteRoom(t, ss1, bob.UserID)
Expand All @@ -426,6 +618,21 @@ func TestClientSpacesSummaryJoinRules(t *testing.T) {
}, nil),
},
})
res = bob.MustDo(t, "GET", []string{"_matrix", "client", "unstable", "org.matrix.msc2946", "rooms", root, "hierarchy"}, nil)
must.MatchResponse(t, res, match.HTTPResponse{
JSON: []match.JSON{
match.JSONCheckOff("rooms", []interface{}{
root, r1, ss1, r2, r3,
}, func(r gjson.Result) interface{} {
return r.Get("room_id").Str
}, nil),
match.JSONCheckOff("rooms.#.children_state|@flatten", []interface{}{
rootToR1, rootToSS1, ss1ToR2, ss1ToR3,
}, func(r gjson.Result) interface{} {
return eventKey(r.Get("room_id").Str, r.Get("state_key").Str, r.Get("type").Str)
}, nil),
},
})
}

// Tests that MSC2946 works over federation. Creates a space directory like:
Expand Down