155 lines
4.7 KiB
Go
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")
|
|
}
|
|
})
|
|
}
|
|
}
|