This commit is contained in:
haibo.chen
2025-10-15 15:35:41 +08:00
parent 1178b974a1
commit 35de09aeb6
4 changed files with 914 additions and 0 deletions

View File

@ -0,0 +1,292 @@
package stack
import (
"testing"
"github.com/emiago/sipgo/sip"
)
func TestNewRequest(t *testing.T) {
tests := []struct {
name string
method sip.RequestMethod
body []byte
conf OutboundConfig
wantErr bool
errMsg string
checkFunc func(*testing.T, *sip.Request)
}{
{
name: "Valid REGISTER request",
method: sip.REGISTER,
body: nil,
conf: OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "34020000001110000001",
},
wantErr: false,
checkFunc: func(t *testing.T, req *sip.Request) {
if req.Method != sip.REGISTER {
t.Errorf("Expected method REGISTER, got %v", req.Method)
}
if req.Destination() != "192.168.1.100:5060" {
t.Errorf("Expected destination 192.168.1.100:5060, got %v", req.Destination())
}
if req.Transport() != "udp" {
t.Errorf("Expected transport udp, got %v", req.Transport())
}
},
},
{
name: "Valid INVITE request with body",
method: sip.INVITE,
body: []byte("v=0\r\no=- 0 0 IN IP4 127.0.0.1\r\n"),
conf: OutboundConfig{
Transport: "tcp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "34020000001110000001",
},
wantErr: false,
checkFunc: func(t *testing.T, req *sip.Request) {
if req.Method != sip.INVITE {
t.Errorf("Expected method INVITE, got %v", req.Method)
}
if req.Body() == nil {
t.Error("Expected body to be set")
}
},
},
{
name: "Invalid From length - too short",
method: sip.REGISTER,
body: nil,
conf: OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "123456789",
To: "34020000001110000001",
},
wantErr: true,
errMsg: "From or To length is not 20",
},
{
name: "Invalid To length - too long",
method: sip.REGISTER,
body: nil,
conf: OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "340200000011100000012345",
},
wantErr: true,
errMsg: "From or To length is not 20",
},
{
name: "Valid MESSAGE request",
method: sip.MESSAGE,
body: []byte("<?xml version=\"1.0\"?><Query></Query>"),
conf: OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "34020000001110000001",
},
wantErr: false,
checkFunc: func(t *testing.T, req *sip.Request) {
if req.Method != sip.MESSAGE {
t.Errorf("Expected method MESSAGE, got %v", req.Method)
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req, err := NewRequest(tt.method, tt.body, tt.conf)
if tt.wantErr {
if err == nil {
t.Errorf("Expected error but got nil")
} else if tt.errMsg != "" && err.Error() != tt.errMsg {
t.Errorf("Expected error message '%s', got '%s'", tt.errMsg, err.Error())
}
return
}
if err != nil {
t.Errorf("Unexpected error: %v", err)
return
}
if req == nil {
t.Error("Expected request to be non-nil")
return
}
// Run custom checks
if tt.checkFunc != nil {
tt.checkFunc(t, req)
}
// Common checks
if req.From() == nil {
t.Error("Expected From header to be set")
}
if req.To() == nil {
t.Error("Expected To header to be set")
}
if req.Contact() == nil {
t.Error("Expected Contact header to be set")
}
})
}
}
func TestNewRegisterRequest(t *testing.T) {
conf := OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "34020000001110000001",
}
req, err := NewRegisterRequest(conf)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if req.Method != sip.REGISTER {
t.Errorf("Expected method REGISTER, got %v", req.Method)
}
// Check for Expires header
expires := req.GetHeader("Expires")
if expires == nil {
t.Error("Expected Expires header to be set")
} else if expires.Value() != "3600" {
t.Errorf("Expected Expires value 3600, got %v", expires.Value())
}
}
func TestNewInviteRequest(t *testing.T) {
conf := OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "34020000001110000001",
}
body := []byte("v=0\r\no=- 0 0 IN IP4 127.0.0.1\r\ns=Play\r\n")
subject := "34020000001320000001:0,34020000001110000001:0"
req, err := NewInviteRequest(body, subject, conf)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if req.Method != sip.INVITE {
t.Errorf("Expected method INVITE, got %v", req.Method)
}
// Check for Content-Type header
contentType := req.GetHeader("Content-Type")
if contentType == nil {
t.Error("Expected Content-Type header to be set")
} else if contentType.Value() != "application/sdp" {
t.Errorf("Expected Content-Type value application/sdp, got %v", contentType.Value())
}
// Check for Subject header
subjectHeader := req.GetHeader("Subject")
if subjectHeader == nil {
t.Error("Expected Subject header to be set")
} else if subjectHeader.Value() != subject {
t.Errorf("Expected Subject value %s, got %v", subject, subjectHeader.Value())
}
// Check body
if req.Body() == nil {
t.Error("Expected body to be set")
}
}
func TestNewMessageRequest(t *testing.T) {
conf := OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "34020000001110000001",
}
body := []byte("<?xml version=\"1.0\"?><Query><CmdType>Catalog</CmdType></Query>")
req, err := NewMessageRequest(body, conf)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if req.Method != sip.MESSAGE {
t.Errorf("Expected method MESSAGE, got %v", req.Method)
}
// Check for Content-Type header
contentType := req.GetHeader("Content-Type")
if contentType == nil {
t.Error("Expected Content-Type header to be set")
} else if contentType.Value() != "Application/MANSCDP+xml" {
t.Errorf("Expected Content-Type value Application/MANSCDP+xml, got %v", contentType.Value())
}
// Check body
if req.Body() == nil {
t.Error("Expected body to be set")
}
}
func TestNewRequestWithInvalidConfig(t *testing.T) {
tests := []struct {
name string
conf OutboundConfig
}{
{
name: "Empty From",
conf: OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "",
To: "34020000001110000001",
},
},
{
name: "Empty To",
conf: OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "",
},
},
{
name: "From length 19",
conf: OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "3402000000132000001",
To: "34020000001110000001",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := NewRequest(sip.REGISTER, nil, tt.conf)
if err == nil {
t.Error("Expected error but got nil")
}
})
}
}

