Files
go-sip/internal/sipcall/run_test.go
T

161 lines
4.7 KiB
Go

package sipcall
import (
"context"
"errors"
"git.ipao.vip/rogee/go-sip/internal/asterisk"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
type fakeCall struct {
closeErr error
ended bool
ctx context.Context
}
func (c *fakeCall) Close() error { return c.closeErr }
func (c *fakeCall) EndConfirmed() bool { return c.ended }
func (c *fakeCall) Done() context.Context { return c.ctx }
type fakeHost struct {
busy, applyErr, dialErr, restoreErr error
calls, restores int
call *fakeCall
}
func (h *fakeHost) CheckIdle(context.Context) error { return h.busy }
func (h *fakeHost) Apply(context.Context, Config) (func() error, error) {
return func() error { h.restores++; return h.restoreErr }, h.applyErr
}
func (h *fakeHost) Dial(ctx context.Context, request asterisk.NativeDial) (testCall, error) {
h.calls++
if h.dialErr != nil {
return nil, h.dialErr
}
if err := request.BindSIPCall(request.ExecutionID, "sip-id-123"); err != nil {
return nil, err
}
if h.call == nil {
h.call = &fakeCall{ended: true, ctx: context.Background()}
}
return h.call, nil
}
func (h *fakeHost) Close() error { return nil }
type fakeCapture struct {
stops int
err error
}
func (c *fakeCapture) Check() error { return nil }
func (c *fakeCapture) Stop(_ context.Context, _ string) (Evidence, error) {
c.stops++
return Evidence{PCAP: "capture.pcap", SIPTimeline: "sip.tsv", Packets: 10, FinalINVITECode: 200}, c.err
}
func fixtureConfig(t *testing.T) Config {
t.Helper()
c, err := ParseConfig(strings.NewReader(validENV))
if err != nil {
t.Fatal(err)
}
c.OutputDir = t.TempDir()
if err := os.Chmod(c.OutputDir, 0700); err != nil {
t.Fatal(err)
}
c.resultDir = c.OutputDir
c.Hold = time.Millisecond
return c
}
func TestRunDefaultNeedsNoCaptureToolsAndDialsOnce(t *testing.T) {
c := fixtureConfig(t)
h := &fakeHost{}
r, err := run(context.Background(), c, "18601010101", false, h, func(context.Context, Config, string) (captureSession, error) {
t.Fatal("capture must be completely absent by default")
return nil, nil
})
if err != nil {
t.Fatal(err)
}
if r.Status != "connected" || !r.EndConfirmed || r.CallID != "sip-id-123" || r.DialedCallee != "708918601010101" || h.calls != 1 || h.restores != 1 || r.Evidence != nil {
t.Fatalf("wrong single call: %+v", r)
}
data, err := os.ReadFile(filepath.Join(r.Directory, "result.json"))
if err != nil || !strings.Contains(string(data), "connected") {
t.Fatalf("missing private result: %v", err)
}
info, _ := os.Stat(r.Directory)
if info.Mode().Perm() != 0700 {
t.Fatal("results must be private")
}
}
func TestRunFailuresAreVisibleAndNeverRetry(t *testing.T) {
for _, phase := range []string{"busy", "apply", "capture", "dial", "end", "restore", "decode"} {
t.Run(phase, func(t *testing.T) {
c := fixtureConfig(t)
h := &fakeHost{}
cap := &fakeCapture{}
debug := phase == "capture" || phase == "decode"
sentinel := errors.New("diagnostic failure")
switch phase {
case "busy":
h.busy = sentinel
case "apply":
h.applyErr = sentinel
case "dial":
h.dialErr = sentinel
case "end":
h.call = &fakeCall{ctx: context.Background()}
case "restore":
h.restoreErr = sentinel
case "decode":
cap.err = sentinel
}
r, err := run(context.Background(), c, "18601010101", debug, h, func(context.Context, Config, string) (captureSession, error) {
if phase == "capture" {
return nil, sentinel
}
return cap, nil
})
if err == nil || r.Status == "connected" || h.calls > 1 {
t.Fatalf("failure disguised: %+v %v", r, err)
}
if (phase == "busy" || phase == "capture" || phase == "apply") && h.calls != 0 {
t.Fatal("preflight failure dialed")
}
if phase == "end" && h.restores != 0 {
t.Fatal("unknown live channel must retain its configuration")
}
if debug && phase == "decode" && cap.stops != 1 {
t.Fatal("capture must always be finalized")
}
if _, err = os.Stat(filepath.Join(r.Directory, "result.json")); err != nil {
t.Fatalf("failure detail missing: %v", err)
}
})
}
}
func TestRunDebugHasLinkedEvidenceAndCancellationDoesNotRedial(t *testing.T) {
c := fixtureConfig(t)
h := &fakeHost{}
cap := &fakeCapture{}
r, err := run(context.Background(), c, "18601010101", true, h, func(context.Context, Config, string) (captureSession, error) { return cap, nil })
if err != nil || r.Evidence == nil || cap.stops != 1 {
t.Fatalf("evidence missing: %+v %v", r, err)
}
ctx, cancel := context.WithCancel(context.Background())
cancel()
h = &fakeHost{}
_, err = run(ctx, c, "18601010101", false, h, nil)
if err == nil || h.calls != 0 {
t.Fatal("cancelled invocation must not dial")
}
}