From 2574030959e917d7244a3078c94be06ceb71f254 Mon Sep 17 00:00:00 2001 From: Patrick Cloke Date: Tue, 10 Aug 2021 13:10:10 -0400 Subject: [PATCH] Test the updated MSC2946 room hierarchy endpoint. (#187) --- tests/msc2946_test.go | 211 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 209 insertions(+), 2 deletions(-) diff --git a/tests/msc2946_test.go b/tests/msc2946_test.go index 5280f9b2..4ef8f400 100644 --- a/tests/msc2946_test.go +++ b/tests/msc2946_test.go @@ -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 ( @@ -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 @@ -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) @@ -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. @@ -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 @@ -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{ @@ -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), + }, + }) }) } @@ -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) @@ -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) @@ -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: