use super::*;
#[test]
fn private_invite_join_opens_chat_without_a_first_message() {
let owner = Keys::generate();
let device = Keys::generate();
let peer_owner = Keys::generate();
let peer_device = Keys::generate();
let mut inviter = isolated_invite_ui_core("join-auto-open-inviter", &owner, &device);
publish_personal_invite_for_test(&mut inviter);
inviter.handle_action(AppAction::PushScreen {
screen: Screen::NewChat,
});
assert!(matches!(inviter.screen_stack.last(), Some(Screen::NewChat)));
let url = inviter
.state
.public_invite
.as_ref()
.unwrap()
.legacy_url
.clone();
let mut joiner = isolated_invite_ui_core("join-auto-open-peer", &peer_owner, &peer_device);
joiner.handle_action(AppAction::AcceptInvite { invite_input: url });
prove_invite_owner(&mut joiner, &owner, &device, 10);
let response = pending_events_with_kind(&joiner, INVITE_RESPONSE_KIND).remove(0);
inviter.handle_relay_event(response);
assert!(
matches!(inviter.screen_stack.last(), Some(Screen::NewChat)),
"Unverified owner claim must not open a chat"
);
prove_invite_owner(&mut inviter, &peer_owner, &peer_device, 10);
assert!(
matches!(inviter.screen_stack.last(), Some(Screen::Chat { chat_id }) if chat_id == &peer_owner.public_key().to_hex()),
"An authenticated invite join should open the chat without any text message"
);
assert!(inviter.threads[&peer_owner.public_key().to_hex()]
.messages
.is_empty());
assert!(
!inviter.private_chat_invites.is_empty(),
"Reusable invite survives authenticated joins"
);
}
#[test]
fn private_invite_join_respects_unchecked_away_and_different_invite() {
for mode in ["unchecked", "away", "different_invite"] {
let owner = Keys::generate();
let device = Keys::generate();
let peer_owner = Keys::generate();
let peer_device = Keys::generate();
let mut inviter = isolated_invite_ui_core("invite-no-focus-steal", &owner, &device);
publish_personal_invite_for_test(&mut inviter);
inviter.handle_action(AppAction::PushScreen {
screen: Screen::NewChat,
});
assert!(
inviter
.state
.public_invite
.as_ref()
.unwrap()
.open_chat_on_join
);
let url = inviter
.state
.public_invite
.as_ref()
.unwrap()
.legacy_url
.clone();
let mut joiner =
isolated_invite_ui_core("invite-no-focus-steal-peer", &peer_owner, &peer_device);
joiner.handle_action(AppAction::AcceptInvite {
invite_input: url.clone(),
});
prove_invite_owner(&mut joiner, &owner, &device, 10);
match mode {
"unchecked" => {
inviter.handle_action(AppAction::SetInviteOpenOnJoin { enabled: false });
assert!(
!inviter
.state
.public_invite
.as_ref()
.unwrap()
.open_chat_on_join
);
}
"away" => inviter.handle_action(AppAction::PushScreen {
screen: Screen::Settings,
}),
"different_invite" => {
std::thread::sleep(Duration::from_millis(1100));
publish_personal_invite_for_test(&mut inviter);
assert_ne!(
inviter.state.public_invite.as_ref().unwrap().legacy_url,
url
);
}
_ => unreachable!(),
}
let before = inviter.screen_stack.clone();
inviter
.handle_relay_event(pending_events_with_kind(&joiner, INVITE_RESPONSE_KIND).remove(0));
prove_invite_owner(&mut inviter, &peer_owner, &peer_device, 10);
assert!(
inviter
.protocol_engine
.as_ref()
.unwrap()
.active_session_count_for_owner(peer_owner.public_key())
> 0
);
assert!(inviter.threads[&peer_owner.public_key().to_hex()]
.messages
.is_empty());
assert_eq!(
inviter.screen_stack, before,
"{mode}: handshake must not steal navigation"
);
}
}
pub(super) fn isolated_invite_ui_core(label: &str, owner: &Keys, device: &Keys) -> AppCore {
let mut core = logged_in_test_core(label, owner, device);
core.preferences.nearby_enabled = false;
core.preferences.nostr_relay_urls.clear();
let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
let std::net::SocketAddr::V4(address) = socket.local_addr().unwrap() else {
unreachable!()
};
drop(socket);
core.reconcile_device_sync_at_rendezvous_for_test(address);
core
}