135 lines
5.6 KiB
Go
135 lines
5.6 KiB
Go
package dispatcher
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.ipao.vip/rogee/go-sip/internal/configread"
|
|
)
|
|
|
|
func policySnapshot(t *testing.T) configread.Snapshot {
|
|
t.Helper()
|
|
var snapshot configread.Snapshot
|
|
if err := json.Unmarshal(configExample(t, "config-read-task-asr"), &snapshot.Task); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sip := string(configExample(t, "config-read-sip"))
|
|
for _, replacement := range [][2]string{{`"transport":null`, `"transport":"udp"`}, {`"auth_mode":null`, `"auth_mode":"ip"`}, {`"registration_required":null`, `"registration_required":false`}, {`"max_concurrent_calls":null`, `"max_concurrent_calls":2`}} {
|
|
sip = strings.Replace(sip, replacement[0], replacement[1], 1)
|
|
}
|
|
if err := json.Unmarshal([]byte(sip), &snapshot.SIP); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := json.Unmarshal(configExample(t, "config-read-quota"), &snapshot.Quota); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var providerList struct {
|
|
Providers []configread.Provider `json:"providers"`
|
|
}
|
|
if err := json.Unmarshal(configExample(t, "config-read-providers"), &providerList); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
snapshot.Providers = make(map[string]configread.Provider, len(providerList.Providers))
|
|
for _, provider := range providerList.Providers {
|
|
snapshot.Providers[provider.ProviderRef] = provider
|
|
}
|
|
return snapshot
|
|
}
|
|
|
|
func monday(hour, minute int) time.Time {
|
|
return time.Date(2026, 9, 21, hour, minute, 0, 0, time.FixedZone("Asia/Shanghai", 8*60*60))
|
|
}
|
|
|
|
func TestSelectTrunkSaaSEventCalleeScheduleAndCaller(t *testing.T) {
|
|
snapshot := policySnapshot(t)
|
|
at := monday(9, 30)
|
|
selected, err := SelectTrunk(snapshot, "15003164745", at, map[string]int64{}, map[string]int64{"trunk-mock": 8})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if selected.TrunkID != "trunk-mock" || selected.CallerID != "BD00000000" || selected.Callee != "15003164745" || selected.DialedCallee != "15003164745" || selected.Deadline.After(at.Add(120*time.Second)) || !selected.Deadline.After(at) || selected.MaxCallDurationMS != snapshot.Task.MaxCallDurationMS {
|
|
t.Fatalf("selected route/prefix/caller/deadline invalid: %+v", selected)
|
|
}
|
|
for _, number := range []string{"15803300952", "13900000000", "15003164746"} {
|
|
choice, err := SelectTrunk(snapshot, number, at, nil, map[string]int64{"trunk-mock": 8})
|
|
if err != nil || choice.Callee != number || choice.DialedCallee != number {
|
|
t.Fatalf("SaaS event number changed or rejected: number=%q choice=%+v err=%v", number, choice, err)
|
|
}
|
|
}
|
|
for _, number := range []string{"", "abc", "15803300952x", "15803300952\n", strings.Repeat("7", 33)} {
|
|
if _, err := SelectTrunk(snapshot, number, at, nil, map[string]int64{"trunk-mock": 8}); !errors.Is(err, ErrCalleeRejected) {
|
|
t.Fatalf("invalid raw dial route %q not rejected: %v", number, err)
|
|
}
|
|
}
|
|
for _, at := range []time.Time{monday(8, 59), monday(20, 0)} {
|
|
if _, err := SelectTrunk(snapshot, "15003164745", at, nil, map[string]int64{"trunk-mock": 8}); !errors.Is(err, ErrRuleWait) {
|
|
t.Fatalf("outside half-open schedule did not wait: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSelectTrunkUsesEachLinesOwnCaller(t *testing.T) {
|
|
snapshot := policySnapshot(t)
|
|
var trunks []trunkConfig
|
|
if err := json.Unmarshal(snapshot.SIP.Trunks, &trunks); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second := trunks[0]
|
|
second.TrunkID, second.CallerID = "trunk-second", "mbkq"
|
|
trunks = append(trunks, second)
|
|
var err error
|
|
snapshot.SIP.Trunks, err = json.Marshal(trunks)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
snapshot.Task.AllowedTrunkIDs = []string{second.TrunkID}
|
|
chosen, err := SelectTrunk(snapshot, "15003164745", monday(9, 30), nil, map[string]int64{second.TrunkID: 8})
|
|
if err != nil || chosen.TrunkID != second.TrunkID || chosen.CallerID != second.CallerID {
|
|
t.Fatalf("second line did not keep its own approved caller: %+v, %v", chosen, err)
|
|
}
|
|
trunks[1].CallerID = ""
|
|
snapshot.SIP.Trunks, err = json.Marshal(trunks)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := SelectTrunk(snapshot, "15003164745", monday(9, 30), nil, map[string]int64{second.TrunkID: 8}); !errors.Is(err, ErrRuleInvalid) {
|
|
t.Fatalf("second line with missing caller did not fail closed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSelectTrunkFailsClosedForUnknownLineAndLimits(t *testing.T) {
|
|
snapshot := policySnapshot(t)
|
|
at := monday(9, 30)
|
|
if _, err := SelectTrunk(snapshot, "15003164745", at, nil, nil); !errors.Is(err, ErrRuleWait) {
|
|
t.Fatalf("unloaded line did not hold command: %v", err)
|
|
}
|
|
if _, err := SelectTrunk(snapshot, "15003164745", at, map[string]int64{"trunk-mock": 2}, map[string]int64{"trunk-mock": 8}); !errors.Is(err, ErrRuleWait) {
|
|
t.Fatalf("full trunk did not hold command: %v", err)
|
|
}
|
|
sip := string(configExample(t, "config-read-sip"))
|
|
if err := json.Unmarshal([]byte(sip), &snapshot.SIP); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := SelectTrunk(snapshot, "15003164745", at, nil, map[string]int64{"trunk-mock": 8}); !errors.Is(err, ErrRuleInvalid) {
|
|
t.Fatalf("unknown transport/auth/quota was not fail-closed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSelectTrunkCapsCallByAIAndPreservesPrefix(t *testing.T) {
|
|
snapshot := policySnapshot(t)
|
|
snapshot.Task.Agent.Mode = "full_ai"
|
|
snapshot.Task.Agent.Raw = []byte(`{"mode":"full_ai","conversation":{"max_duration_ms":60000}}`)
|
|
snapshot.SIP.Trunks = []byte(strings.Replace(string(snapshot.SIP.Trunks), `"dial_prefix":""`, `"dial_prefix":"7089"`, 1))
|
|
at := monday(9, 30)
|
|
selected, err := SelectTrunk(snapshot, "15830461047", at, nil, map[string]int64{"trunk-mock": 8})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if selected.DialedCallee != "708915830461047" || selected.Callee != "15830461047" || !selected.Deadline.Equal(at.Add(time.Minute)) || selected.MaxCallDurationMS != 60000 {
|
|
t.Fatalf("wrong prefix/AI duration bound: %+v", selected)
|
|
}
|
|
}
|