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

155 lines
4.7 KiB
Go

package sipcall
import (
"bytes"
"context"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
)
func configCLI(c Config, basePath string) cliFunc {
return func(_ context.Context, command string) ([]byte, error) {
switch command {
case "core show channels count":
return []byte("0 active channels\n0 active calls\n"), nil
case "pjsip show transports":
return []byte("Transport: go-sip-udp udp 0 0 0.0.0.0:5060\n"), nil
case "module reload res_pjsip.so":
return []byte("Module 'res_pjsip.so' reloaded successfully.\n"), nil
case "pjsip show endpoint " + c.Endpoint():
data, err := os.ReadFile(basePath)
if err != nil {
return nil, err
}
if !bytes.Contains(data, []byte(testInclude)) {
return []byte("Unable to find object " + c.Endpoint()), nil
}
return []byte(fmt.Sprintf("Endpoint: %s\nAor: %s-aor\nallow : (alaw)\ntransport : go-sip-udp\ncontext : go-sip-no-inbound\nfrom_user : %s\ncallerid : \"%s\" <%s>\n", c.Endpoint(), c.Endpoint(), c.CallerID, c.CallerID, c.CallerID)), nil
case "pjsip show aor " + c.Endpoint() + "-aor":
return []byte(fmt.Sprintf("Contact: sip:%s:%d", c.Server, c.Port)), nil
default:
return nil, fmt.Errorf("unexpected CLI: %s", command)
}
}
}
func stageFixture(t *testing.T) (Config, string, []byte) {
t.Helper()
c := fixtureConfig(t)
c.ConfigDir = t.TempDir()
path := filepath.Join(c.ConfigDir, "pjsip.conf")
base := []byte("; original bytes\n[go-sip-udp]\ntype=transport\nprotocol=udp\nbind=0.0.0.0:5060\n")
if err := os.WriteFile(path, base, 0600); err != nil {
t.Fatal(err)
}
return c, path, base
}
func TestTemporaryConfigurationPreservesBusinessBytes(t *testing.T) {
c, path, base := stageFixture(t)
restore, err := applyConfiguration(context.Background(), c, configCLI(c, path))
if err != nil {
t.Fatal(err)
}
managed := filepath.Join(c.ConfigDir, "sip-call-managed.conf")
data, err := os.ReadFile(managed)
if err != nil || !bytes.Contains(data, []byte("from_user=BD1234")) {
t.Fatal("requested identity not configured")
}
if err = restore(); err != nil {
t.Fatal(err)
}
data, err = os.ReadFile(path)
if err != nil || !bytes.Equal(data, base) {
t.Fatal("business config bytes changed")
}
if _, err = os.Stat(managed); !os.IsNotExist(err) {
t.Fatal("temporary file not removed")
}
}
func TestConfigurationRefusesOldAndConcurrentState(t *testing.T) {
c, path, _ := stageFixture(t)
managed := filepath.Join(c.ConfigDir, "sip-call-managed.conf")
if err := os.WriteFile(managed, []byte("unfinished"), 0600); err != nil {
t.Fatal(err)
}
if _, err := applyConfiguration(context.Background(), c, configCLI(c, path)); err == nil {
t.Fatal("old test state overwritten")
}
if err := os.Remove(managed); err != nil {
t.Fatal(err)
}
restore, err := applyConfiguration(context.Background(), c, configCLI(c, path))
if err != nil {
t.Fatal(err)
}
concurrent := []byte("concurrent config edit")
if err = os.WriteFile(path, concurrent, 0600); err != nil {
t.Fatal(err)
}
if err = restore(); err == nil {
t.Fatal("concurrent changes swallowed")
}
data, _ := os.ReadFile(path)
if !bytes.Equal(data, concurrent) {
t.Fatal("concurrent bytes overwritten")
}
}
func TestConfigurationFailsClosedOnRuntimeMismatch(t *testing.T) {
for _, failure := range []string{"transport", "caller", "reload", "aor", "busy"} {
t.Run(failure, func(t *testing.T) {
c, path, base := stageFixture(t)
normal := configCLI(c, path)
cli := func(ctx context.Context, s string) ([]byte, error) {
out, err := normal(ctx, s)
if err != nil {
return nil, err
}
if failure == "transport" && s == "pjsip show transports" {
return []byte("unknown"), nil
}
if failure == "caller" && strings.HasPrefix(s, "pjsip show endpoint") {
return bytes.ReplaceAll(out, []byte("BD1234"), []byte("WRONG")), nil
}
if failure == "reload" && s == "module reload res_pjsip.so" {
return nil, errors.New("reload failed")
}
if failure == "aor" && strings.HasPrefix(s, "pjsip show aor") {
return []byte("sip:192.0.2.99:5060"), nil
}
if failure == "busy" && s == "core show channels count" {
return []byte("1 active channel"), nil
}
return out, nil
}
restore, err := applyConfiguration(context.Background(), c, cli)
if failure == "busy" {
if err != nil {
t.Fatal(err)
}
if err = restore(); err == nil {
t.Fatal("restoration ignored active channel")
}
return
}
if err == nil {
t.Fatal("runtime mismatch accepted")
}
if restore != nil {
if err = restore(); err != nil && failure != "reload" {
t.Fatal(err)
}
}
data, _ := os.ReadFile(path)
if !bytes.Equal(data, base) {
t.Fatal("failed apply changed original config")
}
})
}
}