Skip to content
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
99 changes: 59 additions & 40 deletions tests/msc2716_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,47 @@ func TestImportHistoricalMessages(t *testing.T) {
}
})

t.Run("Non-member state events are allowed in state_events_at_start", func(t *testing.T) {
t.Parallel()

roomID := as.CreateRoom(t, createPublicRoomOpts)
alice.JoinRoom(t, roomID, nil)

// Create the "live" event we are going to insert our historical events next to
eventIDsBefore := createMessagesInRoom(t, alice, roomID, 1, "eventIDsBefore")
eventIdBefore := eventIDsBefore[0]
timeAfterEventBefore := time.Now()

stateEventsAtStart := createJoinStateEventsForBatchSendRequest([]string{virtualUserID}, timeAfterEventBefore)
// Add the non-member state
stateEventsAtStart = append(stateEventsAtStart, map[string]interface{}{
"type": "org.matrix.msc2716.foobarbaz",
"sender": as.UserID,
"origin_server_ts": uint64(timeAfterEventBefore.UnixNano() / int64(time.Millisecond)),
"content": map[string]interface{}{
"foo": "bar",
},
"state_key": "",
})

batchSendRes := batchSendHistoricalMessages(
t,
as,
roomID,
eventIdBefore,
"",
stateEventsAtStart,
createMessageEventsForBatchSendRequest([]string{virtualUserID}, timeAfterEventBefore, 1),
// Status
200,
)
validateBatchSendRes(
t, as, roomID, batchSendRes,
// Validate that the non-member state resolves
true,
)
})

t.Run("Unrecognised prev_event ID will throw an error", func(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -1341,37 +1382,30 @@ func validateBatchSendRes(t *testing.T, c *client.CSAPI, roomID string, batchSen
expectedEventIDOrder = append(expectedEventIDOrder, reversed(historicalEventIDs)...)
expectedEventIDOrder = append(expectedEventIDOrder, insertionEventID)

// Make sure the historical events appear in scrollback without jumping back
// in time specifically.
fullMessagesRes := c.MustDoFunc(t, "GET", []string{"_matrix", "client", "r0", "rooms", roomID, "messages"}, client.WithContentType("application/json"), client.WithQueries(url.Values{
"dir": []string{"b"},
"limit": []string{"100"},
}))
must.MatchResponse(t, fullMessagesRes, match.HTTPResponse{
JSON: []match.JSON{
matcherJSONEventIDArrayInOrder("chunk",
expectedEventIDOrder,
historicalEventFilter,
),
},
})

// Validate state after we paginate `/messages` to avoid any potential 404 if
// the server hasn't backfilled here yet
if validateState {
// Get a pagination token for the newest-in-time event in the historical batch itself
contextRes := c.MustDoFunc(t, "GET", []string{"_matrix", "client", "r0", "rooms", roomID, "context", expectedEventIDOrder[0]}, client.WithContentType("application/json"), client.WithQueries(url.Values{
"limit": []string{"0"},
}))
contextResBody := client.ParseJSON(t, contextRes)
batchStartPaginationToken := client.GetJSONFieldStr(t, contextResBody, "end")

// Fetch a chunk of `/messages` which only contains the historical batch. We
// want to do this because `/messages` only returns the state for the first
// message in the `chunk` and we want to be able assert that the historical
// state is able to be resolved.
messagesRes := c.MustDoFunc(t, "GET", []string{"_matrix", "client", "r0", "rooms", roomID, "messages"}, client.WithContentType("application/json"), client.WithQueries(url.Values{
// Go backwards (newest -> oldest) (same direction as if you were using scrollback)
"dir": []string{"b"},
// From the newest-in-time event in the historical batch
"from": []string{batchStartPaginationToken},
// We are aiming to scrollback to the oldest-in-time event from the
// historical batch
"limit": []string{fmt.Sprintf("%d", len(expectedEventIDOrder))},
// We add these options to the filter so we get member events in the state field
"filter": []string{"{\"lazy_load_members\":true,\"include_redundant_members\":true}"},
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

/messages was only returning member state events and only because we passed this special filter. Now that we're testing with non-member state events, we need an endpoint which will show us them.

We can instead simplify and just use /context directly 🎉

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Bear in mind that /context does not work over federation. That is, if server A is in room X and sends 100 messages, then server B joins room X and hits /context for an event early in the timeline (so not backfilled) it will 404.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I moved the validateState block (which calls /context) after we call /messages which will backfill any of the potential gaps 👍

}))

must.MatchResponse(t, messagesRes, match.HTTPResponse{
must.MatchResponse(t, contextRes, match.HTTPResponse{
JSON: []match.JSON{
// Double-check that we're in the right place of scrollback
matcherJSONEventIDArrayInOrder("chunk",
expectedEventIDOrder,
historicalEventFilter,
),
// Make sure the historical m.room.member join state event resolves
// for the given chunk of messages in scrollback. The member event
// will include the displayname and avatar.
Expand All @@ -1381,21 +1415,6 @@ func validateBatchSendRes(t *testing.T, c *client.CSAPI, roomID string, batchSen
},
})
}

// Make sure the historical events appear in scrollback without jumping back
// in time specifically.
fullMessagesRes := c.MustDoFunc(t, "GET", []string{"_matrix", "client", "r0", "rooms", roomID, "messages"}, client.WithContentType("application/json"), client.WithQueries(url.Values{
"dir": []string{"b"},
"limit": []string{"100"},
}))
must.MatchResponse(t, fullMessagesRes, match.HTTPResponse{
JSON: []match.JSON{
matcherJSONEventIDArrayInOrder("chunk",
expectedEventIDOrder,
historicalEventFilter,
),
},
})
}

// matcherJSONEventIDArrayInOrder loops through `jsonArrayKey` in the response
Expand Down