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 more tests for receiving events while resyncing for a partial join #419

Merged
Merged
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a07b041
Factor out CreateMessageEvent() function
Jul 21, 2022
7b483be
Factor out `testReceiveEventDuringPartialStateJoin()` function
Jul 21, 2022
bd2b688
Add `handleGetMissingEventsRequests()` function to respond to `/get_m…
Jul 21, 2022
d085f11
Test whether the homeserver thinks it has full state at a received event
Jul 21, 2022
2cbeaae
Allow explicitly specified `/state` and `/state_ids` requests to comp…
Jul 21, 2022
894d755
Add simple test for receiving an event with a missing prev event
Jul 21, 2022
0643979
Add a test for receiving an event with one missing and one known prev…
Jul 21, 2022
5e38528
Add a test for receiving an event with a missing prev event, with one…
Jul 21, 2022
2979d24
fixup: s/sent/created/
Jul 22, 2022
a5b3c71
fixup: define StateIDsResult struct and send /state_id failures down …
Jul 22, 2022
8f5b930
fixup: rename new tests to use parents/grandparents terminology
Jul 22, 2022
49e35b4
Revert "Allow explicitly specified `/state` and `/state_ids` requests…
Jul 22, 2022
1adf5aa
Faster joins tests: make request handlers more specific
richvdh Jul 21, 2022
74dc057
fixup: Add explicit /state_id and /state handlers instead
Jul 22, 2022
5a9832f
Log which /state_ids request is being replied to
Jul 22, 2022
d437bfb
fixup: explain purpose of deferred channel read
Jul 22, 2022
8abc0e8
fixup: link to synapse#13288
Jul 22, 2022
8f0b234
Merge remote-tracking branch 'origin/main' into squah/faster_room_joi…
Jul 22, 2022
98907c0
fixup: Use WithRetryUntil to retry /event
Jul 25, 2022
47ce7ba
Use Context.WithTimeout instead of a SendFederationRequest goroutine
Jul 25, 2022
1a0fa9f
Add comment explaining purpose of early /state_ids request
Jul 25, 2022
e3d8197
fixup: Remove .vscode/settings.json
Jul 26, 2022
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
128 changes: 69 additions & 59 deletions tests/federation_room_join_partial_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,66 +143,8 @@ func TestPartialStateJoin(t *testing.T) {

// derek sends an event in the room
event := psjResult.CreateMessageEvent(t, "derek", nil)
psjResult.Server.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{event.JSON()}, nil)
t.Logf("Derek sent event event ID %s", event.EventID())
Copy link
Member

Choose a reason for hiding this comment

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

s/sent/created/ here


/* TODO: check that a lazy-loading sync can see the event. Currently this doesn't work, because /sync blocks.
* https://github.com/matrix-org/synapse/issues/13146
alice.MustSyncUntil(t,
client.SyncReq{
Filter: buildLazyLoadingSyncFilter(nil),
},
client.SyncTimelineHasEventID(psjResult.ServerRoom.RoomID, event.EventID()),
)
*/

// still, Alice should be able to see the event with an /event request. We might have to try it a few times.
start := time.Now()
for {
if time.Since(start) > time.Second {
t.Fatalf("timeout waiting for received event to be visible")
}
res := alice.DoFunc(t, "GET", []string{"_matrix", "client", "r0", "rooms", psjResult.ServerRoom.RoomID, "event", event.EventID()})
eventResBody := client.ParseJSON(t, res)
if res.StatusCode == 200 {
t.Logf("Successfully fetched received event %s", event.EventID())
break
}
if res.StatusCode == 404 && gjson.GetBytes(eventResBody, "errcode").String() == "M_NOT_FOUND" {
t.Logf("Fetching received event failed with M_NOT_FOUND; will retry")
time.Sleep(100 * time.Millisecond)
continue
}
t.Fatalf("GET /event failed with %d: %s", res.StatusCode, string(eventResBody))
}

// allow the partial join to complete
psjResult.FinishStateRequest()
alice.MustSyncUntil(t,
client.SyncReq{},
client.SyncJoinedTo(alice.UserID, psjResult.ServerRoom.RoomID),
)

// check the server's idea of the state at the event. We do this by making a `state_ids` request over federation
stateReq := gomatrixserverlib.NewFederationRequest("GET", "hs1",
fmt.Sprintf("/_matrix/federation/v1/state_ids/%s?event_id=%s",
url.PathEscape(psjResult.ServerRoom.RoomID),
url.QueryEscape(event.EventID()),
),
)
var respStateIDs gomatrixserverlib.RespStateIDs
if err := psjResult.Server.SendFederationRequest(deployment, stateReq, &respStateIDs); err != nil {
t.Errorf("/state_ids request returned non-200: %s", err)
return
}
var gotState, expectedState []interface{}
for _, ev := range respStateIDs.StateEventIDs {
gotState = append(gotState, ev)
}
for _, ev := range psjResult.ServerRoom.AllCurrentState() {
expectedState = append(expectedState, ev.EventID())
}
must.CheckOffAll(t, gotState, expectedState)
testReceiveEventDuringPartialStateJoin(t, deployment, alice, psjResult, event)
})

// a request to (client-side) /members?at= should block until the (federation) /state request completes
Expand Down Expand Up @@ -505,6 +447,74 @@ func TestPartialStateJoin(t *testing.T) {
})
}

// test reception of an event over federation during a resync
// sends the given event to the homeserver under test, checks that a client can see it and checks
// the state at the event
func testReceiveEventDuringPartialStateJoin(
t *testing.T, deployment *docker.Deployment, alice *client.CSAPI, psjResult partialStateJoinResult, event *gomatrixserverlib.Event,
) {
// send the event to the homeserver
psjResult.Server.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{event.JSON()}, nil)

/* TODO: check that a lazy-loading sync can see the event. Currently this doesn't work, because /sync blocks.
* https://github.com/matrix-org/synapse/issues/13146
alice.MustSyncUntil(t,
client.SyncReq{
Filter: buildLazyLoadingSyncFilter(nil),
},
client.SyncTimelineHasEventID(psjResult.ServerRoom.RoomID, event.EventID()),
)
*/

// still, Alice should be able to see the event with an /event request. We might have to try it a few times.
start := time.Now()
for {
if time.Since(start) > time.Second {
t.Fatalf("timeout waiting for received event to be visible")
}
res := alice.DoFunc(t, "GET", []string{"_matrix", "client", "r0", "rooms", psjResult.ServerRoom.RoomID, "event", event.EventID()})
Copy link
Member

Choose a reason for hiding this comment

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

You can now use WithRetryUntil in your DoFunc so you can remove the for loop etc. It would look a bit like:

res := alice.DoFunc(t, "GET", []string{"_matrix", "client", "r0", "rooms", psjResult.ServerRoom.RoomID, "event", event.EventID()},
client.WithRetryUntil(time.Second, func(res *http.Response) bool {
    if res.StatusCode == 200 {
        return true
    }
    eventResBody := client.ParseJSON(t, res)
    if res.StatusCode == 404 && gjson.GetBytes(eventResBody, "errcode").String() == "M_NOT_FOUND" {
        return false
    }
    t.Fatalf("GET /event failed with %d: %s", res.StatusCode, string(eventResBody))
    return false
})

eventResBody := client.ParseJSON(t, res)
if res.StatusCode == 200 {
t.Logf("Successfully fetched received event %s", event.EventID())
break
}
if res.StatusCode == 404 && gjson.GetBytes(eventResBody, "errcode").String() == "M_NOT_FOUND" {
t.Logf("Fetching received event failed with M_NOT_FOUND; will retry")
time.Sleep(100 * time.Millisecond)
continue
}
t.Fatalf("GET /event failed with %d: %s", res.StatusCode, string(eventResBody))
}

// allow the partial join to complete
psjResult.FinishStateRequest()
alice.MustSyncUntil(t,
client.SyncReq{},
client.SyncJoinedTo(alice.UserID, psjResult.ServerRoom.RoomID),
)

// check the server's idea of the state at the event. We do this by making a `state_ids` request over federation
stateReq := gomatrixserverlib.NewFederationRequest("GET", "hs1",
fmt.Sprintf("/_matrix/federation/v1/state_ids/%s?event_id=%s",
url.PathEscape(psjResult.ServerRoom.RoomID),
url.QueryEscape(event.EventID()),
),
)
var respStateIDs gomatrixserverlib.RespStateIDs
if err := psjResult.Server.SendFederationRequest(deployment, stateReq, &respStateIDs); err != nil {
t.Errorf("/state_ids request returned non-200: %s", err)
return
}
var gotState, expectedState []interface{}
for _, ev := range respStateIDs.StateEventIDs {
gotState = append(gotState, ev)
}
for _, ev := range psjResult.ServerRoom.AllCurrentState() {
expectedState = append(expectedState, ev.EventID())
}
must.CheckOffAll(t, gotState, expectedState)
}

// buildLazyLoadingSyncFilter constructs a json-marshalled filter suitable the 'Filter' field of a client.SyncReq
func buildLazyLoadingSyncFilter(timelineOptions map[string]interface{}) string {
timelineFilter := map[string]interface{}{
Expand Down