#[cfg(test)]
use crate::dialog::invite::Role;
use crate::dialog::invite::{Action, Event, InviteDialog, TerminationReason};
use crate::dialog::state::{DialogId, DialogState};
use crate::sip::{Method, SipRequest, SipResponse};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DialogHandle(u64);
#[derive(Debug, Clone)]
pub enum ManagerAction {
SendRequest(SipRequest),
SendResponse(SipResponse),
Event(DialogHandle, ManagerEvent),
}
#[derive(Debug, Clone)]
pub enum ManagerEvent {
IncomingInvite(SipRequest),
Established,
Provisional(SipResponse),
SessionProgress(SipResponse),
ReInvite(SipRequest),
ByeReceived(SipRequest),
Terminated(TerminationReason),
}
#[derive(Debug)]
pub struct DialogManager {
next_handle: u64,
dialogs: HashMap<DialogHandle, InviteDialog>,
id_to_handle: HashMap<DialogId, DialogHandle>,
pending_uac: HashMap<(String, String), DialogHandle>,
actions: Vec<ManagerAction>,
local_contact: String,
}
impl DialogManager {
pub fn new(local_contact: impl Into<String>) -> Self {
Self {
next_handle: 1,
dialogs: HashMap::new(),
id_to_handle: HashMap::new(),
pending_uac: HashMap::new(),
actions: Vec::new(),
local_contact: local_contact.into(),
}
}
pub fn create_dialog(&mut self, invite: SipRequest) -> Option<DialogHandle> {
if invite.method() != Method::Invite {
return None;
}
let handle = self.alloc_handle();
let dialog = InviteDialog::new_uac(invite.clone());
let call_id = invite.call_id().ok()?;
let from_tag = invite.from_tag().ok()?;
self.pending_uac.insert((call_id, from_tag), handle);
self.dialogs.insert(handle, dialog);
Some(handle)
}
pub fn handle_request(&mut self, request: SipRequest) -> Option<DialogHandle> {
if let Some(handle) = self.find_dialog_for_request(&request) {
if let Some(dialog) = self.dialogs.get_mut(&handle) {
dialog.handle_request(request);
self.collect_dialog_actions(handle);
}
return Some(handle);
}
if request.method() == Method::Invite {
return self.create_uas_dialog(request);
}
None
}
pub fn handle_response(&mut self, response: SipResponse) -> Option<DialogHandle> {
let call_id = response.call_id().ok()?;
let from_tag = response.from_tag().ok()?;
let to_tag = response.to_tag();
let handle = if let Some(&h) = self.pending_uac.get(&(call_id.clone(), from_tag.clone())) {
h
} else if let Some(to_tag) = &to_tag {
let id = DialogId::new(&call_id, &from_tag, to_tag);
*self.id_to_handle.get(&id)?
} else {
return None;
};
if let Some(dialog) = self.dialogs.get_mut(&handle) {
let old_state = dialog.state();
dialog.handle_response(response);
let new_state = dialog.state();
if old_state == DialogState::Early && new_state == DialogState::Confirmed {
let id = dialog.id().clone();
self.id_to_handle.insert(id, handle);
self.pending_uac.remove(&(call_id, from_tag));
}
self.collect_dialog_actions(handle);
}
Some(handle)
}
pub fn send_response(&mut self, handle: DialogHandle, response: SipResponse) {
if let Some(dialog) = self.dialogs.get_mut(&handle) {
let old_state = dialog.state();
dialog.send_response(response);
let new_state = dialog.state();
if old_state == DialogState::Early && new_state == DialogState::Confirmed {
let id = dialog.id().clone();
self.id_to_handle.insert(id, handle);
}
self.collect_dialog_actions(handle);
}
}
pub fn send_bye(&mut self, handle: DialogHandle) -> Option<SipRequest> {
let dialog = self.dialogs.get_mut(&handle)?;
let bye = dialog.send_bye();
self.collect_dialog_actions(handle);
bye
}
pub fn ack_sent(&mut self, handle: DialogHandle) {
if let Some(dialog) = self.dialogs.get_mut(&handle) {
dialog.ack_sent();
}
}
pub fn dialog(&self, handle: DialogHandle) -> Option<&InviteDialog> {
self.dialogs.get(&handle)
}
pub fn poll_actions(&mut self) -> Vec<ManagerAction> {
std::mem::take(&mut self.actions)
}
pub fn cleanup_terminated(&mut self) {
let mut to_remove = Vec::new();
for (&handle, dialog) in &self.dialogs {
if dialog.is_terminated() {
to_remove.push((handle, dialog.id().clone()));
}
}
for (handle, id) in to_remove {
self.dialogs.remove(&handle);
self.id_to_handle.remove(&id);
}
}
fn alloc_handle(&mut self) -> DialogHandle {
let handle = DialogHandle(self.next_handle);
self.next_handle += 1;
handle
}
fn create_uas_dialog(&mut self, request: SipRequest) -> Option<DialogHandle> {
let local_tag = format!("{}", uuid::Uuid::new_v4().simple());
let handle = self.alloc_handle();
let dialog = InviteDialog::new_uas(request.clone(), &local_tag, &self.local_contact)?;
let id = dialog.id().clone();
self.dialogs.insert(handle, dialog);
self.id_to_handle.insert(id, handle);
self.actions.push(ManagerAction::Event(
handle,
ManagerEvent::IncomingInvite(request),
));
Some(handle)
}
fn find_dialog_for_request(&self, request: &SipRequest) -> Option<DialogHandle> {
let call_id = request.call_id().ok()?;
let from_tag = request.from_tag().ok()?;
let to_tag = request.to_tag()?;
let id = DialogId::new(&call_id, &to_tag, &from_tag);
self.id_to_handle.get(&id).copied()
}
fn collect_dialog_actions(&mut self, handle: DialogHandle) {
if let Some(dialog) = self.dialogs.get_mut(&handle) {
for action in dialog.poll_actions() {
let manager_action = match action {
Action::SendRequest(req) => ManagerAction::SendRequest(req),
Action::SendResponse(resp) => ManagerAction::SendResponse(resp),
Action::Event(event) => {
let manager_event = match event {
Event::Established => ManagerEvent::Established,
Event::Provisional(resp) => ManagerEvent::Provisional(resp),
Event::SessionProgress(resp) => ManagerEvent::SessionProgress(resp),
Event::ReInvite(req) => ManagerEvent::ReInvite(req),
Event::ByeReceived(req) => ManagerEvent::ByeReceived(req),
Event::Terminated(reason) => ManagerEvent::Terminated(reason),
};
ManagerAction::Event(handle, manager_event)
}
};
self.actions.push(manager_action);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sip::SipMessage;
fn create_invite() -> SipRequest {
SipRequest::builder()
.method(Method::Invite)
.uri("sip:bob@example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKtest")
.from("sip:alice@example.com", "fromtag")
.to("sip:bob@example.com")
.call_id("test@example.com")
.cseq(1)
.contact("sip:alice@192.168.1.1:5060")
.build()
.unwrap()
}
fn create_response(request: &SipRequest, code: u16) -> SipResponse {
SipResponse::builder()
.status(code, "Test")
.from_request(request)
.to_tag("totag")
.contact("sip:bob@192.168.1.2:5060")
.build()
.unwrap()
}
fn parse_request(raw: &str) -> SipRequest {
let msg = SipMessage::parse(raw.as_bytes()).unwrap();
msg.as_request().unwrap().clone()
}
fn parse_response(raw: &str) -> SipResponse {
let msg = SipMessage::parse(raw.as_bytes()).unwrap();
msg.as_response().unwrap().clone()
}
#[test]
fn test_create_uac_dialog() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite).unwrap();
assert!(handle.0 > 0);
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.role(), Role::Uac);
}
#[test]
fn test_create_dialog_missing_call_id() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = parse_request(
"INVITE sip:bob@example.com SIP/2.0\r\n\
Via: SIP/2.0/UDP 192.168.1.1:5060;branch=z9hG4bKtest\r\n\
From: <sip:alice@example.com>;tag=fromtag\r\n\
To: <sip:bob@example.com>\r\n\
CSeq: 1 INVITE\r\n\
Contact: <sip:alice@192.168.1.1:5060>\r\n\
Content-Length: 0\r\n\
\r\n",
);
assert!(mgr.create_dialog(invite).is_none());
}
#[test]
fn test_create_dialog_missing_from_tag() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = parse_request(
"INVITE sip:bob@example.com SIP/2.0\r\n\
Via: SIP/2.0/UDP 192.168.1.1:5060;branch=z9hG4bKtest\r\n\
From: <sip:alice@example.com>\r\n\
To: <sip:bob@example.com>\r\n\
Call-ID: test@example.com\r\n\
CSeq: 1 INVITE\r\n\
Contact: <sip:alice@192.168.1.1:5060>\r\n\
Content-Length: 0\r\n\
\r\n",
);
assert!(mgr.create_dialog(invite).is_none());
}
#[test]
fn test_handle_request_missing_call_id_non_invite() {
let req = parse_request(
"BYE sip:bob@example.com SIP/2.0\r\n\
Via: SIP/2.0/UDP 192.168.1.1:5060;branch=z9hG4bKtest\r\n\
To: <sip:bob@example.com>;tag=totag\r\n\
From: <sip:alice@example.com>;tag=fromtag\r\n\
CSeq: 1 BYE\r\n\
Content-Length: 0\r\n\
\r\n",
);
let mut manager = DialogManager::new("sip:me@192.168.1.1:5060");
let handle = manager.handle_request(req);
assert!(handle.is_none());
}
#[test]
fn test_handle_response_establishes_dialog() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite.clone()).unwrap();
let response = create_response(&invite, 200);
let result_handle = mgr.handle_response(response).unwrap();
assert_eq!(result_handle, handle);
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.state(), DialogState::Confirmed);
}
#[test]
fn test_handle_response_missing_call_id() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let response = parse_response(
"SIP/2.0 200 OK\r\n\
Via: SIP/2.0/UDP 192.168.1.1:5060;branch=z9hG4bKtest\r\n\
From: <sip:alice@example.com>;tag=fromtag\r\n\
To: <sip:bob@example.com>;tag=totag\r\n\
CSeq: 1 INVITE\r\n\
Contact: <sip:bob@192.168.1.2:5060>\r\n\
Content-Length: 0\r\n\
\r\n",
);
assert!(mgr.handle_response(response).is_none());
}
#[test]
fn test_handle_response_missing_from_tag() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let response = parse_response(
"SIP/2.0 200 OK\r\n\
Via: SIP/2.0/UDP 192.168.1.1:5060;branch=z9hG4bKtest\r\n\
From: <sip:alice@example.com>\r\n\
To: <sip:bob@example.com>;tag=totag\r\n\
Call-ID: test@example.com\r\n\
CSeq: 1 INVITE\r\n\
Contact: <sip:bob@192.168.1.2:5060>\r\n\
Content-Length: 0\r\n\
\r\n",
);
assert!(mgr.handle_response(response).is_none());
}
#[test]
fn test_handle_response_session_progress_event() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite.clone()).unwrap();
let response = create_response(&invite, 183);
let result_handle = mgr.handle_response(response).unwrap();
assert_eq!(result_handle, handle);
let actions = mgr.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, ManagerAction::Event(_, ManagerEvent::SessionProgress(_)))));
}
#[test]
fn test_incoming_invite_creates_dialog() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let invite = create_invite();
let handle = mgr.handle_request(invite).unwrap();
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.role(), Role::Uas);
assert_eq!(dialog.state(), DialogState::Early);
let actions = mgr.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, ManagerAction::Event(_, ManagerEvent::IncomingInvite(_)))));
}
#[test]
fn test_incoming_invite_missing_from_tag() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let invite = parse_request(
"INVITE sip:bob@example.com SIP/2.0\r\n\
Via: SIP/2.0/UDP 192.168.1.1:5060;branch=z9hG4bKtest\r\n\
From: <sip:alice@example.com>\r\n\
To: <sip:bob@example.com>\r\n\
Call-ID: test@example.com\r\n\
CSeq: 1 INVITE\r\n\
Contact: <sip:alice@192.168.1.1:5060>\r\n\
Content-Length: 0\r\n\
\r\n",
);
let handle = mgr.handle_request(invite);
assert!(handle.is_none());
}
#[test]
fn test_handle_request_reinvite_event() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let invite = create_invite();
let handle = mgr.handle_request(invite.clone()).unwrap();
let dialog = mgr.dialog(handle).unwrap();
let local_tag = dialog.id().local_tag.clone();
let remote_tag = dialog.id().remote_tag.clone();
let call_id = dialog.id().call_id.clone();
let response = SipResponse::builder()
.status(200, "OK")
.from_request(&invite)
.to_tag(&local_tag)
.contact("sip:bob@192.168.1.2:5060")
.build()
.unwrap();
mgr.send_response(handle, response);
mgr.poll_actions();
let reinvite = SipRequest::builder()
.method(Method::Invite)
.uri("sip:bob@example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKreinvite")
.from("sip:alice@example.com", &remote_tag)
.to("sip:bob@example.com")
.to_tag(&local_tag)
.call_id(&call_id)
.cseq(2)
.build()
.unwrap();
let result_handle = mgr.handle_request(reinvite).unwrap();
assert_eq!(result_handle, handle);
let actions = mgr.poll_actions();
assert!(actions
.iter()
.any(|a| matches!(a, ManagerAction::Event(_, ManagerEvent::ReInvite(_)))));
}
#[test]
fn test_send_200_establishes_dialog() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let invite = create_invite();
let handle = mgr.handle_request(invite.clone()).unwrap();
mgr.poll_actions();
let response = SipResponse::builder()
.status(200, "OK")
.from_request(&invite)
.to_tag("localtag")
.contact("sip:bob@192.168.1.2:5060")
.build()
.unwrap();
mgr.send_response(handle, response);
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.state(), DialogState::Confirmed);
}
#[test]
fn test_send_response_provisional_does_not_confirm() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let invite = create_invite();
let handle = mgr.handle_request(invite.clone()).unwrap();
mgr.poll_actions();
let response = SipResponse::builder()
.status(180, "Ringing")
.from_request(&invite)
.to_tag("localtag")
.contact("sip:bob@192.168.1.2:5060")
.build()
.unwrap();
mgr.send_response(handle, response);
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.state(), DialogState::Early);
}
#[test]
fn test_send_response_after_confirmed_does_not_remap() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let invite = create_invite();
let handle = mgr.handle_request(invite.clone()).unwrap();
mgr.poll_actions();
let response = SipResponse::builder()
.status(200, "OK")
.from_request(&invite)
.to_tag("localtag")
.contact("sip:bob@192.168.1.2:5060")
.build()
.unwrap();
mgr.send_response(handle, response);
mgr.poll_actions();
let response = SipResponse::builder()
.status(200, "OK")
.from_request(&invite)
.to_tag("localtag")
.contact("sip:bob@192.168.1.2:5060")
.build()
.unwrap();
mgr.send_response(handle, response);
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.state(), DialogState::Confirmed);
}
#[test]
fn test_dialog_handle_debug() {
let handle = DialogHandle(1);
let debug = format!("{:?}", handle);
assert!(debug.contains("DialogHandle"));
}
#[test]
#[allow(clippy::clone_on_copy)] fn test_dialog_handle_clone() {
let handle = DialogHandle(1);
let cloned = handle.clone();
assert_eq!(handle, cloned);
}
#[test]
fn test_dialog_handle_copy() {
let handle = DialogHandle(1);
let copied: DialogHandle = handle;
assert_eq!(handle, copied);
}
#[test]
fn test_dialog_handle_eq() {
assert_eq!(DialogHandle(1), DialogHandle(1));
assert_ne!(DialogHandle(1), DialogHandle(2));
}
#[test]
fn test_dialog_handle_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(DialogHandle(1));
set.insert(DialogHandle(2));
assert_eq!(set.len(), 2);
assert!(set.contains(&DialogHandle(1)));
}
#[test]
fn test_manager_action_debug() {
let action = ManagerAction::Event(DialogHandle(1), ManagerEvent::Established);
let debug = format!("{:?}", action);
assert!(debug.contains("Event"));
}
#[test]
fn test_manager_action_clone() {
let action = ManagerAction::Event(DialogHandle(1), ManagerEvent::Established);
let cloned = action.clone();
let debug = format!("{:?}", cloned);
assert!(debug.contains("Established"));
}
#[test]
fn test_manager_event_debug() {
assert!(format!("{:?}", ManagerEvent::Established).contains("Established"));
assert!(
format!("{:?}", ManagerEvent::Terminated(TerminationReason::ByeSent))
.contains("Terminated")
);
}
#[test]
fn test_manager_event_clone() {
let event = ManagerEvent::Established;
let cloned = event.clone();
let debug = format!("{:?}", cloned);
assert!(debug.contains("Established"));
}
#[test]
fn test_dialog_manager_debug() {
let mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let debug = format!("{:?}", mgr);
assert!(debug.contains("DialogManager"));
}
#[test]
fn test_create_dialog_non_invite_returns_none() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let register = SipRequest::builder()
.method(Method::Register)
.uri("sip:registrar@example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKtest")
.from("sip:alice@example.com", "fromtag")
.to("sip:alice@example.com")
.call_id("test@example.com")
.cseq(1)
.build()
.unwrap();
let result = mgr.create_dialog(register);
assert!(result.is_none());
}
#[test]
fn test_handle_request_bye_on_existing_dialog() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let invite = create_invite();
let handle = mgr.handle_request(invite.clone()).unwrap();
mgr.poll_actions();
let response = SipResponse::builder()
.status(200, "OK")
.from_request(&invite)
.to_tag("localtag")
.contact("sip:bob@192.168.1.2:5060")
.build()
.unwrap();
mgr.send_response(handle, response);
mgr.poll_actions();
let dialog = mgr.dialog(handle).unwrap();
let local_tag = dialog.id().local_tag.clone();
let bye = SipRequest::builder()
.method(Method::Bye)
.uri("sip:bob@192.168.1.2:5060")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKbye")
.from("sip:alice@example.com", "fromtag")
.to("sip:bob@example.com")
.to_tag(&local_tag)
.call_id("test@example.com")
.cseq(2)
.build()
.unwrap();
let result = mgr.handle_request(bye);
assert!(result.is_some());
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.state(), DialogState::Terminated);
}
#[test]
fn test_handle_request_for_unknown_dialog() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let bye = SipRequest::builder()
.method(Method::Bye)
.uri("sip:bob@192.168.1.2:5060")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKbye")
.from("sip:alice@example.com", "fromtag")
.to("sip:bob@example.com")
.to_tag("unknowntag")
.call_id("unknown@example.com")
.cseq(1)
.build()
.unwrap();
let result = mgr.handle_request(bye);
assert!(result.is_none());
}
#[test]
fn test_handle_request_missing_dialog_entry() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let handle = DialogHandle(42);
let id = DialogId::new("test@example.com", "totag", "fromtag");
mgr.id_to_handle.insert(id, handle);
let bye = SipRequest::builder()
.method(Method::Bye)
.uri("sip:bob@192.168.1.2:5060")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKbye")
.from("sip:alice@example.com", "fromtag")
.to("sip:bob@example.com")
.to_tag("totag")
.call_id("test@example.com")
.cseq(1)
.build()
.unwrap();
let result = mgr.handle_request(bye);
assert_eq!(result, Some(handle));
assert!(mgr.dialog(handle).is_none());
}
#[test]
fn test_handle_response_without_pending_or_to_tag() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let response = SipResponse::builder()
.status(200, "OK")
.from_request(&invite)
.build()
.unwrap();
let result = mgr.handle_response(response);
assert!(result.is_none());
}
#[test]
fn test_handle_response_missing_dialog_entry() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite.clone()).unwrap();
let response = create_response(&invite, 200);
mgr.dialogs.remove(&handle);
let result = mgr.handle_response(response);
assert_eq!(result, Some(handle));
assert!(mgr.dialog(handle).is_none());
}
#[test]
fn test_handle_response_uses_established_mapping() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite.clone()).unwrap();
let response = create_response(&invite, 200);
mgr.handle_response(response);
let followup = create_response(&invite, 200);
let result = mgr.handle_response(followup).unwrap();
assert_eq!(result, handle);
}
#[test]
fn test_send_bye_unknown_handle_returns_none() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let bye = mgr.send_bye(DialogHandle(999));
assert!(bye.is_none());
}
#[test]
fn test_handle_response_provisional() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite.clone()).unwrap();
let response = create_response(&invite, 180);
let result_handle = mgr.handle_response(response).unwrap();
assert_eq!(result_handle, handle);
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.state(), DialogState::Early);
}
#[test]
fn test_handle_response_rejection() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite.clone()).unwrap();
let response = create_response(&invite, 486);
let result_handle = mgr.handle_response(response).unwrap();
assert_eq!(result_handle, handle);
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.state(), DialogState::Terminated);
}
#[test]
fn test_handle_response_unknown_dialog() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let response = create_response(&invite, 200);
let result = mgr.handle_response(response);
assert!(result.is_none());
}
#[test]
fn test_send_bye() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite.clone()).unwrap();
let response = create_response(&invite, 200);
mgr.handle_response(response);
mgr.poll_actions();
let bye = mgr.send_bye(handle);
assert!(bye.is_some());
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.state(), DialogState::Terminating);
}
#[test]
fn test_send_bye_non_existent_dialog() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let bye = mgr.send_bye(DialogHandle(999));
assert!(bye.is_none());
}
#[test]
fn test_ack_sent() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite.clone()).unwrap();
let response = create_response(&invite, 200);
mgr.handle_response(response);
mgr.ack_sent(handle);
let dialog = mgr.dialog(handle).unwrap();
assert!(dialog.is_ack_complete());
}
#[test]
fn test_ack_sent_non_existent_dialog() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
mgr.ack_sent(DialogHandle(999));
}
#[test]
fn test_collect_dialog_actions_missing_handle() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
mgr.collect_dialog_actions(DialogHandle(999));
assert!(mgr.poll_actions().is_empty());
}
#[test]
fn test_cleanup_terminated() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite.clone()).unwrap();
let response = create_response(&invite, 486);
mgr.handle_response(response);
mgr.poll_actions();
let dialog = mgr.dialog(handle).unwrap();
assert_eq!(dialog.state(), DialogState::Terminated);
mgr.cleanup_terminated();
assert!(mgr.dialog(handle).is_none());
}
#[test]
fn test_cleanup_terminated_noop_for_active() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite = create_invite();
let handle = mgr.create_dialog(invite).unwrap();
mgr.cleanup_terminated();
assert!(mgr.dialog(handle).is_some());
}
#[test]
fn test_poll_actions_clears() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let invite = create_invite();
mgr.handle_request(invite);
let actions = mgr.poll_actions();
assert!(!actions.is_empty());
let actions2 = mgr.poll_actions();
assert!(actions2.is_empty());
}
#[test]
fn test_send_response_with_invalid_handle() {
let mut mgr = DialogManager::new("sip:me@192.168.1.2:5060");
let invite = create_invite();
let response = create_response(&invite, 200);
mgr.send_response(DialogHandle(999), response);
}
#[test]
fn test_multiple_dialogs() {
let mut mgr = DialogManager::new("sip:me@192.168.1.1:5060");
let invite1 = SipRequest::builder()
.method(Method::Invite)
.uri("sip:bob@example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKtest1")
.from("sip:alice@example.com", "fromtag1")
.to("sip:bob@example.com")
.call_id("call1@example.com")
.cseq(1)
.contact("sip:alice@192.168.1.1:5060")
.build()
.unwrap();
let handle1 = mgr.create_dialog(invite1).unwrap();
let invite2 = SipRequest::builder()
.method(Method::Invite)
.uri("sip:carol@example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKtest2")
.from("sip:alice@example.com", "fromtag2")
.to("sip:carol@example.com")
.call_id("call2@example.com")
.cseq(1)
.contact("sip:alice@192.168.1.1:5060")
.build()
.unwrap();
let handle2 = mgr.create_dialog(invite2).unwrap();
assert_ne!(handle1, handle2);
assert!(mgr.dialog(handle1).is_some());
assert!(mgr.dialog(handle2).is_some());
}
#[test]
fn test_manager_event_variants() {
let invite = create_invite();
let response = create_response(&invite, 180);
let event1 = ManagerEvent::IncomingInvite(invite.clone());
assert!(format!("{:?}", event1).contains("IncomingInvite"));
let event2 = ManagerEvent::Provisional(response.clone());
assert!(format!("{:?}", event2).contains("Provisional"));
let event3 = ManagerEvent::SessionProgress(response.clone());
assert!(format!("{:?}", event3).contains("SessionProgress"));
let event4 = ManagerEvent::ReInvite(invite.clone());
assert!(format!("{:?}", event4).contains("ReInvite"));
let event5 = ManagerEvent::ByeReceived(invite);
assert!(format!("{:?}", event5).contains("ByeReceived"));
}
#[test]
fn test_manager_action_variants() {
let invite = create_invite();
let response = create_response(&invite, 200);
let action1 = ManagerAction::SendRequest(invite);
assert!(format!("{:?}", action1).contains("SendRequest"));
let action2 = ManagerAction::SendResponse(response);
assert!(format!("{:?}", action2).contains("SendResponse"));
}
}