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

Add tests for MSC2716 and backfilling history #68

Merged
merged 93 commits into from
Jul 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
15ba451
Add tests for MSC2716 and backfilling history
MadLittleMods Jan 28, 2021
55c5a8c
Allow SendEventSynced to pass over prev_event querystring parameters
MadLittleMods Jan 29, 2021
70036ac
Switch to fire and forget sending over historical events which we don…
MadLittleMods Jan 29, 2021
dabdf5a
Override origin_server_ts
MadLittleMods Jan 30, 2021
6b77b29
Grab context response to visualize DAG
MadLittleMods Feb 2, 2021
2b2a53f
Proper test formatting
MadLittleMods Feb 3, 2021
975665d
Add m.historical to backfilled messages
MadLittleMods Feb 3, 2021
110352f
Add tests for behavior around m.historical
MadLittleMods Feb 3, 2021
8bbb929
Update docs
MadLittleMods Feb 5, 2021
22292b5
Remove docs already moved to #70
MadLittleMods Feb 5, 2021
3c332ba
Merge branch 'master' into eric/msc2716-backfilling-history
MadLittleMods Feb 9, 2021
1675fec
Enable msc2716 feature flag
MadLittleMods Feb 9, 2021
54061cb
Move SendEvent to the only test file it's used in
MadLittleMods Feb 10, 2021
3973312
Initial stab of defining AS in blueprint
MadLittleMods Feb 12, 2021
5a71989
More poking at defining app services
MadLittleMods Feb 13, 2021
c607f8d
More app service changes
MadLittleMods Feb 15, 2021
fe050a9
Passing registration by environment variable
MadLittleMods Feb 16, 2021
7625067
WIP: Make AS tokens available to use as users
MadLittleMods Feb 17, 2021
7d70586
Add hs domain to bridge MXID
MadLittleMods Feb 18, 2021
9292cf8
Copy AS registration to container instead of messy env variables
MadLittleMods Feb 22, 2021
4400459
Look for appservice registrations in the /appservices directory
MadLittleMods Feb 22, 2021
606eca3
Generate tokens on every run
MadLittleMods Feb 22, 2021
2a8ccf1
Refactor to use normalize func
MadLittleMods Feb 22, 2021
1dd26ef
Remove unused function
MadLittleMods Feb 22, 2021
4f96f44
Fix lint
MadLittleMods Feb 22, 2021
7f8c5c1
Revert some newline changes
MadLittleMods Feb 22, 2021
cf4d8e9
Add application service support to blueprints
MadLittleMods Feb 22, 2021
ee1ed9f
Merge branch 'eric/appservice-support' into eric/msc2716-backfilling-…
MadLittleMods Feb 22, 2021
614c66e
Revert always showing logs
MadLittleMods Feb 22, 2021
ccb20e6
Revert always showing logs
MadLittleMods Feb 22, 2021
12e6ec0
Add comment doc
MadLittleMods Feb 22, 2021
0d1e9bf
Merge branch 'eric/appservice-support' into eric/msc2716-backfilling-…
MadLittleMods Feb 22, 2021
1b19990
Some nits and remove the volume paths
MadLittleMods Feb 22, 2021
2d9c3b9
Merge branch 'eric/appservice-support' into eric/msc2716-backfilling-…
MadLittleMods Feb 22, 2021
c1f07c2
Address review and add comment docs
MadLittleMods Feb 22, 2021
e08e8c8
Merge branch 'eric/appservice-support' into eric/msc2716-backfilling-…
MadLittleMods Feb 22, 2021
2f78441
Refactor so our custom event type is contained within our MSC test file
MadLittleMods Feb 22, 2021
169a60d
Revert lint change already in other PR #73
MadLittleMods Feb 22, 2021
0dd18af
Merge branch 'eric/appservice-support' into eric/msc2716-backfilling-…
MadLittleMods Feb 22, 2021
c6155af
Path escape AS IDs to avoid directory traversal attacks
kegsay Feb 23, 2021
be23da8
Merge branch 'eric/appservice-support' into eric/msc2716-backfilling-…
MadLittleMods Feb 23, 2021
f353135
Merge branch 'master' into eric/msc2716-backfilling-history
MadLittleMods Feb 23, 2021
30b4f6a
Refactor tests to use variable amount of messages
MadLittleMods Feb 24, 2021
312feaa
Move test setup to the test itself
MadLittleMods Feb 24, 2021
923c41e
Add more messages to fill up sync and limit response
MadLittleMods Feb 24, 2021
63a59af
Add some better comments what we're actually doing and testing for
MadLittleMods Feb 24, 2021
172ecf2
Start of test that normal users can't backfill messages
MadLittleMods Feb 24, 2021
711ca3c
Proper message order using Synapse backfilled events and point all to…
MadLittleMods Mar 12, 2021
95b20af
Finish 403 forbidden for normal users trying to use ?prev_event
MadLittleMods Mar 15, 2021
dee8369
Add test around unknown prev event
MadLittleMods Mar 15, 2021
8978f58
Fix historical typo
MadLittleMods Mar 15, 2021
c5264d3
Work on backfilling history for a user who hasn't joined before
MadLittleMods Mar 16, 2021
16c50bb
Try to work with bulk send endpoint
MadLittleMods Mar 31, 2021
e8ca419
State test now passing after we just persist the event in Synapse and…
MadLittleMods Mar 31, 2021
bfc260e
Merge branch 'master' into eric/msc2716-backfilling-history
MadLittleMods Apr 14, 2021
6215726
Add better chunk identifiers
MadLittleMods Apr 14, 2021
367db56
Simplify JSON matcher for order or messages
MadLittleMods Apr 14, 2021
88b4e98
Update remaining tests to use new /bulksend
MadLittleMods Apr 15, 2021
90d8f7e
Test that historical messages can be federated
MadLittleMods Apr 22, 2021
ac8e770
Federation test debugging
MadLittleMods Apr 27, 2021
e1d203c
Log historical state events
MadLittleMods May 6, 2021
ec5fc15
Add tests to ensure historical messages are visible to federated user…
MadLittleMods May 6, 2021
7150004
Utilize chunk_id to connect to insertion points
MadLittleMods May 14, 2021
ed82752
Merge branch 'master' into eric/msc2716-backfilling-history
kegsay May 14, 2021
8d65ac7
Remove namespace
kegsay May 14, 2021
0145930
Add ?chunk_id query param to connect chunks
MadLittleMods May 27, 2021
37e086d
Merge branch 'eric/msc2716-backfilling-history' of github.com:matrix-…
MadLittleMods May 27, 2021
4c720f4
Add required type to register user from application service
MadLittleMods May 27, 2021
f970444
Also filter in insertion/marker events
MadLittleMods Jun 7, 2021
9ee5ad1
Switch from bulk to batch wording
MadLittleMods Jun 7, 2021
35c8f76
Use unstable endpoint for MSC2716 batch send
MadLittleMods Jun 17, 2021
3890321
Merge branch 'master' into eric/msc2716-backfilling-history
MadLittleMods Jun 21, 2021
007877f
Refactor to use functional client functions
MadLittleMods Jun 21, 2021
c03e49e
Fix blueprint name conflict
MadLittleMods Jun 21, 2021
3611002
Skip federation tests
MadLittleMods Jun 21, 2021
29582e5
Some cleanup
MadLittleMods Jun 21, 2021
603fd8f
Remove some unused bits
MadLittleMods Jun 25, 2021
b6b615d
Rename to eventIDs and better comments
MadLittleMods Jun 26, 2021
9a3da4e
Fix assertion when no events are returned and other nits
MadLittleMods Jun 28, 2021
dcd84ad
Re-use one test setup with sub-tests
MadLittleMods Jun 28, 2021
4875ef8
Revert "Re-use one test setup with sub-tests"
MadLittleMods Jun 28, 2021
e56c872
Add test for multiple senders within a single chunk
MadLittleMods Jun 30, 2021
d63fb7f
Make backfill vs batch less confusing
MadLittleMods Jul 7, 2021
34756a0
Use body bytes instead of reforming a stream buffer again
MadLittleMods Jul 7, 2021
3224030
Move comment to relevant test
MadLittleMods Jul 7, 2021
3ac2176
Backfill to batch send rename
MadLittleMods Jul 7, 2021
1d09b18
Make sure to register all virtual users
MadLittleMods Jul 9, 2021
9c68cb5
Add homeserver name to client request logging to differentiate who/where
MadLittleMods Jul 9, 2021
1f780ec
Allow subsequent joiners to be able to see the message history
MadLittleMods Jul 10, 2021
425206d
Some cleanup and improving federation tests
MadLittleMods Jul 14, 2021
53f5347
Add another test for pre-made insertion event
MadLittleMods Jul 14, 2021
991e91c
Make federation tests more robust
MadLittleMods Jul 15, 2021
0b0355b
Make chunk breakdown comment more clear
MadLittleMods Jul 15, 2021
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
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",
},
},
},
Copy link
Contributor Author

