use crate::sip::*;
#[cfg(test)]
mod header_parsing {
use super::*;
#[test]
fn test_header_folding_whitespace() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob\r\n \
<sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let result = SipMessage::parse(msg);
assert!(result.is_ok() || result.is_err()); }
#[test]
fn test_header_case_insensitive() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
max-forwards: 70\r\n\
to: Bob <sip:bob@biloxi.com>\r\n\
from: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
call-id: a84b4c76e66710@pc33.atlanta.com\r\n\
cseq: 314159 INVITE\r\n\
content-length: 0\r\n\
\r\n";
let result = SipMessage::parse(msg);
assert!(result.is_ok());
let req = result.unwrap().as_request().unwrap().clone();
assert_eq!(req.method(), Method::Invite);
assert_eq!(req.cseq().unwrap(), 314159);
}
#[test]
fn test_header_lws_around_values() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com> \r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774 \r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let result = SipMessage::parse(msg);
assert!(result.is_ok());
}
#[test]
fn test_cseq_method_matching() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
assert_eq!(req.method(), req.cseq_method().unwrap());
}
#[test]
fn test_cseq_method_mismatch_detectable() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 REGISTER\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
assert_eq!(req.cseq_method().unwrap(), Method::Register);
assert_ne!(req.method(), req.cseq_method().unwrap());
}
#[test]
fn test_content_length_zero() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
assert_eq!(req.body().len(), 0);
}
#[test]
fn test_content_length_with_body() {
let sdp_body = b"v=0\r\n";
let msg = format!(
"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Type: application/sdp\r\n\
Content-Length: {}\r\n\
\r\n{}",
sdp_body.len(),
std::str::from_utf8(sdp_body).unwrap()
);
let parsed = SipMessage::parse(msg.as_bytes()).unwrap();
let req = parsed.as_request().unwrap();
assert_eq!(req.body(), sdp_body);
}
#[test]
fn test_from_header_tag_required() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=abc123\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
assert!(req.from_tag().is_ok());
assert_eq!(req.from_tag().unwrap(), "abc123");
}
#[test]
fn test_to_header_no_tag_in_request() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
assert!(req.to_tag().is_none());
}
#[test]
fn test_to_header_tag_in_response() {
let msg = b"SIP/2.0 200 OK\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let resp = parsed.as_response().unwrap();
assert_eq!(resp.to_tag(), Some("a6c85cf".to_string()));
}
}
#[cfg(test)]
mod via_header {
use super::*;
#[test]
fn test_via_branch_magic_cookie() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
let branch = req.via_branch().unwrap();
assert!(branch.starts_with("z9hG4bK"));
}
#[test]
fn test_via_branch_without_magic_cookie() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=oldstyle123\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
let branch = req.via_branch().unwrap();
assert_eq!(branch, "oldstyle123");
}
#[test]
fn test_multiple_via_headers() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP proxy2.example.com;branch=z9hG4bKproxy2\r\n\
Via: SIP/2.0/UDP proxy1.example.com;branch=z9hG4bKproxy1\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
let vias = req.via_headers_raw();
assert_eq!(vias.len(), 3);
assert!(vias[0].contains("proxy2"));
}
#[test]
fn test_via_received_parameter() {
let msg = b"SIP/2.0 200 OK\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds;received=192.0.2.1\r\n\
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let resp = parsed.as_response().unwrap();
let vias = resp.via_headers_raw();
assert!(!vias.is_empty());
assert!(vias[0].contains("received=192.0.2.1"));
}
#[test]
fn test_via_rport_parameter() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com:5060;branch=z9hG4bK776asdhds;rport\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
let vias = req.via_headers_raw();
assert!(vias[0].contains("rport"));
}
}
#[cfg(test)]
mod uri_parsing {
use super::*;
#[test]
fn test_sip_uri_basic() {
let uri = SipUri::parse("sip:alice@atlanta.com").unwrap();
assert_eq!(uri.scheme(), "sip");
assert_eq!(uri.user(), Some("alice"));
assert_eq!(uri.host(), "atlanta.com");
}
#[test]
fn test_sips_uri() {
let uri = SipUri::parse("sips:alice@secure.example.com").unwrap();
assert_eq!(uri.scheme(), "sips");
assert!(uri.is_secure());
}
#[test]
fn test_uri_with_port() {
let uri = SipUri::parse("sip:alice@atlanta.com:5080").unwrap();
assert_eq!(uri.port(), Some(5080));
}
#[test]
fn test_uri_without_user() {
let uri = SipUri::parse("sip:atlanta.com").unwrap();
assert_eq!(uri.user(), None);
assert_eq!(uri.host(), "atlanta.com");
}
#[test]
fn test_uri_with_transport_param() {
let uri = SipUri::parse("sip:alice@atlanta.com;transport=tcp").unwrap();
assert_eq!(uri.transport(), Some("tcp"));
}
#[test]
fn test_uri_with_user_phone_param() {
let uri = SipUri::parse("sip:+1-212-555-1212@gateway.com;user=phone").unwrap();
assert_eq!(uri.get_param("user"), Some("phone"));
}
#[test]
fn test_uri_with_lr_param() {
let uri = SipUri::parse("sip:proxy.example.com;lr").unwrap();
assert!(uri.is_loose_route());
}
#[test]
fn test_uri_with_maddr_param() {
let uri = SipUri::parse("sip:alice@atlanta.com;maddr=239.255.255.1").unwrap();
assert_eq!(uri.get_param("maddr"), Some("239.255.255.1"));
}
#[test]
fn test_uri_with_ttl_param() {
let uri = SipUri::parse("sip:alice@atlanta.com;ttl=15").unwrap();
assert_eq!(uri.get_param("ttl"), Some("15"));
}
#[test]
fn test_uri_with_method_param() {
let uri = SipUri::parse("sip:alice@atlanta.com;method=REGISTER").unwrap();
assert_eq!(uri.get_param("method"), Some("REGISTER"));
}
#[test]
fn test_tel_uri_accepted() {
let uri = SipUri::parse("tel:+1-212-555-1212").unwrap();
assert_eq!(uri.scheme(), "tel");
assert_eq!(uri.host(), "+1-212-555-1212");
assert_eq!(uri.user(), None);
assert_eq!(uri.port(), None);
}
#[test]
fn test_uri_with_ipv6() {
let uri = SipUri::parse("sip:alice@[2001:db8::1]").unwrap();
assert!(uri.host().contains("2001:db8::1"));
}
#[test]
fn test_uri_with_ipv6_and_port() {
let uri = SipUri::parse("sip:alice@[2001:db8::1]:5060").unwrap();
assert!(uri.host().contains("2001:db8::1"));
assert_eq!(uri.port(), Some(5060));
}
}
#[cfg(test)]
mod max_forwards {
use super::*;
#[test]
fn test_max_forwards_present() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
assert!(parsed.is_request());
}
#[test]
fn test_max_forwards_zero() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 0\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg);
assert!(parsed.is_ok());
}
#[test]
fn test_max_forwards_default_value() {
let req = SipRequest::builder()
.method(Method::Invite)
.uri("sip:bob@example.com")
.via("192.168.1.1", 5060, "UDP", "z9hG4bKtest")
.from("sip:alice@example.com", "tag1")
.to("sip:bob@example.com")
.call_id("call@example.com")
.cseq(1)
.build()
.unwrap();
let bytes = req.to_bytes();
let s = String::from_utf8_lossy(&bytes);
assert!(s.contains("Max-Forwards: 70"));
}
}
#[cfg(test)]
mod request_uri {
use super::*;
#[test]
fn test_request_uri_no_spaces() {
let uri = SipUri::parse("sip:alice@atlanta.com");
assert!(uri.is_ok());
}
#[test]
fn test_request_uri_vs_to_header() {
let msg = b"INVITE sip:proxy.atlanta.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
Max-Forwards: 70\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let req = parsed.as_request().unwrap();
assert!(req.uri().to_string().contains("proxy.atlanta.com"));
assert!(req.to_uri().unwrap().to_string().contains("bob@biloxi.com"));
}
}
#[cfg(test)]
mod response_codes {
use super::*;
#[test]
fn test_1xx_provisional() {
let msg = b"SIP/2.0 100 Trying\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
To: Bob <sip:bob@biloxi.com>\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let resp = parsed.as_response().unwrap();
assert!(resp.is_provisional());
assert!(!resp.is_success());
assert!(!resp.is_failure());
}
#[test]
fn test_2xx_success() {
let msg = b"SIP/2.0 200 OK\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let resp = parsed.as_response().unwrap();
assert!(resp.is_success());
assert!(!resp.is_provisional());
assert!(!resp.is_failure());
}
#[test]
fn test_3xx_redirection() {
let msg = b"SIP/2.0 302 Moved Temporarily\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let resp = parsed.as_response().unwrap();
assert!(resp.is_failure());
assert_eq!(resp.status_code(), 302);
}
#[test]
fn test_4xx_client_error() {
let msg = b"SIP/2.0 404 Not Found\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let resp = parsed.as_response().unwrap();
assert!(resp.is_failure());
assert_eq!(resp.status_code(), 404);
}
#[test]
fn test_5xx_server_error() {
let msg = b"SIP/2.0 500 Server Internal Error\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let resp = parsed.as_response().unwrap();
assert!(resp.is_failure());
assert_eq!(resp.status_code(), 500);
}
#[test]
fn test_6xx_global_failure() {
let msg = b"SIP/2.0 600 Busy Everywhere\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
To: Bob <sip:bob@biloxi.com>;tag=a6c85cf\r\n\
From: Alice <sip:alice@atlanta.com>;tag=1928301774\r\n\
Call-ID: a84b4c76e66710@pc33.atlanta.com\r\n\
CSeq: 314159 INVITE\r\n\
Content-Length: 0\r\n\
\r\n";
let parsed = SipMessage::parse(msg).unwrap();
let resp = parsed.as_response().unwrap();
assert!(resp.is_failure());
assert_eq!(resp.status_code(), 600);
}
}
#[cfg(test)]
mod methods {
use super::*;
#[test]
fn test_invite_method() {
assert_eq!(Method::Invite.to_string(), "INVITE");
assert!(Method::Invite.creates_dialog());
}
#[test]
fn test_ack_method() {
assert_eq!(Method::Ack.to_string(), "ACK");
assert!(!Method::Ack.creates_dialog());
}
#[test]
fn test_bye_method() {
assert_eq!(Method::Bye.to_string(), "BYE");
assert!(!Method::Bye.creates_dialog());
}
#[test]
fn test_cancel_method() {
assert_eq!(Method::Cancel.to_string(), "CANCEL");
assert!(!Method::Cancel.creates_dialog());
}
#[test]
fn test_register_method() {
assert_eq!(Method::Register.to_string(), "REGISTER");
assert!(!Method::Register.creates_dialog());
}
#[test]
fn test_options_method() {
assert_eq!(Method::Options.to_string(), "OPTIONS");
assert!(!Method::Options.creates_dialog());
}
}
#[cfg(test)]
mod malformed_messages {
use super::*;
#[test]
fn test_parse_garbage() {
let result = SipMessage::parse(b"This is not a SIP message");
assert!(result.is_err());
}
#[test]
fn test_parse_truncated() {
let result = SipMessage::parse(b"INVITE sip:bob@biloxi.com SIP/2.0\r\n");
assert!(result.is_err());
}
#[test]
fn test_parse_missing_headers() {
let msg = b"INVITE sip:bob@biloxi.com SIP/2.0\r\n\
Via: SIP/2.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
\r\n";
let result = SipMessage::parse(msg);
if let Ok(parsed) = result {
let req = parsed.as_request().unwrap();
assert!(req.call_id().is_err());
}
}
#[test]
fn test_parse_empty() {
let result = SipMessage::parse(b"");
assert!(result.is_err());
}
#[test]
fn test_parse_invalid_version() {
let msg = b"INVITE sip:bob@biloxi.com SIP/1.0\r\n\
Via: SIP/1.0/UDP pc33.atlanta.com;branch=z9hG4bK776asdhds\r\n\
\r\n";
let result = SipMessage::parse(msg);
assert!(result.is_err() || result.is_ok());
}
}
#[cfg(test)]
mod builder_validation {
use super::*;
#[test]
fn test_build_complete_request() {
let req = SipRequest::builder()
.method(Method::Invite)
.uri("sip:bob@example.com")
.via("192.168.1.1", 5060, "UDP", &generate_branch())
.from("sip:alice@example.com", &generate_tag())
.to("sip:bob@example.com")
.call_id(&generate_call_id("example.com"))
.cseq(1)
.contact("sip:alice@192.168.1.1:5060")
.max_forwards(70)
.build();
assert!(req.is_ok());
}
#[test]
fn test_build_request_missing_via() {
let result = SipRequest::builder()
.method(Method::Invite)
.uri("sip:bob@example.com")
.from("sip:alice@example.com", "tag1")
.to("sip:bob@example.com")
.call_id("call@example.com")
.cseq(1)
.build();
assert!(result.is_err());
}
#[test]
fn test_generate_branch_valid() {
for _ in 0..10 {
let branch = generate_branch();
assert!(branch.starts_with("z9hG4bK"));
assert!(branch.len() > 10);
}
}
#[test]
fn test_generate_tag_unique() {
let mut tags = std::collections::HashSet::new();
for _ in 0..100 {
tags.insert(generate_tag());
}
assert!(tags.len() > 90);
}
#[test]
fn test_generate_call_id_format() {
let call_id = generate_call_id("example.com");
assert!(call_id.contains('@'));
assert!(call_id.ends_with("@example.com"));
}
}