test(e2e): resigning a private room, disbanding it, gifting privileges

Three messages that were implemented and only encoder-tested: a member
giving up their own membership (136), an owner disbanding the room and
freeing its name (137), and a privileged account handing time to another
user (123), which the recipient then sees as privilege time of their own.
This commit is contained in:
Michel de Graaf
2026-09-09 17:17:41 +00:00
parent e30cca6825
commit 17769568d2
2 changed files with 129 additions and 3 deletions
+3 -3
View File
@@ -58,13 +58,13 @@ Message names and codes follow the Soulseek protocol as implemented by
| 117 / 118 | AddThingIHate / Remove | yes | e2e — hates come back in UserInterests |
| 120 | RoomSearch | yes | e2e — a member of the room answers |
| 121 | SendUploadSpeed | yes | e2e — the server records a finished upload |
| 123 | GivePrivileges | yes | unit — needs privileges to exercise |
| 123 | GivePrivileges | yes | e2e — a privileged account hands time over |
| 126 / 127 | BranchLevel / BranchRoot | yes | e2e — leaf adoption, and children |
| 130 | ResetDistributed | yes | unit — a new session starts parentless |
| 133 | RoomMembers | yes | e2e — a private room's roster |
| 134 / 135 | AddRoomMember / Remove | yes | e2e — granted and revoked |
| 136 | CancelRoomMembership | yes | unit |
| 137 | CancelRoomOwnership | yes | unit |
| 136 | CancelRoomMembership | yes | e2e — a member resigns |
| 137 | CancelRoomOwnership | yes | e2e — the owner disbands the room |
| 139 / 140 | RoomMembershipGranted / Revoked | yes | e2e — the guest is told |
| 141 | EnableRoomInvitations | yes | e2e — set before an invitation |
| 142 | ChangePassword | yes | e2e — the next login needs the new one |
+126
View File
@@ -5284,3 +5284,129 @@ fn a_room_reports_members_arriving_and_leaving() {
alice.room_members(room)
);
}
#[test]
fn a_member_can_give_up_a_private_room_and_an_owner_can_disband_it() {
use soulseek_rs::types::RoomEvent;
let server = server_or_skip!();
let room = "e2e_private_leaving";
let mut owner =
Client::with_settings(server.settings("e2e_leave_owner", "pw"));
let mut guest =
Client::with_settings(server.settings("e2e_leave_guest", "pw"));
owner.connect().expect("owner connect");
guest.connect().expect("guest connect");
assert!(owner.login().expect("owner login"));
assert!(guest.login().expect("guest login"));
guest
.set_room_invitations_enabled(true)
.expect("guest accepts invitations");
owner
.join_private_room(room)
.expect("owner creates the room");
std::thread::sleep(Duration::from_millis(750));
owner
.add_room_member(room, "e2e_leave_guest")
.expect("owner invites the guest");
let granted = await_room_event(&guest, Duration::from_secs(5), |event| {
matches!(event, RoomEvent::OwnStandingChanged {
room: r, members: true, granted: true
} if r == room)
.then_some(())
});
assert!(granted.is_some(), "the guest should be a member first");
// The guest gives up their own membership (code 136): the owner's roster
// loses them.
guest.leave_private_room(room).expect("guest resigns");
let deadline = Instant::now() + Duration::from_secs(10);
while Instant::now() < deadline
&& owner
.private_room_members(room)
.iter()
.any(|u| u == "e2e_leave_guest")
{
std::thread::sleep(Duration::from_millis(200));
}
assert!(
!owner
.private_room_members(room)
.iter()
.any(|u| u == "e2e_leave_guest"),
"a member who resigned should leave the roster, got {:?}",
owner.private_room_members(room)
);
// The owner disbands the room (code 137), which frees the name: someone
// else asking for it is not refused as they would be for a room that
// still belongs to another user.
owner.disband_private_room(room).expect("owner disbands");
std::thread::sleep(Duration::from_secs(1));
let mut newcomer =
Client::with_settings(server.settings("e2e_leave_newcomer", "pw"));
newcomer.connect().expect("newcomer connect");
assert!(newcomer.login().expect("newcomer login"));
newcomer
.join_private_room(room)
.expect("newcomer asks for the name");
let refused =
await_room_event(&newcomer, Duration::from_secs(3), |event| {
matches!(event, RoomEvent::CantCreate { room: r } if r == room)
.then_some(())
});
assert!(
refused.is_none(),
"a disbanded room's name should be free again"
);
}
#[test]
fn privileges_can_be_handed_to_another_user() {
let server = server_or_skip!();
// Only a privileged account can give privileges away, and privileges
// themselves are granted in soulfind's own database.
let giver = "e2e_gift_giver";
let mut giving = Client::with_settings(server.settings(giver, "pw"));
giving.connect().expect("giver connect");
assert!(giving.login().expect("giver login"));
if !grant(&server, giver, "privileges") {
println!("e2e skipped: cannot grant privileges on this server");
return;
}
drop(giving);
std::thread::sleep(Duration::from_secs(1));
let mut giving = Client::with_settings(server.settings(giver, "pw"));
giving.connect().expect("giver reconnect");
assert!(giving.login().expect("giver re-login"));
let mut receiver =
Client::with_settings(server.settings("e2e_gift_receiver", "pw"));
receiver.connect().expect("receiver connect");
assert!(receiver.login().expect("receiver login"));
giving
.give_privileges("e2e_gift_receiver", 1)
.expect("hand over a day of privileges");
// The recipient now has privilege time of their own to check (code 92).
let deadline = Instant::now() + Duration::from_secs(10);
let mut left = 0;
while Instant::now() < deadline && left == 0 {
receiver
.check_privileges()
.expect("ask how much time we have");
std::thread::sleep(Duration::from_millis(500));
left = receiver.own_privilege_seconds().unwrap_or(0);
}
assert!(
left > 0,
"the gifted privileges should show up as time of our own"
);
}