@MadLittleMods MadLittleMods Jun 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added so we can test federation cases with MSC2716. The federation tests are currently skipped until we implement the "marker" and "insertion" events in the Synapse implementation.

},
})
43 changes: 35 additions & 8 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 @@ -324,7 +327,7 @@ func (c *CSAPI) DoFunc(t *testing.T, method string, paths []string, opts ...Requ
}

// NewLoggedClient returns an http.Client which logs requests/responses
func NewLoggedClient(t *testing.T, cli *http.Client) *http.Client {
func NewLoggedClient(t *testing.T, hsName string, cli *http.Client) *http.Client {
t.Helper()
if cli == nil {
cli = &http.Client{
Expand All @@ -335,22 +338,23 @@ func NewLoggedClient(t *testing.T, cli *http.Client) *http.Client {
if transport == nil {
transport = http.DefaultTransport
}
cli.Transport = &loggedRoundTripper{t, transport}
cli.Transport = &loggedRoundTripper{t, hsName, transport}
return cli
}

type loggedRoundTripper struct {
t *testing.T
wrap http.RoundTripper
t *testing.T
hsName string
wrap http.RoundTripper
}

func (t *loggedRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
start := time.Now()
res, err := t.wrap.RoundTrip(req)
if err != nil {
t.t.Logf("%s %s => error: %s (%s)", req.Method, req.URL.Path, err, time.Since(start))
t.t.Logf("%s %s%s => error: %s (%s)", req.Method, t.hsName, req.URL.Path, err, time.Since(start))
} else {
t.t.Logf("%s %s => %s (%s)", req.Method, req.URL.Path, res.Status, time.Since(start))
t.t.Logf("%s %s%s => %s (%s)", req.Method, t.hsName, req.URL.Path, res.Status, time.Since(start))
}
return res, err
}
Expand All @@ -368,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" +
kegsay marked this conversation as resolved.
Show resolved Hide resolved
" rooms: []\n" +
" aliases: []\n"
}
Expand Down
4 changes: 2 additions & 2 deletions internal/docker/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func (d *Deployment) Client(t *testing.T, hsName, userID string) *client.CSAPI {
UserID: userID,
AccessToken: token,
BaseURL: dep.BaseURL,
Client: client.NewLoggedClient(t, nil),
Client: client.NewLoggedClient(t, hsName, nil),
SyncUntilTimeout: 5 * time.Second,
Debug: d.Deployer.debugLogging,
}
Expand All @@ -69,7 +69,7 @@ func (d *Deployment) RegisterUser(t *testing.T, hsName, localpart, password stri
}
client := &client.CSAPI{
BaseURL: dep.BaseURL,
Client: client.NewLoggedClient(t, nil),
Client: client.NewLoggedClient(t, hsName, nil),
SyncUntilTimeout: 5 * time.Second,
Debug: d.Deployer.debugLogging,
}
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 @@ -143,6 +143,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 @@ -274,6 +275,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