BREAKING: Change the API to refer to lists by keys not index positions

This provides more flexibility to refer to lists as well as delete them.
This commit is contained in:
Kegan Dougal 2022-12-20 13:32:39 +00:00
parent 3143b60c9f
commit ca6ceb28da
28 changed files with 925 additions and 803 deletions

View File

@ -34,8 +34,8 @@ func TestConn(t *testing.T) {
c := NewConn(connID, &connHandlerMock{func(ctx context.Context, cid ConnID, req *Request, isInitial bool) (*Response, error) {
count += 1
return &Response{
Lists: []ResponseList{
{
Lists: map[string]ResponseList{
"a": {
Count: count,
},
},
@ -48,14 +48,14 @@ func TestConn(t *testing.T) {
})
assertNoError(t, err)
assertPos(t, resp.Pos, 1)
assertInt(t, resp.Lists[0].Count, 101)
assertInt(t, resp.Lists["a"].Count, 101)
// happy case, pos=1
resp, err = c.OnIncomingRequest(ctx, &Request{
pos: 1,
})
assertPos(t, resp.Pos, 2)
assertInt(t, resp.Lists[0].Count, 102)
assertInt(t, resp.Lists["a"].Count, 102)
assertNoError(t, err)
// bogus position returns a 400
_, err = c.OnIncomingRequest(ctx, &Request{
@ -80,10 +80,10 @@ func TestConnBlocking(t *testing.T) {
}
ch := make(chan string)
c := NewConn(connID, &connHandlerMock{func(ctx context.Context, cid ConnID, req *Request, init bool) (*Response, error) {
if req.Lists[0].Sort[0] == "hi" {
if req.Lists["a"].Sort[0] == "hi" {
time.Sleep(10 * time.Millisecond)
}
ch <- req.Lists[0].Sort[0]
ch <- req.Lists["a"].Sort[0]
return &Response{}, nil
}})
@ -95,8 +95,8 @@ func TestConnBlocking(t *testing.T) {
go func() {
defer wg.Done()
c.OnIncomingRequest(ctx, &Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"a": {
Sort: []string{"hi"},
},
},
@ -106,8 +106,8 @@ func TestConnBlocking(t *testing.T) {
defer wg.Done()
time.Sleep(1 * time.Millisecond) // this req happens 2nd
c.OnIncomingRequest(ctx, &Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"a": {
Sort: []string{"hi2"},
},
},
@ -135,37 +135,37 @@ func TestConnRetries(t *testing.T) {
callCount := 0
c := NewConn(connID, &connHandlerMock{func(ctx context.Context, cid ConnID, req *Request, init bool) (*Response, error) {
callCount += 1
return &Response{Lists: []ResponseList{
{
return &Response{Lists: map[string]ResponseList{
"a": {
Count: 20,
},
}}, nil
}})
resp, err := c.OnIncomingRequest(ctx, &Request{})
assertPos(t, resp.Pos, 1)
assertInt(t, resp.Lists[0].Count, 20)
assertInt(t, resp.Lists["a"].Count, 20)
assertInt(t, callCount, 1)
assertNoError(t, err)
resp, err = c.OnIncomingRequest(ctx, &Request{pos: 1})
assertPos(t, resp.Pos, 2)
assertInt(t, resp.Lists[0].Count, 20)
assertInt(t, resp.Lists["a"].Count, 20)
assertInt(t, callCount, 2)
assertNoError(t, err)
// retry! Shouldn't invoke handler again
resp, err = c.OnIncomingRequest(ctx, &Request{pos: 1})
assertPos(t, resp.Pos, 2)
assertInt(t, resp.Lists[0].Count, 20)
assertInt(t, resp.Lists["a"].Count, 20)
assertInt(t, callCount, 2) // this doesn't increment
assertNoError(t, err)
// retry! but with modified request body, so should invoke handler again but return older data (buffered)
resp, err = c.OnIncomingRequest(ctx, &Request{
pos: 1, Lists: []RequestList{
{
pos: 1, Lists: map[string]RequestList{
"a": {
Sort: []string{SortByName},
},
}})
assertPos(t, resp.Pos, 2)
assertInt(t, resp.Lists[0].Count, 20)
assertInt(t, resp.Lists["a"].Count, 20)
assertInt(t, callCount, 3) // this doesn't increment
assertNoError(t, err)
}
@ -178,8 +178,8 @@ func TestConnBufferRes(t *testing.T) {
callCount := 0
c := NewConn(connID, &connHandlerMock{func(ctx context.Context, cid ConnID, req *Request, init bool) (*Response, error) {
callCount += 1
return &Response{Lists: []ResponseList{
{
return &Response{Lists: map[string]ResponseList{
"a": {
Count: callCount,
},
}}, nil
@ -187,24 +187,24 @@ func TestConnBufferRes(t *testing.T) {
resp, err := c.OnIncomingRequest(ctx, &Request{})
assertNoError(t, err)
assertPos(t, resp.Pos, 1)
assertInt(t, resp.Lists[0].Count, 1)
assertInt(t, resp.Lists["a"].Count, 1)
assertInt(t, callCount, 1)
resp, err = c.OnIncomingRequest(ctx, &Request{pos: 1})
assertNoError(t, err)
assertPos(t, resp.Pos, 2)
assertInt(t, resp.Lists[0].Count, 2)
assertInt(t, resp.Lists["a"].Count, 2)
assertInt(t, callCount, 2)
// retry with modified request data, should invoke handler again!
resp, err = c.OnIncomingRequest(ctx, &Request{pos: 1, TxnID: "a"})
assertNoError(t, err)
assertPos(t, resp.Pos, 2)
assertInt(t, resp.Lists[0].Count, 2)
assertInt(t, resp.Lists["a"].Count, 2)
assertInt(t, callCount, 3) // this DOES increment, the response is buffered and not returned yet.
// retry with same request body, so should NOT invoke handler again and return buffered response
resp, err = c.OnIncomingRequest(ctx, &Request{pos: 2, TxnID: "a"})
assertNoError(t, err)
assertPos(t, resp.Pos, 3)
assertInt(t, resp.Lists[0].Count, 3)
assertInt(t, resp.Lists["a"].Count, 3)
assertInt(t, callCount, 3)
}
@ -281,8 +281,8 @@ func TestConnBufferRememberInflight(t *testing.T) {
callCount := 0
c := NewConn(connID, &connHandlerMock{func(ctx context.Context, cid ConnID, req *Request, init bool) (*Response, error) {
callCount += 1
return &Response{Lists: []ResponseList{
{
return &Response{Lists: map[string]ResponseList{
"a": {
Count: callCount,
},
}}, nil
@ -358,7 +358,7 @@ func TestConnBufferRememberInflight(t *testing.T) {
assertNoError(t, err)
}
assertPos(t, resp.Pos, step.wantResPos)
assertInt(t, resp.Lists[0].Count, step.wantResCount)
assertInt(t, resp.Lists["a"].Count, step.wantResCount)
assertInt(t, callCount, step.wantCallCount)
}
}

View File

@ -187,16 +187,18 @@ func (s *ConnState) onIncomingRequest(ctx context.Context, req *sync3.Request, i
region.End()
// counts are AFTER events are applied, hence after liveUpdate
for i := range response.Lists {
response.Lists[i].Count = s.lists.Count(i)
for listKey := range response.Lists {
l := response.Lists[listKey]
l.Count = s.lists.Count(listKey)
response.Lists[listKey] = l
}
return response, nil
}
func (s *ConnState) onIncomingListRequest(ctx context.Context, builder *RoomsBuilder, listIndex int, prevReqList, nextReqList *sync3.RequestList) sync3.ResponseList {
func (s *ConnState) onIncomingListRequest(ctx context.Context, builder *RoomsBuilder, listKey string, prevReqList, nextReqList *sync3.RequestList) sync3.ResponseList {
defer trace.StartRegion(ctx, "onIncomingListRequest").End()
roomList, overwritten := s.lists.AssignList(listIndex, nextReqList.Filters, nextReqList.Sort, sync3.DoNotOverwrite)
roomList, overwritten := s.lists.AssignList(listKey, nextReqList.Filters, nextReqList.Sort, sync3.DoNotOverwrite)
if nextReqList.ShouldGetAllRooms() {
if overwritten || prevReqList.FiltersChanged(nextReqList) {
@ -251,11 +253,11 @@ func (s *ConnState) onIncomingListRequest(ctx context.Context, builder *RoomsBui
}
if filtersChanged {
// we need to re-create the list as the rooms may have completely changed
roomList, _ = s.lists.AssignList(listIndex, nextReqList.Filters, nextReqList.Sort, sync3.Overwrite)
roomList, _ = s.lists.AssignList(listKey, nextReqList.Filters, nextReqList.Sort, sync3.Overwrite)
}
// resort as either we changed the sort order or we added/removed a bunch of rooms
if err := roomList.Sort(nextReqList.Sort); err != nil {
logger.Err(err).Int("index", listIndex).Msg("cannot sort list")
logger.Err(err).Str("key", listKey).Msg("cannot sort list")
}
addedRanges = nextReqList.Ranges
removedRanges = nil
@ -300,17 +302,17 @@ func (s *ConnState) onIncomingListRequest(ctx context.Context, builder *RoomsBui
}
}
func (s *ConnState) buildListSubscriptions(ctx context.Context, builder *RoomsBuilder, listDeltas []sync3.RequestListDelta) []sync3.ResponseList {
result := make([]sync3.ResponseList, len(s.muxedReq.Lists))
func (s *ConnState) buildListSubscriptions(ctx context.Context, builder *RoomsBuilder, listDeltas map[string]sync3.RequestListDelta) map[string]sync3.ResponseList {
result := make(map[string]sync3.ResponseList, len(s.muxedReq.Lists))
// loop each list and handle each independently
for i := range listDeltas {
if listDeltas[i].Curr == nil {
for listKey, list := range listDeltas {
if list.Curr == nil {
// they deleted this list
logger.Debug().Int("index", i).Msg("list deleted")
s.lists.DeleteList(i)
logger.Debug().Str("key", listKey).Msg("list deleted")
s.lists.DeleteList(listKey)
continue
}
result[i] = s.onIncomingListRequest(ctx, builder, i, listDeltas[i].Prev, listDeltas[i].Curr)
result[listKey] = s.onIncomingListRequest(ctx, builder, listKey, list.Prev, list.Curr)
}
return result
}

View File

@ -118,13 +118,15 @@ func (s *connStateLive) processLiveUpdate(ctx context.Context, up caches.Update,
// do per-list updates (e.g resorting, adding/removing rooms which no longer match filter)
for _, listDelta := range delta.Lists {
index := listDelta.ListIndex
list := s.lists.Get(index)
reqList := s.muxedReq.Lists[index]
updates := s.processLiveUpdateForList(ctx, builder, up, listDelta.Op, &reqList, list, &response.Lists[index])
listKey := listDelta.ListKey
list := s.lists.Get(listKey)
reqList := s.muxedReq.Lists[listKey]
resList := response.Lists[listKey]
updates := s.processLiveUpdateForList(ctx, builder, up, listDelta.Op, &reqList, list, &resList)
if updates {
hasUpdates = true
}
response.Lists[listKey] = resList
}
// add in initial rooms FIRST as we replace whatever is in the rooms key for these rooms.

View File

@ -109,7 +109,7 @@ func TestConnStateInitial(t *testing.T) {
t.Fatalf("UserID returned wrong value, got %v want %v", cs.UserID(), userID)
}
res, err := cs.OnIncomingRequest(context.Background(), ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 9},
@ -137,8 +137,8 @@ func TestConnStateInitial(t *testing.T) {
Timeline: []json.RawMessage{timeline[roomA.RoomID]},
},
},
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: 3,
Ops: []sync3.ResponseOp{
&sync3.ResponseOpRange{
@ -161,7 +161,7 @@ func TestConnStateInitial(t *testing.T) {
// request again for the diff
res, err = cs.OnIncomingRequest(context.Background(), ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 9},
@ -177,8 +177,8 @@ func TestConnStateInitial(t *testing.T) {
Timeline: []json.RawMessage{newEvent},
},
},
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: 3,
Ops: []sync3.ResponseOp{
&sync3.ResponseOpSingle{
@ -201,7 +201,7 @@ func TestConnStateInitial(t *testing.T) {
newEvent,
}, 2)
res, err = cs.OnIncomingRequest(context.Background(), ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 9},
@ -217,8 +217,8 @@ func TestConnStateInitial(t *testing.T) {
Timeline: []json.RawMessage{newEvent},
},
},
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: 3,
},
},
@ -272,7 +272,7 @@ func TestConnStateMultipleRanges(t *testing.T) {
// request first page
res, err := cs.OnIncomingRequest(context.Background(), ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 2},
@ -283,8 +283,8 @@ func TestConnStateMultipleRanges(t *testing.T) {
t.Fatalf("OnIncomingRequest returned error : %s", err)
}
checkResponse(t, true, res, &sync3.Response{
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: len(rooms),
Ops: []sync3.ResponseOp{
&sync3.ResponseOpRange{
@ -298,7 +298,7 @@ func TestConnStateMultipleRanges(t *testing.T) {
})
// add on a different non-overlapping range
res, err = cs.OnIncomingRequest(context.Background(), ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 2}, {4, 6},
@ -309,8 +309,8 @@ func TestConnStateMultipleRanges(t *testing.T) {
t.Fatalf("OnIncomingRequest returned error : %s", err)
}
checkResponse(t, true, res, &sync3.Response{
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: len(rooms),
Ops: []sync3.ResponseOp{
&sync3.ResponseOpRange{
@ -335,7 +335,7 @@ func TestConnStateMultipleRanges(t *testing.T) {
}, 1)
res, err = cs.OnIncomingRequest(context.Background(), ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 2}, {4, 6},
@ -346,8 +346,8 @@ func TestConnStateMultipleRanges(t *testing.T) {
t.Fatalf("OnIncomingRequest returned error : %s", err)
}
checkResponse(t, true, res, &sync3.Response{
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: len(rooms),
Ops: []sync3.ResponseOp{
&sync3.ResponseOpSingle{
@ -377,7 +377,7 @@ func TestConnStateMultipleRanges(t *testing.T) {
}, 1)
t.Logf("new event %s : %s", roomIDs[9], string(newEvent))
res, err = cs.OnIncomingRequest(context.Background(), ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 2}, {4, 6},
@ -388,8 +388,8 @@ func TestConnStateMultipleRanges(t *testing.T) {
t.Fatalf("OnIncomingRequest returned error : %s", err)
}
checkResponse(t, true, res, &sync3.Response{
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: len(rooms),
Ops: []sync3.ResponseOp{
&sync3.ResponseOpSingle{
@ -448,7 +448,7 @@ func TestBumpToOutsideRange(t *testing.T) {
cs := NewConnState(userID, deviceID, userCache, globalCache, &NopExtensionHandler{}, &NopJoinTracker{}, nil)
// Ask for A,B
res, err := cs.OnIncomingRequest(context.Background(), ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 1},
@ -459,8 +459,8 @@ func TestBumpToOutsideRange(t *testing.T) {
t.Fatalf("OnIncomingRequest returned error : %s", err)
}
checkResponse(t, true, res, &sync3.Response{
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: 4,
Ops: []sync3.ResponseOp{
&sync3.ResponseOpRange{
@ -485,7 +485,7 @@ func TestBumpToOutsideRange(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
res, err = cs.OnIncomingRequest(ctx, ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 1},
@ -495,7 +495,7 @@ func TestBumpToOutsideRange(t *testing.T) {
if err != nil {
t.Fatalf("OnIncomingRequest returned error : %s", err)
}
if len(res.Lists[0].Ops) > 0 {
if len(res.Lists["a"].Ops) > 0 {
t.Errorf("response returned ops, expected none")
}
}
@ -561,7 +561,7 @@ func TestConnStateRoomSubscriptions(t *testing.T) {
TimelineLimit: 20,
},
},
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 1},
@ -572,8 +572,8 @@ func TestConnStateRoomSubscriptions(t *testing.T) {
t.Fatalf("OnIncomingRequest returned error : %s", err)
}
checkResponse(t, false, res, &sync3.Response{
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: len(roomIDs),
Ops: []sync3.ResponseOp{
&sync3.ResponseOpRange{
@ -617,7 +617,7 @@ func TestConnStateRoomSubscriptions(t *testing.T) {
}, 1)
// we should get this message even though it's not in the range because we are subscribed to this room.
res, err = cs.OnIncomingRequest(context.Background(), ConnID, &sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 1},
@ -628,8 +628,8 @@ func TestConnStateRoomSubscriptions(t *testing.T) {
t.Fatalf("OnIncomingRequest returned error : %s", err)
}
checkResponse(t, false, res, &sync3.Response{
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: len(roomIDs),
},
},
@ -651,7 +651,7 @@ func TestConnStateRoomSubscriptions(t *testing.T) {
},
},
UnsubscribeRooms: []string{roomD.RoomID},
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges([][2]int64{
{0, 1},
@ -662,8 +662,8 @@ func TestConnStateRoomSubscriptions(t *testing.T) {
t.Fatalf("OnIncomingRequest returned error : %s", err)
}
checkResponse(t, false, res, &sync3.Response{
Lists: []sync3.ResponseList{
{
Lists: map[string]sync3.ResponseList{
"a": {
Count: len(roomIDs),
},
},
@ -684,11 +684,14 @@ func checkResponse(t *testing.T, checkRoomIDsOnly bool, got, want *sync3.Respons
if len(got.Lists) != len(want.Lists) {
t.Errorf("got %v lists, want %v", len(got.Lists), len(want.Lists))
}
for i := 0; i < len(want.Lists); i++ {
wl := want.Lists[i]
gl := got.Lists[i]
for listKey, wl := range want.Lists {
gl, exists := got.Lists[listKey]
if !exists {
t.Errorf("no response key for '%s'", listKey)
continue
}
if wl.Count > 0 && gl.Count != wl.Count {
t.Errorf("response list %d got count %d want %d", i, gl.Count, wl.Count)
t.Errorf("response list %v got count %d want %d", listKey, gl.Count, wl.Count)
}
if len(wl.Ops) > 0 {

View File

@ -187,11 +187,11 @@ func (h *SyncLiveHandler) serve(w http.ResponseWriter, req *http.Request) error
}
}
}
for i, l := range requestBody.Lists {
for listKey, l := range requestBody.Lists {
if l.Ranges != nil && !l.Ranges.Valid() {
return &internal.HandlerError{
StatusCode: 400,
Err: fmt.Errorf("list[%d] invalid ranges %v", i, l.Ranges),
Err: fmt.Errorf("list[%v] invalid ranges %v", listKey, l.Ranges),
}
}
}

View File

@ -26,8 +26,8 @@ var (
)
type RoomListDelta struct {
ListIndex int
Op ListOp
ListKey string
Op ListOp
}
type RoomDelta struct {
@ -42,12 +42,13 @@ type RoomDelta struct {
// lists.
type InternalRequestLists struct {
allRooms map[string]RoomConnMetadata
lists []*FilteredSortableRooms
lists map[string]*FilteredSortableRooms
}
func NewInternalRequestLists() *InternalRequestLists {
return &InternalRequestLists{
allRooms: make(map[string]RoomConnMetadata, 10),
lists: make(map[string]*FilteredSortableRooms),
}
}
@ -72,9 +73,9 @@ func (s *InternalRequestLists) SetRoom(r RoomConnMetadata) (delta RoomDelta) {
// filter.Include may call on this room ID in the RoomFinder, so make sure it finds it.
s.allRooms[r.RoomID] = r
for i := range s.lists {
_, alreadyExists := s.lists[i].roomIDToIndex[r.RoomID]
shouldExist := s.lists[i].filter.Include(&r, s)
for listKey, list := range s.lists {
_, alreadyExists := list.roomIDToIndex[r.RoomID]
shouldExist := list.filter.Include(&r, s)
if shouldExist && r.HasLeft {
shouldExist = false
}
@ -82,20 +83,20 @@ func (s *InternalRequestLists) SetRoom(r RoomConnMetadata) (delta RoomDelta) {
if alreadyExists {
if shouldExist { // could be a change
delta.Lists = append(delta.Lists, RoomListDelta{
ListIndex: i,
Op: ListOpChange,
ListKey: listKey,
Op: ListOpChange,
})
} else { // removal
delta.Lists = append(delta.Lists, RoomListDelta{
ListIndex: i,
Op: ListOpDel,
ListKey: listKey,
Op: ListOpDel,
})
}
} else {
if shouldExist { // addition
delta.Lists = append(delta.Lists, RoomListDelta{
ListIndex: i,
Op: ListOpAdd,
ListKey: listKey,
Op: ListOpAdd,
})
} // else it doesn't exist and it shouldn't exist, so do nothing e.g room isn't relevant to this list
}
@ -109,7 +110,7 @@ func (s *InternalRequestLists) RemoveRoom(roomID string) {
// TODO: update lists?
}
func (s *InternalRequestLists) DeleteList(index int) {
func (s *InternalRequestLists) DeleteList(listKey string) {
// TODO
}
@ -118,16 +119,18 @@ func (s *InternalRequestLists) Room(roomID string) *RoomConnMetadata {
return &r
}
func (s *InternalRequestLists) Get(listIndex int) *FilteredSortableRooms {
return s.lists[listIndex]
func (s *InternalRequestLists) Get(listKey string) *FilteredSortableRooms {
return s.lists[listKey]
}
// Assign a new list at the given index. If Overwrite, any existing list is replaced. If DoNotOverwrite, the existing
// Assign a new list at the given key. If Overwrite, any existing list is replaced. If DoNotOverwrite, the existing
// list is returned if one exists, else a new list is created. Returns the list and true if the list was overwritten.
func (s *InternalRequestLists) AssignList(index int, filters *RequestFilters, sort []string, shouldOverwrite OverwriteVal) (*FilteredSortableRooms, bool) {
internal.Assert("Set index is at most list size", index <= len(s.lists))
if shouldOverwrite == DoNotOverwrite && index < len(s.lists) {
return s.lists[index], false
func (s *InternalRequestLists) AssignList(listKey string, filters *RequestFilters, sort []string, shouldOverwrite OverwriteVal) (*FilteredSortableRooms, bool) {
if shouldOverwrite == DoNotOverwrite {
_, exists := s.lists[listKey]
if exists {
return s.lists[listKey], false
}
}
roomIDs := make([]string, len(s.allRooms))
i := 0
@ -143,17 +146,13 @@ func (s *InternalRequestLists) AssignList(index int, filters *RequestFilters, so
logger.Err(err).Strs("sort_by", sort).Msg("failed to sort")
}
}
if index == len(s.lists) {
s.lists = append(s.lists, roomList)
return roomList, true
}
s.lists[index] = roomList
s.lists[listKey] = roomList
return roomList, true
}
// Count returns the count of total rooms in this list
func (s *InternalRequestLists) Count(index int) int {
return int(s.lists[index].Len())
func (s *InternalRequestLists) Count(listKey string) int {
return int(s.lists[listKey].Len())
}
func (s *InternalRequestLists) Len() int {

View File

@ -27,7 +27,7 @@ var (
type Request struct {
TxnID string `json:"txn_id"`
Lists []RequestList `json:"lists"`
Lists map[string]RequestList `json:"lists"`
RoomSubscriptions map[string]RoomSubscription `json:"room_subscriptions"`
UnsubscribeRooms []string `json:"unsubscribe_rooms"`
Extensions extensions.Request `json:"extensions"`
@ -43,6 +43,7 @@ type RequestList struct {
Sort []string `json:"sort"`
Filters *RequestFilters `json:"filters"`
SlowGetAllRooms *bool `json:"slow_get_all_rooms,omitempty"`
Deleted bool `json:"deleted,omitempty"`
}
func (rl *RequestList) ShouldGetAllRooms() bool {
@ -267,7 +268,7 @@ type RequestDelta struct {
// room IDs to unsubscribe from
Unsubs []string
// The complete union of both lists (contains max(a,b) lists)
Lists []RequestListDelta
Lists map[string]RequestListDelta
}
// Internal struct used to represent a single list delta.
@ -294,24 +295,39 @@ func (r *Request) ApplyDelta(nextReq *Request) (result *Request, delta *RequestD
Extensions: r.Extensions.ApplyDelta(&nextReq.Extensions),
}
}
listKeys := make(set)
for k := range nextReq.Lists {
listKeys[k] = struct{}{}
}
for k := range r.Lists {
listKeys[k] = struct{}{}
}
delta = &RequestDelta{}
lists := make([]RequestList, len(nextReq.Lists))
for i := 0; i < len(lists); i++ {
var existingList *RequestList
if i < len(r.Lists) {
existingList = &r.Lists[i]
}
// default to recency sort order if missing and there isn't a previous list to draw from
if len(nextReq.Lists[i].Sort) == 0 && existingList == nil {
nextReq.Lists[i].Sort = []string{SortByRecency}
}
if existingList == nil {
// we added a list
lists[i] = nextReq.Lists[i]
calculatedLists := make(map[string]RequestList, len(nextReq.Lists))
for listKey := range listKeys {
existingList, existingOk := r.Lists[listKey]
nextList, nextOk := nextReq.Lists[listKey]
if !nextOk {
// copy over what they said before (sticky), no diffs to make
calculatedLists[listKey] = existingList
continue
}
nextList := nextReq.Lists[i]
if !existingOk {
// we added a list
// default to recency sort order if missing and there isn't a previous list value to draw from
if len(nextList.Sort) == 0 {
nextList.Sort = []string{SortByRecency}
}
calculatedLists[listKey] = nextList
continue
}
// both existing and next exist, check for deletions
if nextList.Deleted {
// do not add the list to `lists` so it disappears
continue
}
// apply the delta
rooms := nextList.Ranges
if rooms == nil {
rooms = existingList.Ranges
@ -332,7 +348,6 @@ func (r *Request) ApplyDelta(nextReq *Request) (result *Request, delta *RequestD
if includeOldRooms == nil {
includeOldRooms = existingList.IncludeOldRooms
}
timelineLimit := nextList.TimelineLimit
if timelineLimit == 0 {
timelineLimit = existingList.TimelineLimit
@ -342,7 +357,7 @@ func (r *Request) ApplyDelta(nextReq *Request) (result *Request, delta *RequestD
filters = existingList.Filters
}
lists[i] = RequestList{
calculatedLists[listKey] = RequestList{
RoomSubscription: RoomSubscription{
RequiredState: reqState,
TimelineLimit: timelineLimit,
@ -354,20 +369,20 @@ func (r *Request) ApplyDelta(nextReq *Request) (result *Request, delta *RequestD
SlowGetAllRooms: slowGetAllRooms,
}
}
result.Lists = lists
// the delta is as large as the longest list of lists
maxLen := len(result.Lists)
if len(r.Lists) > maxLen {
maxLen = len(r.Lists)
}
delta.Lists = make([]RequestListDelta, maxLen)
for i := range result.Lists {
delta.Lists[i] = RequestListDelta{
Curr: &result.Lists[i],
result.Lists = calculatedLists
delta.Lists = make(map[string]RequestListDelta, len(calculatedLists))
for listKey := range result.Lists {
l := result.Lists[listKey]
delta.Lists[listKey] = RequestListDelta{
Curr: &l,
}
}
for i := range r.Lists {
delta.Lists[i].Prev = &r.Lists[i]
for listKey := range r.Lists {
l := r.Lists[listKey]
rld := delta.Lists[listKey]
rld.Prev = &l
delta.Lists[listKey] = rld
}
// Work out subscriptions. The operations are applied as:
@ -411,15 +426,15 @@ func (r *Request) ApplyDelta(nextReq *Request) (result *Request, delta *RequestD
return
}
func (r *Request) GetTimelineLimit(listIndex int, roomID string) int64 {
func (r *Request) GetTimelineLimit(listKey string, roomID string) int64 {
if r.RoomSubscriptions != nil {
room, ok := r.RoomSubscriptions[roomID]
if ok && room.TimelineLimit > 0 {
return room.TimelineLimit
}
}
if r.Lists[listIndex].TimelineLimit > 0 {
return r.Lists[listIndex].TimelineLimit
if r.Lists[listKey].TimelineLimit > 0 {
return r.Lists[listKey].TimelineLimit
}
return DefaultTimelineLimit
}

View File

@ -224,7 +224,7 @@ func TestRequestApplyDeltas(t *testing.T) {
},
},
want: Request{
Lists: []RequestList{},
Lists: map[string]RequestList{},
RoomSubscriptions: map[string]RoomSubscription{
"!foo:bar": {
TimelineLimit: 10,
@ -235,7 +235,7 @@ func TestRequestApplyDeltas(t *testing.T) {
wantDelta: func(input *Request, d testData) RequestDelta {
return RequestDelta{
Subs: []string{"!foo:bar"},
Lists: []RequestListDelta{},
Lists: map[string]RequestListDelta{},
}
},
},
@ -243,16 +243,16 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "initial: list only",
next: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"a": {
Ranges: [][2]int64{{0, 20}},
Sort: []string{SortByHighlightCount},
},
},
},
want: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"a": {
Ranges: [][2]int64{{0, 20}},
Sort: []string{SortByHighlightCount},
},
@ -262,10 +262,10 @@ func TestRequestApplyDeltas(t *testing.T) {
},
wantDelta: func(input *Request, d testData) RequestDelta {
return RequestDelta{
Lists: []RequestListDelta{
{
Lists: map[string]RequestListDelta{
"a": {
Prev: nil,
Curr: &d.want.Lists[0],
Curr: listPtr(d.want.Lists["a"]),
},
},
}
@ -275,15 +275,15 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "initial: sets sort order to be by_recency if missing",
next: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"a": {
Ranges: [][2]int64{{0, 20}},
},
},
},
want: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"a": {
Ranges: [][2]int64{{0, 20}},
Sort: []string{SortByRecency},
},
@ -293,10 +293,10 @@ func TestRequestApplyDeltas(t *testing.T) {
},
wantDelta: func(input *Request, d testData) RequestDelta {
return RequestDelta{
Lists: []RequestListDelta{
{
Lists: map[string]RequestListDelta{
"a": {
Prev: nil,
Curr: &d.want.Lists[0],
Curr: listPtr(d.want.Lists["a"]),
},
},
}
@ -306,19 +306,19 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "initial: multiple lists",
next: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"z": {
Ranges: [][2]int64{{0, 20}},
Sort: []string{SortByHighlightCount},
},
{
"a": {
Ranges: [][2]int64{{0, 10}},
Filters: &RequestFilters{
IsEncrypted: &boolTrue,
},
Sort: []string{SortByRecency},
},
{
"b": {
Ranges: [][2]int64{{0, 5}},
Sort: []string{SortByRecency, SortByName},
RoomSubscription: RoomSubscription{
@ -331,19 +331,19 @@ func TestRequestApplyDeltas(t *testing.T) {
},
},
want: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"z": {
Ranges: [][2]int64{{0, 20}},
Sort: []string{SortByHighlightCount},
},
{
"a": {
Ranges: [][2]int64{{0, 10}},
Filters: &RequestFilters{
IsEncrypted: &boolTrue,
},
Sort: []string{SortByRecency},
},
{
"b": {
Ranges: [][2]int64{{0, 5}},
Sort: []string{SortByRecency, SortByName},
RoomSubscription: RoomSubscription{
@ -359,18 +359,18 @@ func TestRequestApplyDeltas(t *testing.T) {
},
wantDelta: func(input *Request, d testData) RequestDelta {
return RequestDelta{
Lists: []RequestListDelta{
{
Lists: map[string]RequestListDelta{
"z": {
Prev: nil,
Curr: &d.want.Lists[0],
Curr: listPtr(d.want.Lists["z"]),
},
{
"a": {
Prev: nil,
Curr: &d.want.Lists[1],
Curr: listPtr(d.want.Lists["a"]),
},
{
"b": {
Prev: nil,
Curr: &d.want.Lists[2],
Curr: listPtr(d.want.Lists["b"]),
},
},
}
@ -380,8 +380,8 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "initial: list and sub",
next: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"f": {
Ranges: [][2]int64{{0, 20}},
Sort: []string{SortByHighlightCount},
},
@ -393,8 +393,8 @@ func TestRequestApplyDeltas(t *testing.T) {
},
},
want: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"f": {
Ranges: [][2]int64{{0, 20}},
Sort: []string{SortByHighlightCount},
},
@ -409,10 +409,10 @@ func TestRequestApplyDeltas(t *testing.T) {
wantDelta: func(input *Request, d testData) RequestDelta {
return RequestDelta{
Subs: []string{"!foo:bar"},
Lists: []RequestListDelta{
{
Lists: map[string]RequestListDelta{
"f": {
Prev: nil,
Curr: &d.want.Lists[0],
Curr: listPtr(d.want.Lists["f"]),
},
},
}
@ -422,8 +422,8 @@ func TestRequestApplyDeltas(t *testing.T) {
},
{
input: &Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByName},
RoomSubscription: RoomSubscription{
TimelineLimit: 5,
@ -445,8 +445,8 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "overwriting of sort and updating subs without adding new ones",
next: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByRecency},
},
},
@ -457,8 +457,8 @@ func TestRequestApplyDeltas(t *testing.T) {
},
},
want: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByRecency},
RoomSubscription: RoomSubscription{
TimelineLimit: 5,
@ -476,10 +476,10 @@ func TestRequestApplyDeltas(t *testing.T) {
return RequestDelta{
Subs: nil,
Unsubs: nil,
Lists: []RequestListDelta{
{
Prev: &input.Lists[0],
Curr: &d.want.Lists[0],
Lists: map[string]RequestListDelta{
"q": {
Prev: listPtr(input.Lists["q"]),
Curr: listPtr(d.want.Lists["q"]),
},
},
}
@ -490,8 +490,8 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "Adding a sub",
next: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByRecency},
RoomSubscription: RoomSubscription{
TimelineLimit: 5,
@ -505,8 +505,8 @@ func TestRequestApplyDeltas(t *testing.T) {
},
},
want: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByRecency},
RoomSubscription: RoomSubscription{
TimelineLimit: 5,
@ -527,10 +527,10 @@ func TestRequestApplyDeltas(t *testing.T) {
return RequestDelta{
Subs: []string{"!bar:baz"},
Unsubs: nil,
Lists: []RequestListDelta{
{
Prev: &input.Lists[0],
Curr: &d.want.Lists[0],
Lists: map[string]RequestListDelta{
"q": {
Prev: listPtr(input.Lists["q"]),
Curr: listPtr(d.want.Lists["q"]),
},
},
}
@ -541,16 +541,16 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "Unsubscribing",
next: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByName},
},
},
UnsubscribeRooms: []string{"!foo:bar"},
},
want: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByName},
RoomSubscription: RoomSubscription{
TimelineLimit: 5,
@ -564,10 +564,10 @@ func TestRequestApplyDeltas(t *testing.T) {
return RequestDelta{
Subs: nil,
Unsubs: []string{"!foo:bar"},
Lists: []RequestListDelta{
{
Prev: &input.Lists[0],
Curr: &d.want.Lists[0],
Lists: map[string]RequestListDelta{
"q": {
Prev: listPtr(input.Lists["q"]),
Curr: listPtr(d.want.Lists["q"]),
},
},
}
@ -578,8 +578,8 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "Subscribing/Unsubscribing in one request",
next: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByRecency},
},
},
@ -591,8 +591,8 @@ func TestRequestApplyDeltas(t *testing.T) {
UnsubscribeRooms: []string{"!bar:baz"},
},
want: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByRecency},
RoomSubscription: RoomSubscription{
TimelineLimit: 5,
@ -610,10 +610,10 @@ func TestRequestApplyDeltas(t *testing.T) {
return RequestDelta{
Subs: nil,
Unsubs: nil,
Lists: []RequestListDelta{
{
Prev: &input.Lists[0],
Curr: &d.want.Lists[0],
Lists: map[string]RequestListDelta{
"q": {
Prev: listPtr(input.Lists["q"]),
Curr: listPtr(d.want.Lists["q"]),
},
},
}
@ -623,11 +623,15 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "deleting a list",
next: Request{
Lists: []RequestList{},
Lists: map[string]RequestList{
"q": {
Deleted: true,
},
},
RoomSubscriptions: map[string]RoomSubscription{},
},
want: Request{
Lists: []RequestList{},
Lists: map[string]RequestList{},
RoomSubscriptions: map[string]RoomSubscription{
"!foo:bar": {
TimelineLimit: 10,
@ -639,9 +643,9 @@ func TestRequestApplyDeltas(t *testing.T) {
return RequestDelta{
Subs: nil,
Unsubs: nil,
Lists: []RequestListDelta{
{
Prev: &input.Lists[0],
Lists: map[string]RequestListDelta{
"q": {
Prev: listPtr(input.Lists["q"]),
Curr: nil,
},
},
@ -652,11 +656,11 @@ func TestRequestApplyDeltas(t *testing.T) {
testData: testData{
name: "adding a list",
next: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByRecency},
},
{
"s": {
Sort: []string{SortByHighlightCount},
RoomSubscription: RoomSubscription{
TimelineLimit: 9000,
@ -666,14 +670,14 @@ func TestRequestApplyDeltas(t *testing.T) {
RoomSubscriptions: map[string]RoomSubscription{},
},
want: Request{
Lists: []RequestList{
{
Lists: map[string]RequestList{
"q": {
Sort: []string{SortByRecency},
RoomSubscription: RoomSubscription{
TimelineLimit: 5,
},
},
{
"s": {
Sort: []string{SortByHighlightCount},
RoomSubscription: RoomSubscription{
TimelineLimit: 9000,
@ -691,14 +695,14 @@ func TestRequestApplyDeltas(t *testing.T) {
return RequestDelta{
Subs: nil,
Unsubs: nil,
Lists: []RequestListDelta{
{
Prev: &input.Lists[0],
Curr: &d.want.Lists[0],
Lists: map[string]RequestListDelta{
"q": {
Prev: listPtr(input.Lists["q"]),
Curr: listPtr(d.want.Lists["q"]),
},
{
"s": {
Prev: nil,
Curr: &d.want.Lists[1],
Curr: listPtr(d.want.Lists["s"]),
},
},
}
@ -1040,3 +1044,7 @@ func jsonEqual(t *testing.T, name string, got, want interface{}) {
t.Errorf("%s\ngot %s\nwant %s", name, string(aa), string(bb))
}
}
func listPtr(l RequestList) *RequestList {
return &l
}

View File

@ -16,7 +16,7 @@ const (
)
type Response struct {
Lists []ResponseList `json:"lists"`
Lists map[string]ResponseList `json:"lists"`
Rooms map[string]Room `json:"rooms"`
Extensions extensions.Response `json:"extensions"`
@ -50,7 +50,7 @@ func (r *Response) ListOps() int {
func (r *Response) UnmarshalJSON(b []byte) error {
temporary := struct {
Rooms map[string]Room `json:"rooms"`
Lists []struct {
Lists map[string]struct {
Ops []json.RawMessage `json:"ops"`
Count int `json:"count"`
} `json:"lists"`
@ -68,10 +68,9 @@ func (r *Response) UnmarshalJSON(b []byte) error {
r.TxnID = temporary.TxnID
r.Session = temporary.Session
r.Extensions = temporary.Extensions
r.Lists = make([]ResponseList, len(temporary.Lists))
r.Lists = make(map[string]ResponseList, len(temporary.Lists))
for i := range temporary.Lists {
l := temporary.Lists[i]
for listKey, l := range temporary.Lists {
var list ResponseList
list.Count = l.Count
for _, op := range l.Ops {
@ -89,7 +88,7 @@ func (r *Response) UnmarshalJSON(b []byte) error {
list.Ops = append(list.Ops, &oper)
}
}
r.Lists[i] = list
r.Lists[listKey] = list
}
return nil

View File

@ -34,8 +34,8 @@ func TestMultipleLists(t *testing.T) {
// request 2 lists, one set encrypted, one set unencrypted
res := alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"enc": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms
@ -47,7 +47,7 @@ func TestMultipleLists(t *testing.T) {
IsEncrypted: &boolTrue,
},
},
{
"unenc": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms
@ -63,12 +63,15 @@ func TestMultipleLists(t *testing.T) {
})
m.MatchResponse(t, res,
m.MatchLists([]m.ListMatcher{
m.MatchV3Count(len(encryptedRoomIDs)),
m.MatchV3Ops(m.MatchV3SyncOp(0, 2, encryptedRoomIDs[:3])),
}, []m.ListMatcher{
m.MatchV3Count(len(unencryptedRoomIDs)),
m.MatchV3Ops(m.MatchV3SyncOp(0, 2, unencryptedRoomIDs[:3])),
m.MatchLists(map[string][]m.ListMatcher{
"enc": {
m.MatchV3Count(len(encryptedRoomIDs)),
m.MatchV3Ops(m.MatchV3SyncOp(0, 2, encryptedRoomIDs[:3])),
},
"unenc": {
m.MatchV3Count(len(unencryptedRoomIDs)),
m.MatchV3Ops(m.MatchV3SyncOp(0, 2, unencryptedRoomIDs[:3])),
},
}),
m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
encryptedRoomIDs[0]: {},
@ -82,13 +85,13 @@ func TestMultipleLists(t *testing.T) {
// now scroll one of the lists
res = alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"enc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms still
},
},
{
"unenc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms
[2]int64{3, 5}, // next 3 rooms
@ -96,13 +99,16 @@ func TestMultipleLists(t *testing.T) {
},
},
}, WithPos(res.Pos))
m.MatchResponse(t, res, m.MatchLists([]m.ListMatcher{
m.MatchV3Count(len(encryptedRoomIDs)),
}, []m.ListMatcher{
m.MatchV3Count(len(unencryptedRoomIDs)),
m.MatchV3Ops(
m.MatchV3SyncOp(3, 5, unencryptedRoomIDs[3:6]),
),
m.MatchResponse(t, res, m.MatchLists(map[string][]m.ListMatcher{
"enc": {
m.MatchV3Count(len(encryptedRoomIDs)),
},
"unenc": {
m.MatchV3Count(len(unencryptedRoomIDs)),
m.MatchV3Ops(
m.MatchV3SyncOp(3, 5, unencryptedRoomIDs[3:6]),
),
},
}), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
unencryptedRoomIDs[3]: {},
unencryptedRoomIDs[4]: {},
@ -118,13 +124,13 @@ func TestMultipleLists(t *testing.T) {
// We are tracking the first few encrypted rooms so we expect list 0 to update
// However we do not track old unencrypted rooms so we expect no change in list 1
res = alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"enc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms still
},
},
{
"unenc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms
[2]int64{3, 5}, // next 3 rooms
@ -132,14 +138,17 @@ func TestMultipleLists(t *testing.T) {
},
},
}, WithPos(res.Pos))
m.MatchResponse(t, res, m.MatchLists([]m.ListMatcher{
m.MatchV3Count(len(encryptedRoomIDs)),
m.MatchV3Ops(
m.MatchV3DeleteOp(2),
m.MatchV3InsertOp(0, encryptedRoomIDs[0]),
),
}, []m.ListMatcher{
m.MatchV3Count(len(unencryptedRoomIDs)),
m.MatchResponse(t, res, m.MatchLists(map[string][]m.ListMatcher{
"enc": {
m.MatchV3Count(len(encryptedRoomIDs)),
m.MatchV3Ops(
m.MatchV3DeleteOp(2),
m.MatchV3InsertOp(0, encryptedRoomIDs[0]),
),
},
"unenc": {
m.MatchV3Count(len(unencryptedRoomIDs)),
},
}))
}
@ -171,8 +180,8 @@ func TestMultipleListsDMUpdate(t *testing.T) {
// request 2 lists, one set DM, one set no DM
res := alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"dm": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms
@ -184,7 +193,7 @@ func TestMultipleListsDMUpdate(t *testing.T) {
IsDM: &boolTrue,
},
},
{
"nodm": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms
@ -199,12 +208,15 @@ func TestMultipleListsDMUpdate(t *testing.T) {
},
})
m.MatchResponse(t, res, m.MatchLists([]m.ListMatcher{
m.MatchV3Count(len(dmRoomIDs)),
m.MatchV3Ops(m.MatchV3SyncOp(0, 2, dmRoomIDs[:3])),
}, []m.ListMatcher{
m.MatchV3Count(len(groupRoomIDs)),
m.MatchV3Ops(m.MatchV3SyncOp(0, 2, groupRoomIDs[:3])),
m.MatchResponse(t, res, m.MatchLists(map[string][]m.ListMatcher{
"dm": {
m.MatchV3Count(len(dmRoomIDs)),
m.MatchV3Ops(m.MatchV3SyncOp(0, 2, dmRoomIDs[:3])),
},
"nodm": {
m.MatchV3Count(len(groupRoomIDs)),
m.MatchV3Ops(m.MatchV3SyncOp(0, 2, groupRoomIDs[:3])),
},
}))
// now bring the last DM room to the top with a notif
@ -217,27 +229,30 @@ func TestMultipleListsDMUpdate(t *testing.T) {
// now get the delta: only the DM room should change
res = alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"dm": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms still
},
},
{
"nodm": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms still
},
},
},
}, WithPos(res.Pos))
m.MatchResponse(t, res, m.MatchLists([]m.ListMatcher{
m.MatchV3Count(len(dmRoomIDs)),
m.MatchV3Ops(
m.MatchV3DeleteOp(2),
m.MatchV3InsertOp(0, dmRoomIDs[0]),
),
}, []m.ListMatcher{
m.MatchV3Count(len(groupRoomIDs)),
m.MatchResponse(t, res, m.MatchLists(map[string][]m.ListMatcher{
"dm": {
m.MatchV3Count(len(dmRoomIDs)),
m.MatchV3Ops(
m.MatchV3DeleteOp(2),
m.MatchV3InsertOp(0, dmRoomIDs[0]),
),
},
"nodm": {
m.MatchV3Count(len(groupRoomIDs)),
},
}), m.MatchRoomSubscription(dmRoomIDs[0], MatchRoomTimelineMostRecent(1, []Event{
{
Type: "m.room.message",
@ -261,14 +276,14 @@ func TestNewListMidConnection(t *testing.T) {
// first request no list
res := alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{},
Lists: map[string]sync3.RequestList{},
})
m.MatchResponse(t, res, m.MatchLists())
m.MatchResponse(t, res, m.MatchLists(nil))
// now add a list
res = alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms
},
@ -278,7 +293,7 @@ func TestNewListMidConnection(t *testing.T) {
},
},
}, WithPos(res.Pos))
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(roomIDs)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(roomIDs)), m.MatchV3Ops(
m.MatchV3SyncOp(0, 2, roomIDs[:3]),
)))
}
@ -358,8 +373,8 @@ func TestMultipleOverlappingLists(t *testing.T) {
//
// Rooms with * are union'd
res := alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"enc": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges{
[2]int64{0, 4}, // first 5 rooms
@ -374,7 +389,7 @@ func TestMultipleOverlappingLists(t *testing.T) {
IsEncrypted: &boolTrue,
},
},
{
"dm": {
Sort: []string{sync3.SortByRecency},
Ranges: sync3.SliceRanges{
[2]int64{0, 4}, // first 5 rooms
@ -393,8 +408,8 @@ func TestMultipleOverlappingLists(t *testing.T) {
})
m.MatchResponse(t, res,
m.MatchList(0, m.MatchV3Ops(m.MatchV3SyncOp(0, 4, encryptedRoomIDs[:5]))),
m.MatchList(1, m.MatchV3Ops(m.MatchV3SyncOp(0, 4, dmRoomIDs[:5]))),
m.MatchList("enc", m.MatchV3Ops(m.MatchV3SyncOp(0, 4, encryptedRoomIDs[:5]))),
m.MatchList("dm", m.MatchV3Ops(m.MatchV3SyncOp(0, 4, dmRoomIDs[:5]))),
m.MatchRoomSubscriptions(map[string][]m.RoomMatcher{
// encrypted rooms just come from the encrypted only list
encryptedRoomIDs[0]: {
@ -513,8 +528,8 @@ func TestNot500OnNewRooms(t *testing.T) {
mSpace := "m.space"
alice := registerNewUser(t)
res := alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
SlowGetAllRooms: &boolTrue,
Filters: &sync3.RequestFilters{
RoomTypes: []*string{&mSpace},
@ -524,14 +539,14 @@ func TestNot500OnNewRooms(t *testing.T) {
})
alice.CreateRoom(t, map[string]interface{}{"preset": "public_chat"})
alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
SlowGetAllRooms: &boolTrue,
Filters: &sync3.RequestFilters{
RoomTypes: []*string{&mSpace},
},
},
{
"b": {
Filters: &sync3.RequestFilters{
IsDM: &boolFalse,
},
@ -540,12 +555,13 @@ func TestNot500OnNewRooms(t *testing.T) {
},
}, WithPos(res.Pos))
alice.CreateRoom(t, map[string]interface{}{"preset": "public_chat"})
// should not 500
alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
SlowGetAllRooms: &boolTrue,
},
{
"b": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
@ -557,13 +573,13 @@ func TestNot500OnNewRooms(t *testing.T) {
func TestNewRoomNameCalculations(t *testing.T) {
alice := registerNewUser(t)
res := alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
SlowGetAllRooms: &boolTrue,
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0)))
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)))
// create 10 room in parallel and at the same time spam sliding sync to ensure we get bits of
// rooms before they are fully loaded.
@ -591,8 +607,8 @@ func TestNewRoomNameCalculations(t *testing.T) {
var err error
for {
res = alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
SlowGetAllRooms: &boolTrue,
},
},
@ -638,8 +654,8 @@ func TestChangeSortOrder(t *testing.T) {
alice := registerNewUser(t)
res := alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
Sort: []string{sync3.SortByRecency},
RoomSubscription: sync3.RoomSubscription{
@ -663,8 +679,8 @@ func TestChangeSortOrder(t *testing.T) {
break
}
res = alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
@ -692,8 +708,8 @@ func TestChangeSortOrder(t *testing.T) {
txnID := "a"
res = alice.SlidingSync(t, sync3.Request{
TxnID: "a",
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
Sort: []string{sync3.SortByName},
},
@ -701,15 +717,15 @@ func TestChangeSortOrder(t *testing.T) {
}, WithPos(res.Pos))
for res.TxnID != txnID {
res = alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
}, WithPos(res.Pos))
}
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(4), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(4), m.MatchV3Ops(
m.MatchV3InvalidateOp(0, 20),
m.MatchV3SyncOp(0, 20, []string{gotNameToIDs["Apple"], gotNameToIDs["Kiwi"], gotNameToIDs["Lemon"], gotNameToIDs["Orange"]}),
)))

View File

@ -30,8 +30,8 @@ func TestRoomStateTransitions(t *testing.T) {
// seed the proxy with Alice data
alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
{0, 100},
},
@ -42,8 +42,8 @@ func TestRoomStateTransitions(t *testing.T) {
// bob should see the invited/joined rooms
bobRes := bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
{0, 100},
},
@ -57,7 +57,7 @@ func TestRoomStateTransitions(t *testing.T) {
},
},
})
m.MatchResponse(t, bobRes, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, bobRes, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3SyncOp(0, 100, []string{inviteRoomID, joinRoomID}),
)), m.MatchRoomSubscriptions(map[string][]m.RoomMatcher{
inviteRoomID: {
@ -78,15 +78,15 @@ func TestRoomStateTransitions(t *testing.T) {
// the room should be updated with the initial flag set to replace what was in the invite state
bobRes = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
{0, 100},
},
},
},
}, WithPos(bobRes.Pos))
m.MatchResponse(t, bobRes, m.MatchNoV3Ops(), m.MatchList(0, m.MatchV3Count(2)), m.MatchRoomSubscription(inviteRoomID,
m.MatchResponse(t, bobRes, m.MatchNoV3Ops(), m.MatchList("a", m.MatchV3Count(2)), m.MatchRoomSubscription(inviteRoomID,
MatchRoomRequiredState([]Event{
{
Type: "m.room.create",
@ -122,8 +122,8 @@ func TestInviteRejection(t *testing.T) {
// sync as bob, we should see 1 invite
res := bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
Filters: &sync3.RequestFilters{
IsInvite: &boolTrue,
@ -131,7 +131,7 @@ func TestInviteRejection(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{firstInviteRoomID}),
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
firstInviteRoomID: {
@ -156,13 +156,13 @@ func TestInviteRejection(t *testing.T) {
since = bob.MustSyncUntil(t, SyncReq{Since: since}, SyncInvitedTo(bob.UserID, secondInviteRoomID))
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
}, WithPos(res.Pos))
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3DeleteOp(1),
m.MatchV3InsertOp(0, secondInviteRoomID),
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
@ -190,21 +190,21 @@ func TestInviteRejection(t *testing.T) {
// the list should be purged
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
}, WithPos(res.Pos))
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0), m.MatchV3Ops(
m.MatchV3DeleteOp(1),
m.MatchV3DeleteOp(0),
)))
// fresh sync -> no invites
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
Filters: &sync3.RequestFilters{
IsInvite: &boolTrue,
@ -212,7 +212,7 @@ func TestInviteRejection(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(nil), m.MatchList(0, m.MatchV3Count(0)))
m.MatchResponse(t, res, m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(nil), m.MatchList("a", m.MatchV3Count(0)))
}
func TestInviteAcceptance(t *testing.T) {
@ -228,8 +228,8 @@ func TestInviteAcceptance(t *testing.T) {
// sync as bob, we should see 1 invite
res := bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
Filters: &sync3.RequestFilters{
IsInvite: &boolTrue,
@ -237,7 +237,7 @@ func TestInviteAcceptance(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{firstInviteRoomID}),
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
firstInviteRoomID: {
@ -261,13 +261,13 @@ func TestInviteAcceptance(t *testing.T) {
alice.SlidingSyncUntilMembership(t, "", secondInviteRoomID, bob, "invite")
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
}, WithPos(res.Pos))
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3DeleteOp(1),
m.MatchV3InsertOp(0, secondInviteRoomID),
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
@ -295,21 +295,21 @@ func TestInviteAcceptance(t *testing.T) {
// the list should be purged
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
}, WithPos(res.Pos))
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0), m.MatchV3Ops(
m.MatchV3DeleteOp(1),
m.MatchV3DeleteOp(0),
)))
// fresh sync -> no invites
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
Filters: &sync3.RequestFilters{
IsInvite: &boolTrue,
@ -317,7 +317,7 @@ func TestInviteAcceptance(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(nil), m.MatchList(0, m.MatchV3Count(0)))
m.MatchResponse(t, res, m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(nil), m.MatchList("a", m.MatchV3Count(0)))
}
// test invite/join counts update and are accurate
@ -335,13 +335,13 @@ func TestMemberCounts(t *testing.T) {
// sync as bob, we should see 2 invited rooms with the same join counts so as not to leak join counts
res := bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{firstRoomID, secondRoomID}, true),
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
firstRoomID: {
@ -381,8 +381,8 @@ func TestMemberCounts(t *testing.T) {
alice.SlidingSyncUntilMembership(t, "", secondRoomID, bob, "join")
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
@ -406,8 +406,8 @@ func TestMemberCounts(t *testing.T) {
Content: map[string]interface{}{"body": "ping", "msgtype": "m.text"},
})
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},
@ -425,8 +425,8 @@ func TestMemberCounts(t *testing.T) {
bob.MustSyncUntil(t, SyncReq{}, SyncLeftFrom(charlie.UserID, secondRoomID))
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 20}},
},
},

View File

@ -81,8 +81,8 @@ func TestNumLive(t *testing.T) {
alice.SlidingSyncUntilMembership(t, "", roomID3, bob, "join")
// at this point, roomID is at the bottom, check.
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 1}}, // top 2 rooms
Sort: []string{sync3.SortByRecency},
RoomSubscription: sync3.RoomSubscription{
@ -91,7 +91,7 @@ func TestNumLive(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{roomID3, roomID2}),
)))
// now send 2 live events into roomID to bump it to the top
@ -113,8 +113,8 @@ func TestNumLive(t *testing.T) {
alice.SlidingSyncUntilEventID(t, "", roomID, eventID4)
// now syncing with bob should see 2 live events
res = bob.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 1}}, // top 2 rooms
},
},

View File

@ -30,8 +30,8 @@ func TestPrevBatch(t *testing.T) {
// hit proxy
res := client.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{[2]int64{0, 10}},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 1,

View File

@ -33,23 +33,25 @@ func TestSecurityLiveStreamEventLeftLeak(t *testing.T) {
// start sync streams for Alice and Eve
aliceRes := alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
}},
})
m.MatchResponse(t, aliceRes, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, aliceRes, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 10, []string{roomID}),
)))
eveRes := eve.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
}},
})
m.MatchResponse(t, eveRes, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, eveRes, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 10, []string{roomID}),
)))
@ -70,16 +72,17 @@ func TestSecurityLiveStreamEventLeftLeak(t *testing.T) {
// Ensure Alice sees both events
aliceRes = alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
RoomSubscription: sync3.RoomSubscription{
RequiredState: [][2]string{
{"m.room.name", ""},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
},
}},
RoomSubscription: sync3.RoomSubscription{
RequiredState: [][2]string{
{"m.room.name", ""},
},
},
}},
}, WithPos(aliceRes.Pos))
timeline := aliceRes.Rooms[roomID].Timeline
@ -107,25 +110,26 @@ func TestSecurityLiveStreamEventLeftLeak(t *testing.T) {
kickEvent := lastTwoEvents[0]
// TODO: WE should be returning updated values for name and required_state
m.MatchResponse(t, aliceRes, m.MatchList(0, m.MatchV3Count(1)), m.MatchNoV3Ops(), m.MatchRoomSubscription(
m.MatchResponse(t, aliceRes, m.MatchList("a", m.MatchV3Count(1)), m.MatchNoV3Ops(), m.MatchRoomSubscription(
roomID, m.MatchRoomTimelineMostRecent(2, []json.RawMessage{kickEvent, sensitiveEvent}),
))
// Ensure Eve doesn't see this message in the timeline, name calc or required_state
eveRes = eve.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
RoomSubscription: sync3.RoomSubscription{
RequiredState: [][2]string{
{"m.room.name", ""},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
},
}},
RoomSubscription: sync3.RoomSubscription{
RequiredState: [][2]string{
{"m.room.name", ""},
},
},
}},
}, WithPos(eveRes.Pos))
// the room is deleted from eve's point of view and she sees up to and including her kick event
m.MatchResponse(t, eveRes, m.MatchList(0, m.MatchV3Count(0), m.MatchV3Ops(m.MatchV3DeleteOp(0))), m.MatchRoomSubscription(
m.MatchResponse(t, eveRes, m.MatchList("a", m.MatchV3Count(0), m.MatchV3Ops(m.MatchV3DeleteOp(0))), m.MatchRoomSubscription(
roomID, m.MatchRoomName(""), m.MatchRoomRequiredState(nil), m.MatchRoomTimelineMostRecent(1, []json.RawMessage{kickEvent}),
))
}
@ -157,11 +161,12 @@ func TestSecurityRoomSubscriptionLeak(t *testing.T) {
// start sync streams for Eve, with a room subscription to alice's private room
eveRes := eve.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
}},
RoomSubscriptions: map[string]sync3.RoomSubscription{
alicePrivateRoomID: {
TimelineLimit: 5,
@ -172,7 +177,7 @@ func TestSecurityRoomSubscriptionLeak(t *testing.T) {
},
})
// Assert that Eve doesn't see anything
m.MatchResponse(t, eveRes, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, eveRes, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 10, []string{eveUnrelatedRoomID}),
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
eveUnrelatedRoomID: {},
@ -187,15 +192,16 @@ func TestSecurityRoomSubscriptionLeak(t *testing.T) {
},
})
eveRes = eve.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
}},
}, WithPos(eveRes.Pos))
// Assert that Eve doesn't see anything
m.MatchResponse(t, eveRes, m.MatchList(0, m.MatchV3Count(1)), m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{}))
m.MatchResponse(t, eveRes, m.MatchList("a", m.MatchV3Count(1)), m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{}))
}
// Test that events do not leak via direct space subscriptions.
@ -231,8 +237,8 @@ func TestSecuritySpaceDataLeak(t *testing.T) {
// ensure eve sees nothing
res := eve.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: [][2]int64{{0, 20}},
Filters: &sync3.RequestFilters{
Spaces: []string{roomA},
@ -295,8 +301,8 @@ func TestSecuritySpaceMetadataLeak(t *testing.T) {
// ensure eve sees nothing
res := eve.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: [][2]int64{{0, 20}},
Filters: &sync3.RequestFilters{
Spaces: []string{roomA},

View File

@ -77,8 +77,8 @@ func TestSpacesFilter(t *testing.T) {
}
t.Logf("requesting rooms in spaces %v", spaces)
res := alice.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: [][2]int64{{0, 20}},
Filters: &sync3.RequestFilters{
Spaces: spaces,
@ -86,7 +86,7 @@ func TestSpacesFilter(t *testing.T) {
},
},
}, opts...)
m.MatchResponse(t, res, m.MatchList(0, listMatchers...))
m.MatchResponse(t, res, m.MatchList("a", listMatchers...))
return res
}

View File

@ -17,8 +17,8 @@ func TestIncludeOldRooms(t *testing.T) {
roomID := client.CreateRoom(t, map[string]interface{}{})
res := client.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: [][2]int64{{0, 1}},
RoomSubscription: sync3.RoomSubscription{
RequiredState: [][2]string{{"m.room.create", ""}},
@ -26,7 +26,7 @@ func TestIncludeOldRooms(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{roomID}),
)))
newRoomID := upgradeRoom(t, client, roomID)
@ -34,15 +34,15 @@ func TestIncludeOldRooms(t *testing.T) {
time.Sleep(100 * time.Millisecond) // let the proxy process it
res = client.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: [][2]int64{{0, 1}},
},
},
}, WithPos(res.Pos))
var tombstoneEventID string
// count is 1 as we are auto-joined to the upgraded room
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3DeleteOp(1),
m.MatchV3InsertOp(0, newRoomID), // insert new room
m.MatchV3DeleteOp(1), // remove old room
@ -86,8 +86,8 @@ func TestIncludeOldRooms(t *testing.T) {
// now fresh sync with old rooms enabled
res = client.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: [][2]int64{{0, 2}},
RoomSubscription: sync3.RoomSubscription{
RequiredState: [][2]string{{"m.room.member", client.UserID}},
@ -98,7 +98,7 @@ func TestIncludeOldRooms(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 2, []string{newRoomID}),
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
newRoomID: {
@ -126,8 +126,8 @@ func TestIncludeOldRooms(t *testing.T) {
// finally, a fresh sync without include_old_rooms -> newest room only
// now fresh sync with old rooms enabled
res = client.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: [][2]int64{{0, 2}},
RoomSubscription: sync3.RoomSubscription{
RequiredState: [][2]string{{"m.room.member", client.UserID}},
@ -135,7 +135,7 @@ func TestIncludeOldRooms(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 2, []string{newRoomID}),
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
newRoomID: {
@ -271,8 +271,8 @@ func TestIncludeOldRoomsSubscriptionUnion(t *testing.T) {
// should union to timeline_limit=2, req_state=create+member+tombstone
res := client.SlidingSync(t, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: [][2]int64{{0, 1}},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 0,
@ -293,7 +293,7 @@ func TestIncludeOldRoomsSubscriptionUnion(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{roomB}),
)), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
roomA: {

View File

@ -60,14 +60,15 @@ func benchNumV2Rooms(numRooms int, b *testing.B) {
})
// do the initial request
v3.mustDoV3Request(b, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // first few rooms
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 3,
},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // first few rooms
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 3,
},
}},
})
b.ResetTimer() // don't count setup code
@ -75,25 +76,26 @@ func benchNumV2Rooms(numRooms int, b *testing.B) {
// these should all take roughly the same amount of time, regardless of the value of `numRooms`
for n := 0; n < b.N; n++ {
v3.mustDoV3Request(b, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
// always use a fixed range else we will scale O(n) with the number of rooms
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // first few rooms
},
// include a filter to ensure we loop over rooms
Filters: &sync3.RequestFilters{
IsEncrypted: &boolFalse,
},
// include a few required state events to force us to query the database
// include a few timeline events to force us to query the database
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 3,
RequiredState: [][2]string{
{"m.room.create", ""},
{"m.room.member", alice},
Lists: map[string]sync3.RequestList{
"a": {
// always use a fixed range else we will scale O(n) with the number of rooms
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // first few rooms
},
},
}},
// include a filter to ensure we loop over rooms
Filters: &sync3.RequestFilters{
IsEncrypted: &boolFalse,
},
// include a few required state events to force us to query the database
// include a few timeline events to force us to query the database
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 3,
RequiredState: [][2]string{
{"m.room.create", ""},
{"m.room.member", alice},
},
},
}},
})
}
}

View File

@ -33,7 +33,7 @@ func TestMultipleConnsAtStartup(t *testing.T) {
go func() {
defer wg.Done()
_, body, _ := v3.doV3Request(t, ctx, aliceToken, "", sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -67,7 +67,7 @@ func TestMultipleConnsAtStartup(t *testing.T) {
// do another /sync
res = v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -76,7 +76,7 @@ func TestMultipleConnsAtStartup(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Ops(
m.MatchV3SyncOp(0, 10, []string{roomID}),
)))
}
@ -122,7 +122,7 @@ func TestOutstandingRequestsGetCancelled(t *testing.T) {
})
// first request to get some data
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1},
},
@ -132,7 +132,7 @@ func TestOutstandingRequestsGetCancelled(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{roomA, roomB}),
)))
// now we do a blocking request, and a few ms later do another request which can be satisfied
@ -146,7 +146,7 @@ func TestOutstandingRequestsGetCancelled(t *testing.T) {
defer wg.Done()
time.Sleep(100 * time.Millisecond)
res2 := v3.mustDoV3RequestWithPos(t, aliceToken, pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1},
},
@ -160,7 +160,7 @@ func TestOutstandingRequestsGetCancelled(t *testing.T) {
m.MatchResponse(t, res2, m.MatchNoV3Ops())
// retry request with new pos and we should see the new data
res2 = v3.mustDoV3RequestWithPos(t, aliceToken, res2.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1},
},
@ -170,7 +170,7 @@ func TestOutstandingRequestsGetCancelled(t *testing.T) {
},
}},
})
m.MatchResponse(t, res2, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, res2, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3InvalidateOp(0, 1),
m.MatchV3SyncOp(0, 1, []string{roomB, roomA}),
)))
@ -179,7 +179,7 @@ func TestOutstandingRequestsGetCancelled(t *testing.T) {
}
}()
req := sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1},
},
@ -190,7 +190,7 @@ func TestOutstandingRequestsGetCancelled(t *testing.T) {
if time.Since(startTime) > time.Second {
t.Errorf("took >1s to process request which should have been interrupted before timing out, took %v", time.Since(startTime))
}
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2)), m.MatchNoV3Ops())
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2)), m.MatchNoV3Ops())
wg.Wait()
}
@ -228,7 +228,7 @@ func TestConnectionTimeoutNotReset(t *testing.T) {
})
// first request to get some data
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 0}, // first room only -> roomID
},
@ -238,12 +238,12 @@ func TestConnectionTimeoutNotReset(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3SyncOp(0, 0, []string{roomA}),
)))
// 2nd request with a 1s timeout
req := sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 0},
},
@ -282,7 +282,7 @@ func TestConnectionTimeoutNotReset(t *testing.T) {
if dur > (1500 * time.Millisecond) { // 0.5s leeway
t.Fatalf("request took %v to complete, expected ~1s", dur)
}
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2)), m.MatchNoV3Ops())
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2)), m.MatchNoV3Ops())
}
@ -377,7 +377,7 @@ func TestTxnIDResponseBuffering(t *testing.T) {
// Send a request with room_name_like = C. Get back pos=1
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
TxnID: "c",
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -390,13 +390,13 @@ func TestTxnIDResponseBuffering(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchTxnID("c"), m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchTxnID("c"), m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 10, []string{roomC}),
)))
// Send a request with pos=1 to filter for room_name_like = A . Discard the response.
v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
TxnID: "a",
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -413,7 +413,7 @@ func TestTxnIDResponseBuffering(t *testing.T) {
// Send a request with pos=1 to filter for room_name_like = B. Ensure we see both A,B and the txn_id is set correctly for both.
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
TxnID: "b",
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -427,7 +427,7 @@ func TestTxnIDResponseBuffering(t *testing.T) {
}},
})
// this response should be the one for A
m.MatchResponse(t, res, m.MatchTxnID("a"), m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchTxnID("a"), m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3InvalidateOp(0, 10),
m.MatchV3SyncOp(0, 10, []string{roomA}),
)))
@ -436,7 +436,7 @@ func TestTxnIDResponseBuffering(t *testing.T) {
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{})
// now we get the response for B
m.MatchResponse(t, res, m.MatchTxnID("b"), m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchTxnID("b"), m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3InvalidateOp(0, 10),
m.MatchV3SyncOp(0, 10, []string{roomB}),
)))

View File

@ -36,7 +36,7 @@ func TestExtensionE2EE(t *testing.T) {
DeviceUnusedFallbackKeyTypes: fallbackKeyTypes,
})
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -62,7 +62,7 @@ func TestExtensionE2EE(t *testing.T) {
},
})
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -82,7 +82,7 @@ func TestExtensionE2EE(t *testing.T) {
})
v2.waitUntilEmpty(t, alice)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -111,7 +111,7 @@ func TestExtensionE2EE(t *testing.T) {
v2.waitUntilEmpty(t, alice)
lastPos := res.Pos
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -127,7 +127,7 @@ func TestExtensionE2EE(t *testing.T) {
// check that changed|left persist if requesting with the same v3 position
res = v3.mustDoV3RequestWithPos(t, aliceToken, lastPos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -153,7 +153,7 @@ func TestExtensionE2EE(t *testing.T) {
},
})
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -204,7 +204,7 @@ func TestExtensionToDevice(t *testing.T) {
// 1: check that a fresh sync returns to-device messages
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -215,11 +215,11 @@ func TestExtensionToDevice(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0)), m.MatchToDeviceMessages(toDeviceMsgs))
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchToDeviceMessages(toDeviceMsgs))
// 2: repeating the fresh sync request returns the same messages (not deleted)
res = v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -230,11 +230,11 @@ func TestExtensionToDevice(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0)), m.MatchToDeviceMessages(toDeviceMsgs))
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchToDeviceMessages(toDeviceMsgs))
// 3: update the since token -> no new messages
res = v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -246,7 +246,7 @@ func TestExtensionToDevice(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0)), m.MatchToDeviceMessages([]json.RawMessage{}))
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchToDeviceMessages([]json.RawMessage{}))
// 4: inject live to-device messages -> receive them only.
sinceBeforeMsgs := res.Extensions.ToDevice.NextBatch
@ -261,7 +261,7 @@ func TestExtensionToDevice(t *testing.T) {
})
v2.waitUntilEmpty(t, alice)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -272,11 +272,11 @@ func TestExtensionToDevice(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0)), m.MatchToDeviceMessages(newToDeviceMsgs))
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchToDeviceMessages(newToDeviceMsgs))
// 5: repeating the previous sync request returns the same live to-device messages (retransmit)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -287,11 +287,11 @@ func TestExtensionToDevice(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0)), m.MatchToDeviceMessages(newToDeviceMsgs))
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchToDeviceMessages(newToDeviceMsgs))
// ack the to-device messages
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -303,11 +303,11 @@ func TestExtensionToDevice(t *testing.T) {
},
})
// this response contains nothing
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0)), m.MatchToDeviceMessages([]json.RawMessage{}))
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchToDeviceMessages([]json.RawMessage{}))
// 6: using an old since token does not return to-device messages anymore as they were deleted.
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10}, // doesn't matter
},
@ -318,7 +318,7 @@ func TestExtensionToDevice(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0)), m.MatchToDeviceMessages([]json.RawMessage{}))
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)), m.MatchToDeviceMessages([]json.RawMessage{}))
}
// tests that the account data extension works:
@ -399,7 +399,7 @@ func TestExtensionAccountData(t *testing.T) {
Enabled: true,
},
},
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // first two rooms A,B
},
@ -419,7 +419,7 @@ func TestExtensionAccountData(t *testing.T) {
// 5- when the range changes, make sure room account data is sent
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // A,B,C
},
@ -473,7 +473,7 @@ func TestExtensionAccountData(t *testing.T) {
Enabled: true,
},
},
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // first two rooms A,B
},
@ -500,7 +500,7 @@ func TestExtensionAccountData(t *testing.T) {
v2.waitUntilEmpty(t, alice)
// now we should get room account data for C
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // first two rooms A,B
},

View File

@ -30,8 +30,8 @@ func TestFiltersEncryption(t *testing.T) {
// connect and make sure either the encrypted room or not depending on what the filter says
res := rig.V3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"enc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // all rooms
},
@ -39,7 +39,7 @@ func TestFiltersEncryption(t *testing.T) {
IsEncrypted: &boolTrue,
},
},
{
"noenc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // all rooms
},
@ -50,17 +50,19 @@ func TestFiltersEncryption(t *testing.T) {
},
})
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchV3Count(1),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{encryptedRoomID}),
),
},
[]m.ListMatcher{
m.MatchV3Count(1),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{unencryptedRoomID}),
),
map[string][]m.ListMatcher{
"enc": {
m.MatchV3Count(1),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{encryptedRoomID}),
),
},
"noenc": {
m.MatchV3Count(1),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{unencryptedRoomID}),
),
},
},
))
@ -69,14 +71,14 @@ func TestFiltersEncryption(t *testing.T) {
// now requesting the encrypted list should include it (added)
res = rig.V3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"enc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // all rooms
},
// sticky; should remember filters
},
{
"noenc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // all rooms
},
@ -85,54 +87,62 @@ func TestFiltersEncryption(t *testing.T) {
},
})
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchV3Count(2),
m.MatchV3Ops(
m.MatchV3DeleteOp(1), m.MatchV3InsertOp(0, unencryptedRoomID),
),
},
[]m.ListMatcher{
m.MatchV3Count(0),
m.MatchV3Ops(
m.MatchV3DeleteOp(0),
),
map[string][]m.ListMatcher{
"enc": {
m.MatchV3Count(2),
m.MatchV3Ops(
m.MatchV3DeleteOp(1), m.MatchV3InsertOp(0, unencryptedRoomID),
),
},
"noenc": {
m.MatchV3Count(0),
m.MatchV3Ops(
m.MatchV3DeleteOp(0),
),
},
},
))
// requesting the encrypted list from scratch returns 2 rooms now
res = rig.V3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // all rooms
},
Filters: &sync3.RequestFilters{
IsEncrypted: &boolTrue,
},
}},
Lists: map[string]sync3.RequestList{
"enc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // all rooms
},
Filters: &sync3.RequestFilters{
IsEncrypted: &boolTrue,
},
}},
})
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchV3Count(2),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{unencryptedRoomID, encryptedRoomID}),
),
map[string][]m.ListMatcher{
"enc": {
m.MatchV3Count(2),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 1, []string{unencryptedRoomID, encryptedRoomID}),
),
},
},
))
// requesting the unencrypted stream from scratch returns 0 rooms
res = rig.V3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // all rooms
},
Filters: &sync3.RequestFilters{
IsEncrypted: &boolFalse,
},
}},
Lists: map[string]sync3.RequestList{
"noenc": {
Ranges: sync3.SliceRanges{
[2]int64{0, 1}, // all rooms
},
Filters: &sync3.RequestFilters{
IsEncrypted: &boolFalse,
},
}},
})
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchV3Count(0),
map[string][]m.ListMatcher{
"noenc": {
m.MatchV3Count(0),
},
},
))
}
@ -152,8 +162,8 @@ func TestFiltersInvite(t *testing.T) {
// make sure the is_invite filter works
res := rig.V3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"inv": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -161,7 +171,7 @@ func TestFiltersInvite(t *testing.T) {
IsInvite: &boolTrue,
},
},
{
"noinv": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -172,14 +182,16 @@ func TestFiltersInvite(t *testing.T) {
},
})
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchV3Count(1),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{roomID}),
),
},
[]m.ListMatcher{
m.MatchV3Count(0),
map[string][]m.ListMatcher{
"inv": {
m.MatchV3Count(1),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{roomID}),
),
},
"noinv": {
m.MatchV3Count(0),
},
},
))
@ -188,14 +200,14 @@ func TestFiltersInvite(t *testing.T) {
// now the room should move from one room to another
res = rig.V3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"inv": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
// sticky; should remember filters
},
{
"noinv": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -205,18 +217,20 @@ func TestFiltersInvite(t *testing.T) {
})
// the room swaps from the invite list to the join list
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchV3Count(0),
m.MatchV3Ops(
m.MatchV3DeleteOp(0),
),
},
[]m.ListMatcher{
m.MatchV3Count(1),
m.MatchV3Ops(
m.MatchV3DeleteOp(0),
m.MatchV3InsertOp(0, roomID),
),
map[string][]m.ListMatcher{
"inv": {
m.MatchV3Count(0),
m.MatchV3Ops(
m.MatchV3DeleteOp(0),
),
},
"noinv": {
m.MatchV3Count(1),
m.MatchV3Ops(
m.MatchV3DeleteOp(0),
m.MatchV3InsertOp(0, roomID),
),
},
},
))
}
@ -258,8 +272,8 @@ func TestFiltersRoomName(t *testing.T) {
// make sure the room name filter works
res := rig.V3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -269,21 +283,19 @@ func TestFiltersRoomName(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchV3Count(5),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{
ridApple, ridPear, ridOrange, ridPineapple, ridBanana,
}, true),
),
},
m.MatchResponse(t, res, m.MatchList("a",
m.MatchV3Count(5),
m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{
ridApple, ridPear, ridOrange, ridPineapple, ridBanana,
}, true),
),
))
// refine the filter
res = rig.V3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -293,16 +305,14 @@ func TestFiltersRoomName(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchV3Count(2),
m.MatchV3Ops(
m.MatchV3InvalidateOp(0, 20),
m.MatchV3SyncOp(0, 20, []string{
ridApple, ridPineapple,
}, true),
),
},
m.MatchResponse(t, res, m.MatchList("a",
m.MatchV3Count(2),
m.MatchV3Ops(
m.MatchV3InvalidateOp(0, 20),
m.MatchV3SyncOp(0, 20, []string{
ridApple, ridPineapple,
}, true),
),
))
}
@ -328,9 +338,9 @@ func TestFiltersRoomTypes(t *testing.T) {
// make sure the room_types and not_room_types filters works
res := rig.V3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{
Lists: map[string]sync3.RequestList{
// returns spaceRoomID only due to direct match
{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -339,7 +349,7 @@ func TestFiltersRoomTypes(t *testing.T) {
},
},
// returns roomID only due to direct match (null = things without a room type)
{
"b": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -348,7 +358,7 @@ func TestFiltersRoomTypes(t *testing.T) {
},
},
// returns roomID and otherRoomID due to exclusion
{
"c": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -357,7 +367,7 @@ func TestFiltersRoomTypes(t *testing.T) {
},
},
// returns otherRoomID due to otherRoomType inclusive, roomType is excluded (override)
{
"d": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -367,7 +377,7 @@ func TestFiltersRoomTypes(t *testing.T) {
},
},
// returns no rooms as filtered room type isn't set on any rooms
{
"e": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -376,7 +386,7 @@ func TestFiltersRoomTypes(t *testing.T) {
},
},
// returns all rooms as filtered not room type isn't set on any rooms
{
"f": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20}, // all rooms
},
@ -386,26 +396,26 @@ func TestFiltersRoomTypes(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchResponse(t, res, m.MatchLists(map[string][]m.ListMatcher{
"a": {
m.MatchV3Count(1), m.MatchV3Ops(m.MatchV3SyncOp(0, 20, []string{spaceRoomID})),
},
[]m.ListMatcher{
"b": {
m.MatchV3Count(1), m.MatchV3Ops(m.MatchV3SyncOp(0, 20, []string{roomID})),
},
[]m.ListMatcher{
"c": {
m.MatchV3Count(2), m.MatchV3Ops(m.MatchV3SyncOp(0, 20, []string{roomID, otherRoomID}, true)),
},
[]m.ListMatcher{
"d": {
m.MatchV3Count(1), m.MatchV3Ops(m.MatchV3SyncOp(0, 20, []string{otherRoomID})),
},
[]m.ListMatcher{
"e": {
m.MatchV3Count(0),
},
[]m.ListMatcher{
"f": {
m.MatchV3Count(3), m.MatchV3Ops(m.MatchV3SyncOp(0, 20, []string{roomID, otherRoomID, spaceRoomID}, true)),
},
))
}))
}
func TestFiltersTags(t *testing.T) {
@ -448,8 +458,8 @@ func TestFiltersTags(t *testing.T) {
})
aliceToken := rig.Token(alice)
res := rig.V3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"fav": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20},
},
@ -457,7 +467,7 @@ func TestFiltersTags(t *testing.T) {
Tags: []string{tagFav},
},
},
{
"lp": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20},
},
@ -465,7 +475,7 @@ func TestFiltersTags(t *testing.T) {
Tags: []string{tagLow},
},
},
{
"favlp": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20},
},
@ -475,21 +485,21 @@ func TestFiltersTags(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(3), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("fav", m.MatchV3Count(3), m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{fav1RoomID, fav2RoomID, favAndLowRoomID}, true),
)), m.MatchList(1, m.MatchV3Count(3), m.MatchV3Ops(
)), m.MatchList("lp", m.MatchV3Count(3), m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{low1RoomID, low2RoomID, favAndLowRoomID}, true),
)), m.MatchList(2, m.MatchV3Count(5), m.MatchV3Ops(
)), m.MatchList("favlp", m.MatchV3Count(5), m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{fav1RoomID, fav2RoomID, favAndLowRoomID, low1RoomID, low2RoomID}, true),
)))
// first bump the fav1 room
rig.FlushEvent(t, alice, fav1RoomID, testutils.NewMessageEvent(t, alice, "Hi"))
res = rig.V3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{
{Ranges: sync3.SliceRanges{{0, 20}}},
{Ranges: sync3.SliceRanges{{0, 20}}},
{Ranges: sync3.SliceRanges{{0, 20}}},
Lists: map[string]sync3.RequestList{
"fav": {Ranges: sync3.SliceRanges{{0, 20}}},
"lp": {Ranges: sync3.SliceRanges{{0, 20}}},
"favlp": {Ranges: sync3.SliceRanges{{0, 20}}},
},
})
@ -511,22 +521,22 @@ func TestFiltersTags(t *testing.T) {
// we should see DELETEs
res = rig.V3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{
{Ranges: sync3.SliceRanges{{0, 20}}},
{Ranges: sync3.SliceRanges{{0, 20}}},
{Ranges: sync3.SliceRanges{{0, 20}}},
Lists: map[string]sync3.RequestList{
"fav": {Ranges: sync3.SliceRanges{{0, 20}}},
"lp": {Ranges: sync3.SliceRanges{{0, 20}}},
"favlp": {Ranges: sync3.SliceRanges{{0, 20}}},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("fav", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3DeleteOp(0),
)), m.MatchList(1, m.MatchV3Ops()), m.MatchList(2, m.MatchV3Count(4), m.MatchV3Ops(
)), m.MatchList("lp", m.MatchV3Ops()), m.MatchList("favlp", m.MatchV3Count(4), m.MatchV3Ops(
m.MatchV3DeleteOp(0),
)))
// check not_tags works
res = rig.V3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"fav": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20},
},
@ -534,7 +544,7 @@ func TestFiltersTags(t *testing.T) {
Tags: []string{tagFav},
},
},
{
"nofav": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20},
},
@ -544,9 +554,9 @@ func TestFiltersTags(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("fav", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{fav2RoomID, favAndLowRoomID}, true),
)), m.MatchList(1, m.MatchV3Count(3), m.MatchV3Ops(
)), m.MatchList("nofav", m.MatchV3Count(3), m.MatchV3Ops(
m.MatchV3SyncOp(0, 20, []string{low1RoomID, low2RoomID, fav1RoomID}, true),
)))
@ -568,13 +578,13 @@ func TestFiltersTags(t *testing.T) {
})
rig.V2.waitUntilEmpty(t, alice)
res = rig.V3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"fav": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20},
},
},
{
"nofav": {
Ranges: sync3.SliceRanges{
[2]int64{0, 20},
},
@ -589,9 +599,9 @@ func TestFiltersTags(t *testing.T) {
// now we have removed fav tag on FAV2 so new lists are:
// FAVLOW
// FAV1, LOW2, LOW1, FAV2
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("fav", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3DeleteOp(1),
)), m.MatchList(1, m.MatchV3Count(4), m.MatchV3Ops(
)), m.MatchList("nofav", m.MatchV3Count(4), m.MatchV3Ops(
m.MatchV3DeleteOp(3),
m.MatchV3InsertOp(3, fav2RoomID),
)))

View File

@ -42,8 +42,8 @@ func TestStuckInvites(t *testing.T) {
// initial sync, should see the invite
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 10}},
Filters: &sync3.RequestFilters{
IsInvite: &boolTrue,
@ -51,7 +51,7 @@ func TestStuckInvites(t *testing.T) {
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(1), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(1), m.MatchV3Ops(
m.MatchV3SyncOp(0, 10, []string{preSyncInviteRoomID}),
)))
@ -74,13 +74,13 @@ func TestStuckInvites(t *testing.T) {
v2.waitUntilEmpty(t, alice)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 10}},
},
},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(2), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(2), m.MatchV3Ops(
m.MatchV3DeleteOp(1),
m.MatchV3InsertOp(0, postSyncInviteRoomID),
)))
@ -112,23 +112,23 @@ func TestStuckInvites(t *testing.T) {
// the entries are removed
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 10}},
},
},
})
// not asserting the ops here as they could be DELETE 1, DELETE 0 or DELETE 0, DELETE 0 which is hard
// to assert.
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(0)))
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(0)))
// restart the server
v3.restart(t, v2, pqString)
// now query for invites: there should be none if we are clearing the DB correctly.
res = v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{
{
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{{0, 10}},
Filters: &sync3.RequestFilters{
IsInvite: &boolTrue,

View File

@ -0,0 +1,54 @@
package syncv3
import (
"testing"
"github.com/matrix-org/sliding-sync/sync3"
"github.com/matrix-org/sliding-sync/testutils/m"
)
func TestListsAsKeys(t *testing.T) {
boolTrue := true
rig := NewTestRig(t)
defer rig.Finish()
encryptedRoomID := "!TestListsAsKeys_encrypted:localhost"
unencryptedRoomID := "!TestListsAsKeys_unencrypted:localhost"
rig.SetupV2RoomsForUser(t, alice, NoFlush, map[string]RoomDescriptor{
encryptedRoomID: {
IsEncrypted: true,
},
unencryptedRoomID: {
IsEncrypted: false,
},
})
aliceToken := rig.Token(alice)
// make an encrypted room list, then bump both rooms and send a 2nd request with zero data
// and make sure that we see the encrypted room message only (so the filter is still active)
res := rig.V3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: map[string]sync3.RequestList{
"enc": {
Ranges: sync3.SliceRanges{{0, 20}},
Filters: &sync3.RequestFilters{
IsEncrypted: &boolTrue,
},
},
},
})
m.MatchResponse(t, res, m.MatchLists(map[string][]m.ListMatcher{
"enc": {
m.MatchV3Count(1),
},
}), m.MatchRoomSubscription(encryptedRoomID))
rig.FlushText(t, alice, encryptedRoomID, "bump encrypted")
rig.FlushText(t, alice, unencryptedRoomID, "bump unencrypted")
res = rig.V3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{})
m.MatchResponse(t, res, m.MatchLists(map[string][]m.ListMatcher{
"enc": {
m.MatchV3Count(1),
},
}), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
encryptedRoomID: {},
}))
}

