fix(sdk): SlidingSyncList.room_list emits updates for non-moving rooms

fix(sdk): `SlidingSyncList.room_list` emits updates for non-moving rooms
This commit is contained in:
Ivan Enderlin
2023-04-06 15:08:47 +02:00
committed by GitHub
3 changed files with 288 additions and 101 deletions

View File

@@ -5,6 +5,7 @@ mod room_list_entry;
use std::{
cmp::min,
collections::HashSet,
fmt::Debug,
iter,
ops::Not,
@@ -204,17 +205,34 @@ impl SlidingSyncList {
self.inner.next_request()
}
// Handle the response from the server.
#[instrument(skip(self, sync_operations), fields(name = self.name(), sync_operations_count = sync_operations.len()))]
pub(super) fn handle_response(
/// Update the list based on the response from the server.
///
/// The `maximum_number_of_rooms` is the `lists.$this_list.count` value,
/// i.e. maximum number of available rooms as defined by the server. The
/// `list_sync_operations` is the `list.$this_list.ops` value
/// received from the server for this specific list. It consists of
/// operations to “move” rooms' positions. Finally,
/// the `rooms_that_have_received_an_update` is the `rooms` value received
/// from the server, which represents aggregated rooms that have received an
/// update. Maybe their position has changed, maybe they have received a new
/// event in their timeline. We need this information to update the
/// `room_list` even if the position of the room hasn't be modified: it
/// helps the user to know that a room has received an update.
#[instrument(skip(self, list_sync_operations), fields(name = self.name(), list_sync_operations_count = list_sync_operations.len()))]
pub(super) fn update(
&mut self,
maximum_number_of_rooms: u32,
sync_operations: &[v4::SyncOp],
list_sync_operations: &[v4::SyncOp],
rooms_that_have_received_an_update: &[OwnedRoomId],
) -> Result<bool, Error> {
let result = self.inner.update_room_list(maximum_number_of_rooms, sync_operations)?;
let new_changes = self.inner.update_room_list(
maximum_number_of_rooms,
list_sync_operations,
rooms_that_have_received_an_update,
)?;
self.inner.update_request_generator_state(maximum_number_of_rooms)?;
Ok(result)
Ok(new_changes)
}
// Reset `Self`.
@@ -385,12 +403,25 @@ impl SlidingSyncListInner {
})
}
// Update the [`Self::room_list`]. It also updates
// `[Self::maximum_number_of_rooms]`.
/// Update the [`Self::room_list`]. It also updates
/// `[Self::maximum_number_of_rooms]`.
///
/// The `maximum_number_of_rooms` is the `lists.$this_list.count` value,
/// i.e. maximum number of available rooms as defined by the server. The
/// `list_sync_operations` is the `list.$this_list.ops` value
/// received from the server for this specific list. It consists of
/// operations to “move” rooms' positions. Finally,
/// the `rooms_that_have_received_an_update` is the `rooms` value received
/// from the server, which represents aggregated rooms that have received an
/// update. Maybe their position has changed, maybe they have received a new
/// event in their timeline. We need this information to update the
/// `room_list` even if the position of the room hasn't be modified: it
/// helps the user to know that a room has received an update.
fn update_room_list(
&self,
maximum_number_of_rooms: u32,
sync_operations: &[v4::SyncOp],
list_sync_operations: &[v4::SyncOp],
rooms_that_have_received_an_update: &[OwnedRoomId],
) -> Result<bool, Error> {
let mut new_changes = false;
@@ -409,8 +440,6 @@ impl SlidingSyncListInner {
}
// Update the `maximum_number_of_rooms` if it has changed.
// Again, it should happened only once at the beginning, as the value of
// `maximum_number_of_rooms` should not change over time.
{
let mut maximum_number_of_rooms_lock = self.maximum_number_of_rooms.write().unwrap();
@@ -421,13 +450,61 @@ impl SlidingSyncListInner {
}
}
// Run the sync operations.
if !sync_operations.is_empty() {
{
let mut room_list = self.room_list.write().unwrap();
apply_sync_operations(sync_operations, &mut room_list)?;
// Run the sync operations.
let mut rooms_that_have_received_an_update =
HashSet::from_iter(rooms_that_have_received_an_update.iter().cloned());
new_changes = true;
if !list_sync_operations.is_empty() {
apply_sync_operations(
list_sync_operations,
&mut room_list,
&mut rooms_that_have_received_an_update,
)?;
new_changes = true;
}
// Finally, some rooms have received an update, but their position
// didn't change in the `room_list`, so no “diff” has
// been triggered. The `room_list` value is used by the
// user to know if a room has received an update: be either a
// position modification or an update in general (like a new room
// message). Let's trigger those.
let ranges = self.ranges.read().unwrap();
for (start, end) in ranges.iter().map(|(start, end)| {
(usize::try_from(*start).unwrap(), usize::try_from(*end).unwrap())
}) {
let mut rooms_to_update =
Vec::with_capacity(rooms_that_have_received_an_update.len());
for (position, room_list_entry) in
room_list.iter().enumerate().skip(start).take(end.saturating_add(1))
{
// Invalidated rooms must be considered as empty rooms, so let's just filter by
// filled rooms.
if let RoomListEntry::Filled(room_id) = room_list_entry {
// If room has received an update but that has not been handled by a
// sync operation.
if rooms_that_have_received_an_update.contains(room_id) {
rooms_to_update.push((position, room_list_entry.clone()));
}
}
}
if !rooms_to_update.is_empty() {
for (position, room_list_entry) in rooms_to_update {
// Setting to `room_list`'s item to the same value, just
// to generate an “diff update”.
room_list.set(position, room_list_entry);
}
new_changes = true;
}
}
}
Ok(new_changes)
@@ -525,6 +602,7 @@ impl SlidingSyncListInner {
fn apply_sync_operations(
operations: &[v4::SyncOp],
room_list: &mut ObservableVector<RoomListEntry>,
rooms_that_have_received_an_update: &mut HashSet<OwnedRoomId>,
) -> Result<(), Error> {
for operation in operations {
match &operation.op {
@@ -615,6 +693,10 @@ fn apply_sync_operations(
for (room_entry_index, room_id) in room_entry_range.zip(room_ids) {
// Syncing means updating the room list to `Filled`.
room_list.set(room_entry_index, RoomListEntry::Filled(room_id.clone()));
// This `room_id` has been handled, let's remove it from the rooms to handle
// later.
rooms_that_have_received_an_update.remove(room_id);
}
}
@@ -639,11 +721,17 @@ fn apply_sync_operations(
// `DELETE` for an index that doesn't exist. It happens with the existing
// Sliding Sync Proxy (at the time of writing). It may be a bug or something
// else. Anyway, it's better to consider an out-of-bounds `DELETE` as a no-op.
return Ok(());
continue;
}
// Removing the entry in the room list.
room_list.remove(index);
let room_entry = room_list.remove(index);
// This `room_id` has been handled, let's remove it from the rooms to handle
// later.
if let Some(room_id) = room_entry.as_room_id() {
rooms_that_have_received_an_update.remove(room_id);
}
}
// Specification says:
@@ -676,6 +764,10 @@ fn apply_sync_operations(
)));
}
// This `room_id` is being handled, let's remove it from the rooms to handle
// later.
rooms_that_have_received_an_update.remove(&room_id);
// Inserting a `Filled` entry in the room list .
room_list.insert(index, RoomListEntry::Filled(room_id));
}
@@ -737,9 +829,25 @@ fn apply_sync_operations(
//
// If the previous room list entry is `Filled`, it becomes `Invalidated`.
// Otherwise, for `Empty` or `Invalidated`, it stays as is.
if let Some(RoomListEntry::Filled(room_id)) = room_list.get(room_entry_index) {
room_list
.set(room_entry_index, RoomListEntry::Invalidated(room_id.clone()));
match room_list.get(room_entry_index) {
Some(RoomListEntry::Filled(room_id)) => {
// This `room_id` is being handled, let's remove it from the rooms to
// handle later.
rooms_that_have_received_an_update.remove(room_id);
room_list.set(
room_entry_index,
RoomListEntry::Invalidated(room_id.to_owned()),
);
}
Some(RoomListEntry::Invalidated(room_id)) => {
// This `room_id` has been handled, let's remove it from the rooms to
// handle later.
rooms_that_have_received_an_update.remove(room_id);
}
_ => {}
}
}
}
@@ -808,9 +916,11 @@ impl SlidingSyncMode {
mod tests {
use std::ops::Deref;
use futures::StreamExt;
use imbl::vector;
use ruma::{api::client::sync::sync_events::v4::SlidingOp, assign, room_id, uint};
use serde_json::json;
use tokio::{spawn, sync::mpsc::unbounded_channel};
use super::*;
@@ -1154,7 +1264,7 @@ mod tests {
}))
.unwrap();
list.handle_response(6, &[sync0]).unwrap();
list.update(6, &[sync0], &[]).unwrap();
assert_eq!(list.get_room_id(0), Some(room0.to_owned()));
assert_eq!(list.get_room_id(1), Some(room1.to_owned()));
@@ -1191,7 +1301,7 @@ mod tests {
);
// Fake a response.
let _ = $list.handle_response($maximum_number_of_rooms, &[]);
let _ = $list.update($maximum_number_of_rooms, &[], &[]);
assert_eq!(
$list.inner.request_generator.read().unwrap().is_fully_loaded(),
@@ -1500,8 +1610,9 @@ mod tests {
};
}
#[test]
fn test_sliding_sync_inner_update_state_room_lists_and_maximum_number_of_rooms() {
#[tokio::test]
#[allow(clippy::await_holding_lock)]
async fn test_sliding_sync_inner_update_state_room_list_and_maximum_number_of_rooms() {
let mut list = SlidingSyncList::builder()
.name("foo")
.sync_mode(SlidingSyncMode::Selective)
@@ -1531,7 +1642,7 @@ mod tests {
}))
.unwrap();
let new_changes = list.handle_response(5, &[sync]).unwrap();
let new_changes = list.update(5, &[sync], &[]).unwrap();
assert!(new_changes);
@@ -1554,41 +1665,77 @@ mod tests {
);
}
// Update the range.
for _ in 0..=1 {
// Simulate a request.
let _ = list.next_request();
let mut room_list_stream = list.room_list_stream();
let (room_list_stream_sender, mut room_list_stream_receiver) = unbounded_channel();
// A new response.
let sync: v4::SyncOp = serde_json::from_value(json!({
"op": SlidingOp::Sync,
"range": [3, 4],
"room_ids": [room3, room4],
}))
spawn(async move {
while let Some(diff) = room_list_stream.next().await {
room_list_stream_sender.send(diff).unwrap();
}
});
// Simulate a request.
let _ = list.next_request();
// A new response.
let sync: v4::SyncOp = serde_json::from_value(json!({
"op": SlidingOp::Sync,
"range": [3, 4],
"room_ids": [room3, room4],
}))
.unwrap();
let new_changes = list
.update(
5,
&[sync],
// Let's imagine `room2` has received an update, but its position doesn't
// change.
&[room3.to_owned(), room4.to_owned(), room2.to_owned()],
)
.unwrap();
let new_changes = list.handle_response(5, &[sync]).unwrap();
assert!(new_changes);
assert!(new_changes);
// The `maximum_number_of_rooms` has been updated as expected.
assert_eq!(**list.inner.maximum_number_of_rooms.read().unwrap(), Some(5));
// The `maximum_number_of_rooms` has been updated as expected.
assert_eq!(**list.inner.maximum_number_of_rooms.read().unwrap(), Some(5));
// The `room_list` has the correct size and contains expected room entries.
let room_list = list.inner.room_list.read().unwrap();
// The `room_list` has the correct size and contains expected room entries.
let room_list = list.inner.room_list.read().unwrap();
assert_eq!(room_list.len(), 5);
assert_eq!(
**room_list,
vector![
RoomListEntry::Filled(room0.to_owned()),
RoomListEntry::Filled(room1.to_owned()),
RoomListEntry::Filled(room2.to_owned()),
RoomListEntry::Filled(room3.to_owned()),
RoomListEntry::Filled(room4.to_owned()),
]
);
assert_eq!(room_list.len(), 5);
assert_eq!(
**room_list,
vector![
RoomListEntry::Filled(room0.to_owned()),
RoomListEntry::Filled(room1.to_owned()),
RoomListEntry::Filled(room2.to_owned()),
RoomListEntry::Filled(room3.to_owned()),
RoomListEntry::Filled(room4.to_owned()),
]
);
}
// Wait for “diff” to be generated.
// Why this? Because the following `assert_eq` are using `try_recv()` instead of
// recv().await`, so that a missing “diff” doesn't make the test to hang
// forever.
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
// `room3` has been modified by a `SYNC` operation.
assert_eq!(
room_list_stream_receiver.try_recv(),
Ok(VectorDiff::Set { index: 3, value: RoomListEntry::Filled(room3.to_owned()) })
);
// `room4` has been modified by a `SYNC` operation.
assert_eq!(
room_list_stream_receiver.try_recv(),
Ok(VectorDiff::Set { index: 4, value: RoomListEntry::Filled(room4.to_owned()) })
);
// `room2` has been modified by another update (like a new event).
assert_eq!(
room_list_stream_receiver.try_recv(),
Ok(VectorDiff::Set { index: 2, value: RoomListEntry::Filled(room2.to_owned()) })
);
}
#[test]
@@ -1632,16 +1779,18 @@ mod tests {
macro_rules! assert_sync_operations {
(
room_list = [ $( $room_list_entries:tt )* ],
operations = [
sync_operations = [
$(
{ $( $operation:tt )+ }
),*
$(,)?
]
$( , rooms = [ $( $rooms:literal ),* ] )?
$(,)?
=>
result = $result:tt,
room_list = [ $( $expected_room_list_entries:tt )* ]
$( , rooms = [ $( $expected_rooms:literal ),* ] )?
$(,)?
) => {
let mut room_list = ObservableVector::from(entries![ $( $room_list_entries )* ]);
@@ -1653,10 +1802,33 @@ mod tests {
),*
];
let result = apply_sync_operations(operations, &mut room_list);
let mut rooms_that_have_received_an_update = HashSet::<OwnedRoomId>::new();
$(
{
$(
rooms_that_have_received_an_update.insert(room_id!( $rooms ).to_owned());
)*
}
)?
let result = apply_sync_operations(operations, &mut room_list, &mut rooms_that_have_received_an_update);
assert!(result.$result());
assert_eq!(*room_list, entries![ $( $expected_room_list_entries )* ]);
$(
#[allow(unused_mut)]
let mut expected_rooms_that_have_received_an_update = HashSet::<OwnedRoomId>::new();
{
$(
expected_rooms_that_have_received_an_update.insert(room_id!( $expected_rooms ).to_owned());
)*
}
assert_eq!(rooms_that_have_received_an_update, expected_rooms_that_have_received_an_update);
)?
};
}
@@ -1664,23 +1836,25 @@ mod tests {
fn test_sync_operations_sync() {
// All room list is updated.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
room_list = [E, E, E, F("!r3:x.y")],
sync_operations = [
{
"op": SlidingOp::Sync,
"range": [0, 2],
"room_ids": ["!r0:x.y", "!r1:x.y", "!r2:x.y"],
}
]
],
rooms = ["!r0:x.y", "!r1:x.y", "!r2:x.y", "!r3:x.y"], // `r3` has received an update, but its position didn't change
=>
result = is_ok,
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y"), F("!r3:x.y")],
rooms = ["!r3:x.y"],
};
// Partial update.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Sync,
"range": [0, 1],
@@ -1693,7 +1867,7 @@ mod tests {
};
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Sync,
"range": [1, 2],
@@ -1711,7 +1885,7 @@ mod tests {
// See https://github.com/matrix-org/sliding-sync/issues/52.
assert_sync_operations! {
room_list = [E],
operations = [
sync_operations = [
{
"op": SlidingOp::Sync,
"range": [0, 9], // <- it should be [0, 0]
@@ -1726,7 +1900,7 @@ mod tests {
// Missing `range`.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Sync,
"room_ids": ["!r0:x.y", "!r1:x.y"],
@@ -1740,7 +1914,7 @@ mod tests {
// Invalid `range`.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Sync,
"range": [2, 0],
@@ -1755,7 +1929,7 @@ mod tests {
// Missing `room_ids`.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Sync,
"range": [0, 2],
@@ -1769,7 +1943,7 @@ mod tests {
// Out of bounds operation.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Sync,
"range": [2, 3],
@@ -1791,7 +1965,7 @@ mod tests {
// missing.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Sync,
"range": [0, 2],
@@ -1813,7 +1987,7 @@ mod tests {
// room IDs.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Sync,
"range": [0, 1],
@@ -1837,21 +2011,23 @@ mod tests {
// Delete a room entry in the middle.
assert_sync_operations! {
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Delete,
"index": 1,
}
]
],
rooms = ["!r0:x.y", "!r1:x.y"], // `r0` has also received an update.
=>
result = is_ok,
room_list = [F("!r0:x.y"), F("!r2:x.y")],
rooms = ["!r0:x.y"],
};
// Delete a room entry at the beginning.
assert_sync_operations! {
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Delete,
"index": 0,
@@ -1865,7 +2041,7 @@ mod tests {
// Delete a room entry at the end.
assert_sync_operations! {
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Delete,
"index": 2,
@@ -1879,7 +2055,7 @@ mod tests {
// Delete an out of bounds room entry.
assert_sync_operations! {
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Delete,
"index": 3,
@@ -1896,22 +2072,24 @@ mod tests {
// Insert a room entry in the middle.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Insert,
"index": 1,
"room_id": "!r1:x.y",
}
]
],
rooms = ["!r0:x.y", "!r1:x.y"], // `r0` has also received an update.
=>
result = is_ok,
room_list = [E, F("!r1:x.y"), E, E],
rooms = ["!r0:x.y"],
};
// Insert a room entry at the beginning.
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Insert,
"index": 0,
@@ -1926,7 +2104,7 @@ mod tests {
// Insert a room entry at the end
assert_sync_operations! {
room_list = [E, E, E],
operations = [
sync_operations = [
{
"op": SlidingOp::Insert,
"index": 3,
@@ -1941,7 +2119,7 @@ mod tests {
// Insert an out of bounds room entry.
assert_sync_operations! {
room_list = [E, F("!r1:x.y"), E],
operations = [
sync_operations = [
{
"op": SlidingOp::Insert,
"index": 4,
@@ -1958,50 +2136,56 @@ mod tests {
fn test_sync_operations_invalidate() {
// Invalidating an empty room.
assert_sync_operations! {
room_list = [E],
operations = [
room_list = [E, F("!r1:x.y")],
sync_operations = [
{
"op": SlidingOp::Invalidate,
"range": [0, 0],
}
]
],
rooms = ["!r1:x.y"], // `r1` has also received an update.
=>
result = is_ok,
room_list = [E],
room_list = [E, F("!r1:x.y")],
rooms = ["!r1:x.y"],
};
// Invalidating a filled room.
assert_sync_operations! {
room_list = [F("!r0:x.y")],
operations = [
room_list = [F("!r0:x.y"), F("!r1:x.y")],
sync_operations = [
{
"op": SlidingOp::Invalidate,
"range": [0, 0],
}
]
],
rooms = ["!r0:x.y", "!r1:x.y"], // `r1` has also received an update.
=>
result = is_ok,
room_list = [I("!r0:x.y")],
room_list = [I("!r0:x.y"), F("!r1:x.y")],
rooms = ["!r1:x.y"],
};
// Invalidating an invalidated room.
assert_sync_operations! {
room_list = [I("!r0:x.y")],
operations = [
room_list = [I("!r0:x.y"), F("!r1:x.y")],
sync_operations = [
{
"op": SlidingOp::Invalidate,
"range": [0, 0],
}
]
],
rooms = ["!r0:x.y", "!r1:x.y"], // `r1` has also received an update.
=>
result = is_ok,
room_list = [I("!r0:x.y")],
room_list = [I("!r0:x.y"), F("!r1:x.y")],
rooms = ["!r1:x.y"],
};
// Partial update.
assert_sync_operations! {
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Invalidate,
"range": [0, 1],
@@ -2013,7 +2197,7 @@ mod tests {
};
assert_sync_operations! {
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Invalidate,
"range": [1, 2],
@@ -2027,7 +2211,7 @@ mod tests {
// Full update.
assert_sync_operations! {
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Invalidate,
"range": [0, 2],
@@ -2044,7 +2228,7 @@ mod tests {
// See https://github.com/matrix-org/sliding-sync/issues/52.
assert_sync_operations! {
room_list = [F("!r0:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Invalidate,
"range": [0, 9], // <- it should be [0, 0]
@@ -2058,7 +2242,7 @@ mod tests {
// Missing `range`.
assert_sync_operations! {
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Invalidate,
}
@@ -2071,7 +2255,7 @@ mod tests {
// Invalid `range`.
assert_sync_operations! {
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
operations = [
sync_operations = [
{
"op": SlidingOp::Invalidate,
"range": [12, 0],

View File

@@ -317,9 +317,11 @@ impl SlidingSync {
}
let update_summary = {
let mut updated_rooms = Vec::new();
let mut rooms_map = self.inner.rooms.write().unwrap();
// Update the rooms.
let mut updated_rooms = Vec::with_capacity(sliding_sync_response.rooms.len());
for (room_id, mut room_data) in sliding_sync_response.rooms.into_iter() {
// `sync_response` contains the rooms with decrypted events if any, so look at
// the timeline events here first if the room exists.
@@ -353,7 +355,8 @@ impl SlidingSync {
updated_rooms.push(room_id);
}
let mut updated_lists = Vec::new();
// Update the lists.
let mut updated_lists = Vec::with_capacity(sliding_sync_response.lists.len());
for (name, updates) in sliding_sync_response.lists {
let Some(list) = lists.get_mut(&name) else {
@@ -363,9 +366,9 @@ impl SlidingSync {
};
let maximum_number_of_rooms: u32 =
updates.count.try_into().expect("the list total count convertible into u32");
updates.count.try_into().expect("failed to convert `count` to `u32`");
if list.handle_response(maximum_number_of_rooms, &updates.ops)? {
if list.update(maximum_number_of_rooms, &updates.ops, &updated_rooms)? {
updated_lists.push(name.clone());
}
}

View File

@@ -360,7 +360,7 @@ async fn adding_list_later() -> anyhow::Result<()> {
// we only heard about the ones we had asked for
if !summary.lists.is_empty() {
// only if we saw an update come through
assert_eq!(summary.lists, [list_name_3]);
assert_eq!(summary.lists, [list_name_1, list_name_2, list_name_3]);
// we didn't update the other lists, so only no 2 should se an update
saw_update = true;
break;