@@ -9,6 +9,7 @@ package tests
99
1010import (
1111 "fmt"
12+ "net/http"
1213 "net/url"
1314 "strconv"
1415 "testing"
@@ -204,19 +205,88 @@ func TestJumpToDateEndpoint(t *testing.T) {
204205 mustCheckEventisReturnedForTime (t , remoteCharlie , roomID , timeBeforeRoomCreation , "b" , importedEventID )
205206 })
206207
207- t .Run ("can paginate after getting remote event from timestamp to event endpoint" , func (t * testing.T ) {
208+ t .Run ("can paginate backwards after getting remote event from timestamp to event endpoint (start) " , func (t * testing.T ) {
208209 t .Parallel ()
209210 roomID , eventA , eventB := createTestRoom (t , alice )
210211 remoteCharlie .MustJoinRoom (t , roomID , []spec.ServerName {
211212 deployment .GetFullyQualifiedHomeserverName (t , "hs1" ),
212213 })
214+ // After Charlie's homeserver finds the event, it "should try to backfill this
215+ // event" (per the spec,
216+ // https://spec.matrix.org/v1.17/server-server-api/#get_matrixfederationv1timestamp_to_eventroomid)
213217 mustCheckEventisReturnedForTime (t , remoteCharlie , roomID , eventB .AfterTimestamp , "b" , eventB .EventID )
214218
215- // Get a pagination token from eventB
219+ // And then "clients can call /rooms/{roomId}/context/{eventId} to obtain a
220+ // pagination token to retrieve the events around the returned event." (per the
221+ // spec, https://spec.matrix.org/v1.17/client-server-api/#get_matrixclientv1roomsroomidtimestamp_to_event).
222+ //
223+ // Get a pagination token that represents the position just *before* eventB
216224 contextRes := remoteCharlie .MustDo (t , "GET" , []string {"_matrix" , "client" , "r0" , "rooms" , roomID , "context" , eventB .EventID },
217225 client .WithContentType ("application/json" ), client .WithQueries (url.Values {
218226 "limit" : []string {"0" },
219227 }),
228+ // Retry as the worker backfilling and persisting the event isn't necessarily
229+ // the same as the worker serving `/context`
230+ client .WithRetryUntil (remoteCharlie .SyncUntilTimeout , func (res * http.Response ) bool {
231+ return res .StatusCode == 200
232+ }),
233+ )
234+ contextResResBody := client .ParseJSON (t , contextRes )
235+ // Remember: Tokens are positions between events.
236+ //
237+ // start end
238+ // | |
239+ // [A] <-- ▼ [B] ▼ <--- [remoteCharlie join]
240+ //
241+ // "start" is the token that represents the position just *before* eventB
242+ paginationToken := client .GetJSONFieldStr (t , contextResResBody , "start" )
243+
244+ // Paginate backwards seamlessly from the `/context` request (start, point
245+ // before eventB)
246+ messagesRes := remoteCharlie .MustDo (t , "GET" , []string {"_matrix" , "client" , "r0" , "rooms" , roomID , "messages" },
247+ client .WithContentType ("application/json" ),
248+ client .WithQueries (url.Values {
249+ "dir" : []string {"b" },
250+ "limit" : []string {"100" },
251+ "from" : []string {paginationToken },
252+ }),
253+ )
254+
255+ // Make sure A is visible
256+ must .MatchResponse (t , messagesRes , match.HTTPResponse {
257+ JSON : []match.JSON {
258+ match .JSONCheckOff ("chunk" , []interface {}{eventA .EventID }, match .CheckOffMapper (func (r gjson.Result ) interface {} {
259+ return r .Get ("event_id" ).Str
260+ }), match .CheckOffAllowUnwanted ()),
261+ },
262+ })
263+ })
264+
265+ t .Run ("can paginate backwards after getting remote event from timestamp to event endpoint (end)" , func (t * testing.T ) {
266+ t .Parallel ()
267+ roomID , eventA , eventB := createTestRoom (t , alice )
268+ remoteCharlie .MustJoinRoom (t , roomID , []spec.ServerName {
269+ deployment .GetFullyQualifiedHomeserverName (t , "hs1" ),
270+ })
271+ // After Charlie's homeserver finds the event, it "should try to backfill this
272+ // event" (per the spec,
273+ // https://spec.matrix.org/v1.17/server-server-api/#get_matrixfederationv1timestamp_to_eventroomid)
274+ mustCheckEventisReturnedForTime (t , remoteCharlie , roomID , eventB .AfterTimestamp , "b" , eventB .EventID )
275+
276+ // And then "clients can call /rooms/{roomId}/context/{eventId} to obtain a
277+ // pagination token to retrieve the events around the returned event." (per the
278+ // spec, https://spec.matrix.org/v1.17/client-server-api/#get_matrixclientv1roomsroomidtimestamp_to_event).
279+ //
280+ // Get a pagination token that represents the position just *after* eventB
281+ contextRes := remoteCharlie .MustDo (t , "GET" , []string {"_matrix" , "client" , "r0" , "rooms" , roomID , "context" , eventB .EventID },
282+ client .WithContentType ("application/json" ), client .WithQueries (url.Values {
283+ "limit" : []string {"0" },
284+ }),
285+ // Retry as the worker backfilling and persisting the event isn't necessarily
286+ // the same as the worker serving `/context`
287+ client .WithRetryUntil (remoteCharlie .SyncUntilTimeout , func (res * http.Response ) bool {
288+ return res .StatusCode == 200
289+ }),
220290 )
221291 contextResResBody := client .ParseJSON (t , contextRes )
222292 // Remember: Tokens are positions between events. Normally, you would use the
@@ -227,16 +297,12 @@ func TestJumpToDateEndpoint(t *testing.T) {
227297 // start end
228298 // | |
229299 // [A] <-- ▼ [B] ▼ <--- [remoteCharlie join]
300+ //
301+ // "end" is the token that represents the position just *after* eventB
230302 paginationToken := client .GetJSONFieldStr (t , contextResResBody , "end" )
231303
232- // Hit `/messages` until `eventA` has been backfilled and replicated across
233- // workers (the worker persisting events isn't necessarily the same as the worker
234- // serving `/messages`)
235- fetchUntilMessagesResponseHas (t , remoteCharlie , roomID , func (ev gjson.Result ) bool {
236- return ev .Get ("event_id" ).Str == eventA .EventID
237- })
238-
239- // Paginate backwards from the point after eventB
304+ // Paginate backwards seamlessly from the `/context` request (end, point after
305+ // eventB)
240306 messagesRes := remoteCharlie .MustDo (t , "GET" , []string {"_matrix" , "client" , "r0" , "rooms" , roomID , "messages" },
241307 client .WithContentType ("application/json" ),
242308 client .WithQueries (url.Values {
@@ -357,42 +423,6 @@ func mustCheckEventisReturnedForTime(t *testing.T, c *client.CSAPI, roomID strin
357423 }
358424}
359425
360- func fetchUntilMessagesResponseHas (t * testing.T , c * client.CSAPI , roomID string , check func (gjson.Result ) bool ) {
361- t .Helper ()
362- start := time .Now ()
363- checkCounter := 0
364- for {
365- if time .Since (start ) > c .SyncUntilTimeout {
366- t .Fatalf ("fetchUntilMessagesResponseHas timed out. Called check function %d times" , checkCounter )
367- }
368-
369- messagesRes := c .MustDo (t , "GET" , []string {"_matrix" , "client" , "v3" , "rooms" , roomID , "messages" }, client .WithContentType ("application/json" ), client .WithQueries (url.Values {
370- "dir" : []string {"b" },
371- "limit" : []string {"100" },
372- }))
373- messsageResBody := client .ParseJSON (t , messagesRes )
374- wantKey := "chunk"
375- keyRes := gjson .GetBytes (messsageResBody , wantKey )
376- if ! keyRes .Exists () {
377- t .Fatalf ("missing key '%s'" , wantKey )
378- }
379- if ! keyRes .IsArray () {
380- t .Fatalf ("key '%s' is not an array (was %s)" , wantKey , keyRes .Type )
381- }
382-
383- events := keyRes .Array ()
384- for _ , ev := range events {
385- if check (ev ) {
386- return
387- }
388- }
389-
390- checkCounter ++
391- // Add a slight delay so we don't hammmer the messages endpoint
392- time .Sleep (500 * time .Millisecond )
393- }
394- }
395-
396426func getDebugMessageListFromMessagesResponse (t * testing.T , c * client.CSAPI , roomID string , expectedEventId string , actualEventId string , givenTimestamp int64 ) string {
397427 t .Helper ()
398428
0 commit comments