View File

@ -54,19 +54,20 @@ func TestNotificationsOnTop(t *testing.T) {
// connect and make sure we get nobing, bing
syncRequestBody := sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(allRooms) - 1)}, // all rooms
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(100),
},
// prefer highlights/notifs/rest, and group them by recency not counts count first, THEN eventually recency
Sort: []string{sync3.SortByNotificationLevel, sync3.SortByRecency},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(allRooms) - 1)}, // all rooms
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(100),
},
// prefer highlights/notifs/rest, and group them by recency not counts count first, THEN eventually recency
Sort: []string{sync3.SortByNotificationLevel, sync3.SortByRecency},
}},
}
res := v3.mustDoV3Request(t, aliceToken, syncRequestBody)
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3SyncOp(0, int64(len(allRooms)-1), []string{noBingRoomID, bingRoomID}),
)))
@ -90,7 +91,7 @@ func TestNotificationsOnTop(t *testing.T) {
})
v2.waitUntilEmpty(t, alice)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, syncRequestBody)
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)),
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)),
m.MatchV3Ops(m.MatchV3DeleteOp(1), m.MatchV3InsertOp(0, bingRoomID)),
))
@ -111,25 +112,26 @@ func TestNotificationsOnTop(t *testing.T) {
})
v2.waitUntilEmpty(t, alice)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, syncRequestBody)
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms))),
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms))),
m.MatchNoV3Ops(),
)
// restart the server and sync from fresh again, it should still have the bing room on top
v3.restart(t, v2, pqString)
res = v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(allRooms) - 1)}, // all rooms
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(100),
},
// prefer highlight count first, THEN eventually recency
Sort: []string{sync3.SortByNotificationLevel, sync3.SortByRecency},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(allRooms) - 1)}, // all rooms
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(100),
},
// prefer highlight count first, THEN eventually recency
Sort: []string{sync3.SortByNotificationLevel, sync3.SortByRecency},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3SyncOpFn(func(op *sync3.ResponseOpRange) error {
if len(op.RoomIDs) != len(allRooms) {
return fmt.Errorf("want %d rooms, got %d", len(allRooms), len(op.RoomIDs))

View File

@ -168,6 +168,10 @@ func (r *testRig) EncryptRoom(t *testing.T, userID, roomID string) {
))
}
func (r *testRig) FlushText(t *testing.T, userID, roomID, text string) {
r.FlushEvent(t, userID, roomID, testutils.NewMessageEvent(t, userID, text))
}
func (r *testRig) FlushEvent(t *testing.T, userID, roomID string, event json.RawMessage) {
r.V2.queueResponse(userID, sync2.SyncResponse{
Rooms: sync2.SyncRoomsResponse{

View File

@ -71,16 +71,17 @@ func TestRoomNames(t *testing.T) {
t.Helper()
// do a sync, make sure room names are sensible
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(allRooms) - 1)}, // all rooms
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(100),
},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(allRooms) - 1)}, // all rooms
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(100),
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3SyncOpFn(func(op *sync3.ResponseOpRange) error {
if len(op.RoomIDs) != len(allRooms) {
return fmt.Errorf("want %d rooms, got %d", len(allRooms), len(op.RoomIDs))
@ -109,14 +110,15 @@ func TestRoomNames(t *testing.T) {
t.Helper()
// do a sync, make sure room names are sensible
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(allRooms) - 1)}, // all rooms
},
Filters: &sync3.RequestFilters{
RoomNameFilter: searchTerm,
},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(allRooms) - 1)}, // all rooms
},
Filters: &sync3.RequestFilters{
RoomNameFilter: searchTerm,
},
}},
})
matchers := make(map[string][]m.RoomMatcher, len(wantRooms))
wantRoomIDs := make([]string, len(wantRooms))
@ -126,7 +128,7 @@ func TestRoomNames(t *testing.T) {
m.MatchRoomName(wantRooms[i].name),
}
}
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(wantRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(wantRooms)), m.MatchV3Ops(
m.MatchV3SyncOp(0, int64(len(allRooms)-1), wantRoomIDs),
)), m.MatchRoomSubscriptions(matchers))
}

