Skip to content

Commit

Permalink
Merge pull request #68 from matrix-org/eric/msc2716-backfilling-history
Browse files Browse the repository at this point in the history
Add tests for MSC2716 and backfilling history
  • Loading branch information
MadLittleMods committed Jul 15, 2021
2 parents b383ce0 + 0b0355b commit 91b0879
Show file tree
Hide file tree
Showing 8 changed files with 957 additions and 26 deletions.
2 changes: 2 additions & 0 deletions dockerfiles/synapse/homeserver.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,5 @@ experimental_features:
msc2403_enabled: true
# Enable spaces support
spaces_enabled: true
# Enable history backfilling support
msc2716_enabled: true
2 changes: 2 additions & 0 deletions dockerfiles/synapse/workers-shared.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,7 @@ federation_rr_transactions_per_room_per_second: 9999
experimental_features:
# Enable knocking support
msc2403_enabled: true
# Enable history backfilling support
msc2716_enabled: true
# Enable spaces support
spaces_enabled: true
11 changes: 10 additions & 1 deletion internal/b/hs_with_application_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package b

// BlueprintHSWithApplicationService who has an application service to interact with
var BlueprintHSWithApplicationService = MustValidate(Blueprint{
Name: "alice",
Name: "hs_with_application_service",
Homeservers: []Homeserver{
{
Name: "hs1",
Expand All @@ -21,5 +21,14 @@ var BlueprintHSWithApplicationService = MustValidate(Blueprint{
},
},
},
{
Name: "hs2",
Users: []User{
{
Localpart: "@charlie",
DisplayName: "Charlie",
},
},
},
},
})
30 changes: 28 additions & 2 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ func (c *CSAPI) SendEventSynced(t *testing.T, roomID string, e b.Event) string {
// Will time out after CSAPI.SyncUntilTimeout.
func (c *CSAPI) SyncUntilTimelineHas(t *testing.T, roomID string, check func(gjson.Result) bool) {
t.Helper()
c.SyncUntil(t, "", "rooms.join."+GjsonEscape(roomID)+".timeline.events", check)
c.SyncUntil(t, "", "", "rooms.join."+GjsonEscape(roomID)+".timeline.events", check)
}

// SyncUntil blocks and continually calls /sync until the `check` function returns true.
// If the `check` function fails the test, the failing event will be automatically logged.
// Will time out after CSAPI.SyncUntilTimeout.
func (c *CSAPI) SyncUntil(t *testing.T, since, key string, check func(gjson.Result) bool) {
func (c *CSAPI) SyncUntil(t *testing.T, since, filter, key string, check func(gjson.Result) bool) {
t.Helper()
start := time.Now()
checkCounter := 0
Expand All @@ -152,6 +152,9 @@ func (c *CSAPI) SyncUntil(t *testing.T, since, key string, check func(gjson.Resu
if since != "" {
query["since"] = []string{since}
}
if filter != "" {
query["filter"] = []string{filter}
}
res := c.MustDoFunc(t, "GET", []string{"_matrix", "client", "r0", "sync"}, WithQueries(query))
body := ParseJSON(t, res)
since = GetJSONFieldStr(t, body, "next_batch")
Expand Down Expand Up @@ -369,6 +372,29 @@ func GetJSONFieldStr(t *testing.T, body []byte, wantKey string) string {
return res.Str
}

func GetJSONFieldStringArray(t *testing.T, body []byte, wantKey string) []string {
t.Helper()

res := gjson.GetBytes(body, wantKey)

if !res.Exists() {
t.Fatalf("JSONFieldStr: key '%s' missing from %s", wantKey, string(body))
}

arrLength := len(res.Array())
arr := make([]string, arrLength)
i := 0
res.ForEach(func(key, value gjson.Result) bool {
arr[i] = value.Str

// Keep iterating
i++
return true
})

return arr
}

// ParseJSON parses a JSON-encoded HTTP Response body into a byte slice
func ParseJSON(t *testing.T, res *http.Response) []byte {
t.Helper()
Expand Down
4 changes: 3 additions & 1 deletion internal/docker/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,9 @@ func generateASRegistrationYaml(as b.ApplicationService) string {
fmt.Sprintf("sender_localpart: %s\n", as.SenderLocalpart) +
fmt.Sprintf("rate_limited: %v\n", as.RateLimited) +
"namespaces:\n" +
" users: []\n" +
" users:\n" +
" - exclusive: false\n" +
" regex: .*\n" +
" rooms: []\n" +
" aliases: []\n"
}
Expand Down
73 changes: 51 additions & 22 deletions internal/match/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,7 @@ func JSONKeyTypeEqual(wantKey string, wantType gjson.Type) JSON {
}
}

// JSONCheckOff returns a matcher which will loop over `wantKey` and ensure that the items
// (which can be array elements or object keys)
// are present exactly once in any order in `wantItems`. If there are unexpected items or items
// appear more than once then the match fails. This matcher can be used to check off items in
// an array/object. The `mapper` function should map the item to an interface which will be
// comparable via `reflect.DeepEqual` with items in `wantItems`. The optional `fn` callback
// allows more checks to be performed other than checking off the item from the list. It is
// called with 2 args: the result of the `mapper` function and the element itself (or value if
// it's an object).
//
// Usage: (ensures `events` has these events in any order, with the right event type)
// JSONCheckOff("events", []interface{}{"$foo:bar", "$baz:quuz"}, func(r gjson.Result) interface{} {
// return r.Get("event_id").Str
// }, func(eventID interface{}, eventBody gjson.Result) error {
// if eventBody.Get("type").Str != "m.room.message" {
// return fmt.Errorf("expected event to be 'm.room.message'")
// }
// })
func JSONCheckOff(wantKey string, wantItems []interface{}, mapper func(gjson.Result) interface{}, fn func(interface{}, gjson.Result) error) JSON {
func jsonCheckOffInternal(wantKey string, wantItems []interface{}, allowUnwantedItems bool, mapper func(gjson.Result) interface{}, fn func(interface{}, gjson.Result) error) JSON {
return func(body []byte) error {
res := gjson.GetBytes(body, wantKey)
if !res.Exists() {
Expand Down Expand Up @@ -103,12 +85,15 @@ func JSONCheckOff(wantKey string, wantItems []interface{}, mapper func(gjson.Res
break
}
}
if want == -1 {
if !allowUnwantedItems && want == -1 {
err = fmt.Errorf("JSONCheckOff: unexpected item %s", item)
return false
}
// delete the wanted item
wantItems = append(wantItems[:want], wantItems[want+1:]...)

if want != -1 {
// delete the wanted item
wantItems = append(wantItems[:want], wantItems[want+1:]...)
}

// do further checks
if fn != nil {
Expand All @@ -130,6 +115,50 @@ func JSONCheckOff(wantKey string, wantItems []interface{}, mapper func(gjson.Res
}
}

// JSONCheckOffAllowUnwanted returns a matcher which will loop over `wantKey` and ensure that the items
// (which can be array elements or object keys)
// are present exactly once in any order in `wantItems`. Allows unexpected items or items
// appear that more than once. This matcher can be used to check off items in
// an array/object. The `mapper` function should map the item to an interface which will be
// comparable via `reflect.DeepEqual` with items in `wantItems`. The optional `fn` callback
// allows more checks to be performed other than checking off the item from the list. It is
// called with 2 args: the result of the `mapper` function and the element itself (or value if
// it's an object).
//
// Usage: (ensures `events` has these events in any order, with the right event type)
// JSONCheckOffAllowUnwanted("events", []interface{}{"$foo:bar", "$baz:quuz"}, func(r gjson.Result) interface{} {
// return r.Get("event_id").Str
// }, func(eventID interface{}, eventBody gjson.Result) error {
// if eventBody.Get("type").Str != "m.room.message" {
// return fmt.Errorf("expected event to be 'm.room.message'")
// }
// })
func JSONCheckOffAllowUnwanted(wantKey string, wantItems []interface{}, mapper func(gjson.Result) interface{}, fn func(interface{}, gjson.Result) error) JSON {
return jsonCheckOffInternal(wantKey, wantItems, true, mapper, fn)
}

// JSONCheckOff returns a matcher which will loop over `wantKey` and ensure that the items
// (which can be array elements or object keys)
// are present exactly once in any order in `wantItems`. If there are unexpected items or items
// appear more than once then the match fails. This matcher can be used to check off items in
// an array/object. The `mapper` function should map the item to an interface which will be
// comparable via `reflect.DeepEqual` with items in `wantItems`. The optional `fn` callback
// allows more checks to be performed other than checking off the item from the list. It is
// called with 2 args: the result of the `mapper` function and the element itself (or value if
// it's an object).
//
// Usage: (ensures `events` has these events in any order, with the right event type)
// JSONCheckOff("events", []interface{}{"$foo:bar", "$baz:quuz"}, func(r gjson.Result) interface{} {
// return r.Get("event_id").Str
// }, func(eventID interface{}, eventBody gjson.Result) error {
// if eventBody.Get("type").Str != "m.room.message" {
// return fmt.Errorf("expected event to be 'm.room.message'")
// }
// })
func JSONCheckOff(wantKey string, wantItems []interface{}, mapper func(gjson.Result) interface{}, fn func(interface{}, gjson.Result) error) JSON {
return jsonCheckOffInternal(wantKey, wantItems, false, mapper, fn)
}

// JSONArrayEach returns a matcher which will check that `wantKey` is an array then loops over each
// item calling `fn`. If `fn` returns an error, iterating stops and an error is returned.
func JSONArrayEach(wantKey string, fn func(gjson.Result) error) JSON {
Expand Down
2 changes: 2 additions & 0 deletions tests/msc2403_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ func knockingBetweenTwoUsersTest(t *testing.T, roomID string, inRoomUser, knocki
knockingUser.SyncUntil(
t,
since,
"",
"rooms.leave."+client.GjsonEscape(roomID)+".timeline.events",
func(ev gjson.Result) bool {
if ev.Get("type").Str != "m.room.member" || ev.Get("sender").Str != knockingUser.UserID {
Expand Down Expand Up @@ -313,6 +314,7 @@ func knockOnRoomSynced(t *testing.T, c *client.CSAPI, roomID, reason string, ser
c.SyncUntil(
t,
"",
"",
"rooms.knock."+client.GjsonEscape(roomID)+".knock_state.events",
func(ev gjson.Result) bool {
// We don't currently define any required state event types to be sent.
Expand Down
Loading

0 comments on commit 91b0879

Please sign in to comment.