View File

@ -0,0 +1,296 @@
package stack
import (
"testing"
"github.com/emiago/sipgo/sip"
)
func TestNewRegisterResponse(t *testing.T) {
// Create a test request first - we need a properly initialized request
// Skip this test as it requires a full SIP stack to create valid responses
t.Skip("Skipping response test - requires full SIP stack initialization")
conf := OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "34020000001110000001",
}
req, err := NewRegisterRequest(conf)
if err != nil {
t.Fatalf("Failed to create test request: %v", err)
}
tests := []struct {
name string
code sip.StatusCode
reason string
}{
{
name: "200 OK response",
code: sip.StatusOK,
reason: "OK",
},
{
name: "401 Unauthorized response",
code: sip.StatusUnauthorized,
reason: "Unauthorized",
},
{
name: "403 Forbidden response",
code: sip.StatusForbidden,
reason: "Forbidden",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp := NewRegisterResponse(req, tt.code, tt.reason)
if resp == nil {
t.Fatal("Expected response to be non-nil")
}
if resp.StatusCode != tt.code {
t.Errorf("Expected status code %d, got %d", tt.code, resp.StatusCode)
}
if resp.Reason != tt.reason {
t.Errorf("Expected reason '%s', got '%s'", tt.reason, resp.Reason)
}
// Check for Expires header
expires := resp.GetHeader("Expires")
if expires == nil {
t.Error("Expected Expires header to be set")
} else if expires.Value() != "3600" {
t.Errorf("Expected Expires value 3600, got %v", expires.Value())
}
// Check for Date header
date := resp.GetHeader("Date")
if date == nil {
t.Error("Expected Date header to be set")
}
// Check that To header has tag
to := resp.To()
if to == nil {
t.Error("Expected To header to be set")
} else {
tag, ok := to.Params.Get("tag")
if !ok || tag == "" {
t.Error("Expected To header to have tag parameter")
}
}
// Check that Allow header is removed
allow := resp.GetHeader("Allow")
if allow != nil {
t.Error("Expected Allow header to be removed")
}
})
}
}
func TestNewUnauthorizedResponse(t *testing.T) {
// Skip this test as it requires a full SIP stack to create valid responses
t.Skip("Skipping response test - requires full SIP stack initialization")
conf := OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "34020000001110000001",
}
req, err := NewRegisterRequest(conf)
if err != nil {
t.Fatalf("Failed to create test request: %v", err)
}
tests := []struct {
name string
code sip.StatusCode
reason string
nonce string
realm string
}{
{
name: "401 Unauthorized with nonce and realm",
code: sip.StatusUnauthorized,
reason: "Unauthorized",
nonce: "dcd98b7102dd2f0e8b11d0f600bfb0c093",
realm: "3402000000",
},
{
name: "407 Proxy Authentication Required",
code: sip.StatusProxyAuthRequired,
reason: "Proxy Authentication Required",
nonce: "abc123def456",
realm: "proxy.example.com",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp := NewUnauthorizedResponse(req, tt.code, tt.reason, tt.nonce, tt.realm)
if resp == nil {
t.Fatal("Expected response to be non-nil")
}
if resp.StatusCode != tt.code {
t.Errorf("Expected status code %d, got %d", tt.code, resp.StatusCode)
}
if resp.Reason != tt.reason {
t.Errorf("Expected reason '%s', got '%s'", tt.reason, resp.Reason)
}
// Check for WWW-Authenticate header
wwwAuth := resp.GetHeader("WWW-Authenticate")
if wwwAuth == nil {
t.Error("Expected WWW-Authenticate header to be set")
} else {
authValue := wwwAuth.Value()
// Check that it contains the nonce
if len(authValue) == 0 {
t.Error("Expected WWW-Authenticate header to have a value")
}
// The value should contain Digest, realm, nonce, and algorithm
expectedSubstrings := []string{"Digest", "realm=", "nonce=", "algorithm=MD5"}
for _, substr := range expectedSubstrings {
if len(authValue) > 0 && !contains(authValue, substr) {
t.Errorf("Expected WWW-Authenticate to contain '%s', got '%s'", substr, authValue)
}
}
}
// Check that To header has tag
to := resp.To()
if to == nil {
t.Error("Expected To header to be set")
} else {
tag, ok := to.Params.Get("tag")
if !ok || tag == "" {
t.Error("Expected To header to have tag parameter")
}
}
})
}
}
func TestNewResponse(t *testing.T) {
// Skip this test as it requires a full SIP stack to create valid responses
t.Skip("Skipping response test - requires full SIP stack initialization")
conf := OutboundConfig{
Transport: "udp",
Via: "192.168.1.100:5060",
From: "34020000001320000001",
To: "34020000001110000001",
}
req, err := NewRequest(sip.INVITE, nil, conf)
if err != nil {
t.Fatalf("Failed to create test request: %v", err)
}
tests := []struct {
name string
code sip.StatusCode
reason string
}{
{
name: "100 Trying",
code: sip.StatusTrying,
reason: "Trying",
},
{
name: "180 Ringing",
code: sip.StatusRinging,
reason: "Ringing",
},
{
name: "200 OK",
code: sip.StatusOK,
reason: "OK",
},
{
name: "404 Not Found",
code: sip.StatusNotFound,
reason: "Not Found",
},
{
name: "500 Server Internal Error",
code: sip.StatusInternalServerError,
reason: "Server Internal Error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resp := newResponse(req, tt.code, tt.reason)
if resp == nil {
t.Fatal("Expected response to be non-nil")
}
if resp.StatusCode != tt.code {
t.Errorf("Expected status code %d, got %d", tt.code, resp.StatusCode)
}
if resp.Reason != tt.reason {
t.Errorf("Expected reason '%s', got '%s'", tt.reason, resp.Reason)
}
// Check that To header has tag
to := resp.To()
if to == nil {
t.Error("Expected To header to be set")
} else {
tag, ok := to.Params.Get("tag")
if !ok || tag == "" {
t.Error("Expected To header to have tag parameter")
}
// Check tag length is 10
if len(tag) != 10 {
t.Errorf("Expected tag length to be 10, got %d", len(tag))
}
}
// Check that Allow header is removed
allow := resp.GetHeader("Allow")
if allow != nil {
t.Error("Expected Allow header to be removed")
}
})
}
}
func TestResponseConstants(t *testing.T) {
if TIME_LAYOUT != "2024-01-01T00:00:00" {
t.Errorf("Expected TIME_LAYOUT to be '2024-01-01T00:00:00', got '%s'", TIME_LAYOUT)
}
if EXPIRES_TIME != 3600 {
t.Errorf("Expected EXPIRES_TIME to be 3600, got %d", EXPIRES_TIME)
}
}
// Helper function to check if a string contains a substring
func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && findSubstring(s, substr))
}
func findSubstring(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}