View File

@ -60,34 +60,36 @@ func TestSlowGetAllRoomsInitial(t *testing.T) {
})
// fetch all the rooms!
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 3}, // these get ignored
},
SlowGetAllRooms: &boolTrue,
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(numTimelineEventsPerRoom),
},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 3}, // these get ignored
},
SlowGetAllRooms: &boolTrue,
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(numTimelineEventsPerRoom),
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3SyncOp(0, int64(len(allRooms)-1), allRoomIDs, true),
)), m.MatchRoomSubscriptionsStrict(allRoomMatchers))
// now redo this but with a room name filter
res = v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Ranges: sync3.SliceRanges{
[2]int64{0, 3}, // these get ignored
},
SlowGetAllRooms: &boolTrue,
Filters: &sync3.RequestFilters{
RoomNameFilter: "My Room 1", // returns 1,10,11,12,13,14 etc
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(numTimelineEventsPerRoom),
},
}},
Lists: map[string]sync3.RequestList{
"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 3}, // these get ignored
},
SlowGetAllRooms: &boolTrue,
Filters: &sync3.RequestFilters{
RoomNameFilter: "My Room 1", // returns 1,10,11,12,13,14 etc
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: int64(numTimelineEventsPerRoom),
},
}},
})
// remove rooms that don't have a leading 1 index as they should be filtered out
for i := 0; i < 10; i++ {
@ -100,7 +102,7 @@ func TestSlowGetAllRoomsInitial(t *testing.T) {
for roomID := range allRoomMatchers {
roomIDs = append(roomIDs, roomID)
}
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRoomMatchers)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRoomMatchers)), m.MatchV3Ops(
m.MatchV3SyncOp(0, int64(len(allRoomMatchers)-1), roomIDs, true),
)), m.MatchRoomSubscriptionsStrict(allRoomMatchers))
@ -125,9 +127,9 @@ func TestSlowGetAllRoomsInitial(t *testing.T) {
v2.waitUntilEmpty(t, alice)
// reuse the position from the room name filter test, we should get this new room
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{}},
Lists: map[string]sync3.RequestList{},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRoomMatchers)+1)), m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRoomMatchers)+1)), m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
newRoom.roomID: {
m.MatchRoomInitial(true),
m.MatchRoomName(roomName),
@ -148,9 +150,9 @@ func TestSlowGetAllRoomsInitial(t *testing.T) {
})
v2.waitUntilEmpty(t, alice)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{}},
Lists: map[string]sync3.RequestList{},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRoomMatchers)+1)), m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRoomMatchers)+1)), m.MatchNoV3Ops(), m.MatchRoomSubscriptionsStrict(map[string][]m.RoomMatcher{
allRooms[11].roomID: {
m.MatchRoomInitial(false),
m.MatchRoomTimelineMostRecent(1, []json.RawMessage{newEvent}),

View File

@ -158,7 +158,7 @@ func TestTimelinesLiveStream(t *testing.T) {
// first request => rooms 19,18,17,16
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(wantRooms) - 1)}, // first N rooms
},
@ -167,7 +167,7 @@ func TestTimelinesLiveStream(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3SyncOpFn(func(op *sync3.ResponseOpRange) error {
if len(op.RoomIDs) != len(wantRooms) {
return fmt.Errorf("want %d rooms, got %d", len(wantRooms), len(op.RoomIDs))
@ -190,14 +190,14 @@ func TestTimelinesLiveStream(t *testing.T) {
// next request, DELETE 3; INSERT 0 7;
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(wantRooms) - 1)}, // first N rooms
},
// sticky remember the timeline_limit
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3DeleteOp(3),
m.MatchV3InsertOp(0, allRooms[7].roomID),
)), m.MatchRoomSubscription(
@ -208,25 +208,25 @@ func TestTimelinesLiveStream(t *testing.T) {
// next request, UPDATE 0 7;
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(wantRooms) - 1)}, // first N rooms
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms))), m.MatchNoV3Ops())
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms))), m.MatchNoV3Ops())
bumpRoom(18)
// next request, DELETE 2; INSERT 0 18;
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(wantRooms) - 1)}, // first N rooms
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3DeleteOp(2),
m.MatchV3InsertOp(0, allRooms[18].roomID),
)), m.MatchRoomSubscription(allRooms[18].roomID, m.MatchRoomTimelineMostRecent(1, []json.RawMessage{allRooms[18].events[len(allRooms[18].events)-1]})))
@ -266,7 +266,7 @@ func TestMultipleWindows(t *testing.T) {
// request 3 windows
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms
[2]int64{10, 12}, // 3 rooms in the middle
@ -277,7 +277,7 @@ func TestMultipleWindows(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3SyncOp(0, 2, []string{allRooms[0].roomID, allRooms[1].roomID, allRooms[2].roomID}),
m.MatchV3SyncOp(10, 12, []string{allRooms[10].roomID, allRooms[11].roomID, allRooms[12].roomID}),
m.MatchV3SyncOp(17, 19, []string{allRooms[17].roomID, allRooms[18].roomID, allRooms[19].roomID}),
@ -302,7 +302,7 @@ func TestMultipleWindows(t *testing.T) {
bumpRoom(18)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 2}, // first 3 rooms
[2]int64{10, 12}, // 3 rooms in the middle
@ -316,7 +316,7 @@ func TestMultipleWindows(t *testing.T) {
//18 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// DELETE 2 DELETE 12 DELETE 18
// INSERT 0,18 INSERT 10,9 INSERT 17,16
m.MatchResponse(t, res, m.MatchList(0,
m.MatchResponse(t, res, m.MatchList("a",
m.MatchV3Count(len(allRooms)),
m.MatchV3Ops(
m.MatchV3DeleteOp(18),
@ -348,7 +348,7 @@ func TestInitialFlag(t *testing.T) {
},
})
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -357,7 +357,7 @@ func TestInitialFlag(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Ops(
m.MatchV3SyncOp(0, 10, []string{roomID}),
)), m.MatchRoomSubscription(roomID, m.MatchRoomInitial(true)))
// send an update
@ -376,7 +376,7 @@ func TestInitialFlag(t *testing.T) {
v2.waitUntilEmpty(t, alice)
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -415,7 +415,7 @@ func TestDuplicateEventsInTimeline(t *testing.T) {
},
})
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -424,7 +424,7 @@ func TestDuplicateEventsInTimeline(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchList(0,
m.MatchResponse(t, res, m.MatchList("a",
m.MatchV3Ops(m.MatchV3SyncOp(0, 10, []string{roomID})),
), m.MatchRoomSubscription(roomID, m.MatchRoomTimelineMostRecent(1, []json.RawMessage{dupeEvent})))
}
@ -468,7 +468,7 @@ func TestTimelineMiddleWindowZeroTimelineLimit(t *testing.T) {
// Request rooms 5-10 with a 0 timeline limit
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{5, 10},
},
@ -478,7 +478,7 @@ func TestTimelineMiddleWindowZeroTimelineLimit(t *testing.T) {
}},
})
wantRooms := allRooms[5:11]
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(len(allRooms)), m.MatchV3Ops(
m.MatchV3SyncOpFn(func(op *sync3.ResponseOpRange) error {
if len(op.RoomIDs) != len(wantRooms) {
return fmt.Errorf("want %d rooms, got %d", len(wantRooms), len(op.RoomIDs))
@ -512,21 +512,21 @@ func TestTimelineMiddleWindowZeroTimelineLimit(t *testing.T) {
// should see room 4, the server should not panic
res = v3.mustDoV3RequestWithPos(t, aliceToken, res.Pos, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{5, 10},
},
}},
})
m.MatchResponse(t, res, m.MatchLists(
[]m.ListMatcher{
m.MatchResponse(t, res, m.MatchLists(map[string][]m.ListMatcher{
"a": {
m.MatchV3Count(len(allRooms)),
m.MatchV3Ops(
m.MatchV3DeleteOp(10),
m.MatchV3InsertOp(5, allRooms[4].roomID),
),
},
))
}))
}
// Regression test to ensure that the 'state' block NEVER appears when requesting a high timeline_limit.
@ -562,7 +562,7 @@ func TestHistoryDoesntIncludeState(t *testing.T) {
},
})
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -571,7 +571,7 @@ func TestHistoryDoesntIncludeState(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchList(0,
m.MatchResponse(t, res, m.MatchList("a",
m.MatchV3Ops(m.MatchV3SyncOp(0, 10, []string{roomID})),
), m.MatchRoomSubscription(roomID, m.MatchRoomTimeline(room.events), m.MatchRoomPrevBatch(prevBatch)))
}
@ -609,27 +609,25 @@ func TestTimelineTxnID(t *testing.T) {
})
aliceRes := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{
{
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 2,
},
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 2,
},
},
},
})
bobRes := v3.mustDoV3Request(t, bobToken, sync3.Request{
Lists: []sync3.RequestList{
{
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 2,
},
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
RoomSubscription: sync3.RoomSubscription{
TimelineLimit: 2,
},
},
},
})
@ -666,29 +664,27 @@ func TestTimelineTxnID(t *testing.T) {
// now Alice syncs, she should see the event with the txn ID
aliceRes = v3.mustDoV3RequestWithPos(t, aliceToken, aliceRes.Pos, sync3.Request{
Lists: []sync3.RequestList{
{
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
},
},
})
m.MatchResponse(t, aliceRes, m.MatchLists([]m.ListMatcher{m.MatchV3Count(1)}), m.MatchNoV3Ops(), m.MatchRoomSubscription(
m.MatchResponse(t, aliceRes, m.MatchList("a", m.MatchV3Count(1)), m.MatchNoV3Ops(), m.MatchRoomSubscription(
roomID, m.MatchRoomTimelineMostRecent(1, []json.RawMessage{newEvent}),
))
// now Bob syncs, he should see the event without the txn ID
bobRes = v3.mustDoV3RequestWithPos(t, bobToken, bobRes.Pos, sync3.Request{
Lists: []sync3.RequestList{
{
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
},
},
})
m.MatchResponse(t, bobRes, m.MatchLists([]m.ListMatcher{m.MatchV3Count(1)}), m.MatchNoV3Ops(), m.MatchRoomSubscription(
m.MatchResponse(t, bobRes, m.MatchList("a", m.MatchV3Count(1)), m.MatchNoV3Ops(), m.MatchRoomSubscription(
roomID, m.MatchRoomTimelineMostRecent(1, []json.RawMessage{newEventNoUnsigned}),
))
}
@ -698,7 +694,7 @@ func testTimelineLoadInitialEvents(v3 *testV3Server, token string, count int, wa
return func(t *testing.T) {
t.Helper()
res := v3.mustDoV3Request(t, token, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, int64(len(wantRooms) - 1)}, // first N rooms
},
@ -708,7 +704,7 @@ func testTimelineLoadInitialEvents(v3 *testV3Server, token string, count int, wa
}},
})
m.MatchResponse(t, res, m.MatchList(0, m.MatchV3Count(count), m.MatchV3Ops(
m.MatchResponse(t, res, m.MatchList("a", m.MatchV3Count(count), m.MatchV3Ops(
m.MatchV3SyncOpFn(func(op *sync3.ResponseOpRange) error {
if len(op.RoomIDs) != len(wantRooms) {
return fmt.Errorf("want %d rooms, got %d", len(wantRooms), len(op.RoomIDs))
@ -755,7 +751,7 @@ func TestPrevBatchInTimeline(t *testing.T) {
},
})
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -764,11 +760,11 @@ func TestPrevBatchInTimeline(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchLists([]m.ListMatcher{
m.MatchResponse(t, res, m.MatchList("a",
m.MatchV3Ops(
m.MatchV3SyncOp(0, 10, []string{roomID}),
),
}), m.MatchRoomSubscription(roomID, m.MatchRoomPrevBatch("")))
), m.MatchRoomSubscription(roomID, m.MatchRoomPrevBatch("")))
// now make a newer prev_batch and try again
v2.queueResponse(alice, sync2.SyncResponse{
@ -807,7 +803,7 @@ func TestPrevBatchInTimeline(t *testing.T) {
}
for _, tc := range testCases {
res := v3.mustDoV3Request(t, aliceToken, sync3.Request{
Lists: []sync3.RequestList{{
Lists: map[string]sync3.RequestList{"a": {
Ranges: sync3.SliceRanges{
[2]int64{0, 10},
},
@ -816,10 +812,10 @@ func TestPrevBatchInTimeline(t *testing.T) {
},
}},
})
m.MatchResponse(t, res, m.MatchLists([]m.ListMatcher{
m.MatchResponse(t, res, m.MatchList("a",
m.MatchV3Ops(
m.MatchV3SyncOp(0, 10, []string{roomID}),
),
}), m.MatchRoomSubscription(roomID, m.MatchRoomPrevBatch(tc.wantPrevBatch)))
), m.MatchRoomSubscription(roomID, m.MatchRoomPrevBatch(tc.wantPrevBatch)))
}
}

View File

@ -367,9 +367,9 @@ func MatchV3InvalidateOp(start, end int64) OpMatcher {
func MatchNoV3Ops() RespMatcher {
return func(res *sync3.Response) error {
for i, l := range res.Lists {
for key, l := range res.Lists {
if len(l.Ops) > 0 {
return fmt.Errorf("MatchNoV3Ops: list %d got %d ops", i, len(l.Ops))
return fmt.Errorf("MatchNoV3Ops: list %v got %d ops", key, len(l.Ops))
}
}
return nil
@ -498,10 +498,10 @@ func MatchAccountData(globals []json.RawMessage, rooms map[string][]json.RawMess
}
}
func CheckList(i int, res sync3.ResponseList, matchers ...ListMatcher) error {
func CheckList(listKey string, res sync3.ResponseList, matchers ...ListMatcher) error {
for _, m := range matchers {
if err := m(res); err != nil {
return fmt.Errorf("MatchList[%d]: %v", i, err)
return fmt.Errorf("MatchList[%v]: %v", listKey, err)
}
}
return nil
@ -516,24 +516,24 @@ func MatchTxnID(txnID string) RespMatcher {
}
}
func MatchList(i int, matchers ...ListMatcher) RespMatcher {
func MatchList(listKey string, matchers ...ListMatcher) RespMatcher {
return func(res *sync3.Response) error {
if i >= len(res.Lists) {
return fmt.Errorf("MatchSingleList: index %d does not exist, got %d lists", i, len(res.Lists))
if _, exists := res.Lists[listKey]; !exists {
return fmt.Errorf("MatchSingleList: key %v does not exist, got %d lists", listKey, len(res.Lists))
}
list := res.Lists[i]
return CheckList(i, list, matchers...)
list := res.Lists[listKey]
return CheckList(listKey, list, matchers...)
}
}
func MatchLists(matchers ...[]ListMatcher) RespMatcher {
func MatchLists(matchers map[string][]ListMatcher) RespMatcher {
return func(res *sync3.Response) error {
if len(matchers) != len(res.Lists) {
return fmt.Errorf("MatchLists: got %d matchers for %d lists", len(matchers), len(res.Lists))
}
for i := range matchers {
if err := CheckList(i, res.Lists[i], matchers[i]...); err != nil {
return fmt.Errorf("MatchLists[%d]: %v", i, err)
for listKey, matchersForList := range matchers {
if err := CheckList(listKey, res.Lists[listKey], matchersForList...); err != nil {
return fmt.Errorf("MatchLists[%v]: %v", listKey, err)
}
}
return nil