diff --git a/docs/protocol-coverage.md b/docs/protocol-coverage.md index 98abe88..0646c4c 100644 --- a/docs/protocol-coverage.md +++ b/docs/protocol-coverage.md @@ -70,8 +70,8 @@ Message names and codes follow the Soulseek protocol as implemented by | 142 | ChangePassword | yes | e2e — the next login needs the new one | | 143 / 144 | AddRoomOperator / Remove | yes | e2e — the guest is promoted | | 145 / 146 | RoomOperatorshipGranted / Revoked | yes | e2e — the guest is told | -| 147 | CancelRoomOperatorship | yes | unit | -| 148 | RoomOperators | yes | unit | +| 147 | CancelRoomOperatorship | yes | e2e — an operator stands down | +| 148 | RoomOperators | yes | e2e — the roster after a promotion | | 149 | MessageUsers | yes | e2e — one message, two recipients | | 150 / 151 / 152 | Global room | yes | e2e — the feed starts and stops | | 160 | ExcludedSearchPhrases | yes | e2e — the list is kept for our replies | diff --git a/soulseek-rs-lib/tests/e2e.rs b/soulseek-rs-lib/tests/e2e.rs index bded8db..aa72f6b 100644 --- a/soulseek-rs-lib/tests/e2e.rs +++ b/soulseek-rs-lib/tests/e2e.rs @@ -4655,6 +4655,48 @@ fn a_private_room_is_owned_granted_and_revoked() { "the guest should be told they run the room" ); + // The owner's operator roster carries them too (code 143). + let deadline = Instant::now() + Duration::from_secs(5); + while Instant::now() < deadline + && !owner + .private_room_operators(room) + .iter() + .any(|u| u == "e2e_priv_guest") + { + std::thread::sleep(Duration::from_millis(100)); + } + assert!( + owner + .private_room_operators(room) + .iter() + .any(|u| u == "e2e_priv_guest"), + "the operator roster should carry the guest, got {:?}", + owner.private_room_operators(room) + ); + + // And an operator can stand down themselves (code 147), which the + // owner's roster reflects. + guest + .resign_room_operatorship(room) + .expect("guest stands down"); + let deadline = Instant::now() + Duration::from_secs(10); + while Instant::now() < deadline + && owner + .private_room_operators(room) + .iter() + .any(|u| u == "e2e_priv_guest") + { + std::thread::sleep(Duration::from_millis(200)); + } + assert!( + !owner + .private_room_operators(room) + .iter() + .any(|u| u == "e2e_priv_guest"), + "an operator who stood down should leave the roster, got {:?}", + owner.private_room_operators(room) + ); + // And membership can be taken away again (code 140). owner .remove_room_member(room, "e2e_priv_guest")