Files
go-sip/internal/asterisk/hep_test.go
T

141 lines
5.0 KiB
Go

package asterisk
import (
"context"
"encoding/binary"
"strings"
"sync"
"testing"
"time"
)
func hepTestFrame(cid, sip string) []byte {
packet := []byte("HEP3\x00\x00")
for _, chunk := range []struct {
kind uint16
data []byte
}{{11, []byte{1}}, {17, []byte(cid)}, {15, []byte(sip)}} {
part := make([]byte, 6+len(chunk.data))
binary.BigEndian.PutUint16(part[2:4], chunk.kind)
binary.BigEndian.PutUint16(part[4:6], uint16(len(part)))
copy(part[6:], chunk.data)
packet = append(packet, part...)
}
binary.BigEndian.PutUint16(packet[4:6], uint16(len(packet)))
return packet
}
func hepTestSIP(callID, method, branch, firstLine string) string {
return firstLine + "\r\nVia: SIP/2.0/UDP 127.0.0.1:5060;branch=" + branch +
"\r\nFrom: <sip:caller@localhost>;tag=a\r\nTo: <sip:callee@localhost>\r\nCall-ID: " + callID +
"\r\nCSeq: 1 " + method + "\r\nContent-Length: 0\r\n\r\n"
}
func TestHEPMirrorKeepsConcurrentSIPTransactionsSeparate(t *testing.T) {
mirror := NewHEPMirror()
if err := mirror.Bind("exec-a", "sip-id-a"); err != nil {
t.Fatal(err)
}
if err := mirror.Bind("exec-b", "sip-id-b"); err != nil {
t.Fatal(err)
}
if err := mirror.Bind("exec-duplicate", "sip-id-a"); err == nil {
t.Fatal("one SIP Call-ID cannot belong to two executions")
}
var wg sync.WaitGroup
for _, tc := range []struct {
id, branch, code string
}{{"sip-id-a", "z9hG4bK-a", "480 Temporarily Unavailable"}, {"sip-id-b", "z9hG4bK-b", "486 Busy Here"}} {
wg.Add(1)
go func() {
defer wg.Done()
invite := hepTestSIP(tc.id, "INVITE", tc.branch, "INVITE sip:callee@localhost SIP/2.0")
response := hepTestSIP(tc.id, "INVITE", tc.branch, "SIP/2.0 "+tc.code)
if err := mirror.Observe(hepTestFrame(tc.id, response)); err != nil { // UDP may reorder the response.
t.Error(err)
}
if err := mirror.Observe(hepTestFrame(tc.id, invite)); err != nil {
t.Error(err)
}
}()
}
wg.Wait()
for _, tc := range []struct {
execID, want string
}{{"exec-a", "SIP/2.0 480 Temporarily Unavailable"}, {"exec-b", "SIP/2.0 486 Busy Here"}} {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
resp, err := mirror.Final(ctx, tc.execID)
cancel()
if err != nil || resp.StatusLine != tc.want || !strings.HasPrefix(resp.Raw, tc.want+"\r\n") {
t.Fatalf("wrong execution response: %+v err=%v", resp, err)
}
}
}
func TestHEPMirrorRejectsUnmatchedAndMalformedPacketsWithoutGuessing(t *testing.T) {
mirror := NewHEPMirror()
if err := mirror.Bind("exec-a", "sip-id-a"); err != nil {
t.Fatal(err)
}
for _, bad := range [][]byte{nil, []byte("HEP3"), []byte("HEP3\x00\x06"), []byte("HEP3\x00\x0c\x00\x00\x00\x0f\x00\x00")} {
if err := mirror.Observe(bad); err == nil {
t.Fatalf("malformed frame accepted: len=%d", len(bad))
}
}
unrelated := hepTestSIP("sip-id-b", "INVITE", "z9hG4bK-b", "SIP/2.0 480 Temporarily Unavailable")
if err := mirror.Observe(hepTestFrame("sip-id-b", unrelated)); err != nil {
t.Fatalf("unrelated traffic is not a parser error: %v", err)
}
incorrectCID := hepTestSIP("sip-id-b", "INVITE", "z9hG4bK-b", "SIP/2.0 480 Temporarily Unavailable")
if err := mirror.Observe(hepTestFrame("sip-id-a", incorrectCID)); err == nil {
t.Fatal("payload Call-ID and HEP CID disagreed")
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
if resp, err := mirror.Final(ctx, "exec-a"); err == nil || resp != nil {
t.Fatalf("missing observed response was fabricated: %+v err=%v", resp, err)
}
}
func TestHEPMirrorKeepsOriginalInviteWhenLaterReinviteArrives(t *testing.T) {
mirror := NewHEPMirror()
if err := mirror.Bind("exec", "sip-id"); err != nil {
t.Fatal(err)
}
for _, sip := range []string{
hepTestSIP("sip-id", "INVITE", "z9hG4bK-first", "INVITE sip:callee@localhost SIP/2.0"),
hepTestSIP("sip-id", "INVITE", "z9hG4bK-first", "SIP/2.0 200 OK"),
hepTestSIP("sip-id", "INVITE", "z9hG4bK-later", "INVITE sip:callee@localhost SIP/2.0"),
hepTestSIP("sip-id", "INVITE", "z9hG4bK-later", "SIP/2.0 488 Not Acceptable Here"),
} {
if err := mirror.Observe(hepTestFrame("sip-id", sip)); err != nil {
t.Fatal(err)
}
}
resp, err := mirror.Final(context.Background(), "exec")
if err != nil || resp.Code != 200 {
t.Fatalf("later reINVITE must not replace original INVITE outcome: %+v %v", resp, err)
}
}
func TestHEPMirrorNeverTreatsProvisionalOrOtherMethodsAsFinalINVITE(t *testing.T) {
mirror := NewHEPMirror()
if err := mirror.Bind("exec-a", "sip-id-a"); err != nil {
t.Fatal(err)
}
for _, sip := range []string{
hepTestSIP("sip-id-a", "INVITE", "z9hG4bK-a", "INVITE sip:callee@localhost SIP/2.0"),
hepTestSIP("sip-id-a", "INVITE", "z9hG4bK-a", "SIP/2.0 180 Ringing"),
hepTestSIP("sip-id-a", "BYE", "z9hG4bK-a", "SIP/2.0 200 OK"),
} {
if err := mirror.Observe(hepTestFrame("sip-id-a", sip)); err != nil {
t.Fatal(err)
}
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancel()
if resp, err := mirror.Final(ctx, "exec-a"); err == nil || resp != nil {
t.Fatalf("non-final or non-INVITE response became an outcome: %+v err=%v", resp, err)
}
}