Files
go-sip/cmd/saas-server/operations_test.go
T

146 lines
3.8 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"os"
"path/filepath"
"sync"
"testing"
)
func request(t *testing.T, s *service, method, path string, body []byte) (int, []byte) {
t.Helper()
req, _ := http.NewRequest(method, "http://test"+path, bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
resp, err := s.app.Test(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
raw, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
return resp.StatusCode, raw
}
func TestMemoryUpdatesAndCommands(t *testing.T) {
dir, id := fixture(t)
data, err := loadSnapshots(dir, id)
if err != nil {
t.Fatal(err)
}
s := newService(data, "secret")
var published []envelope
s.publish = func(_ context.Context, _ string, raw []byte) (string, error) {
var msg envelope
json.Unmarshal(raw, &msg)
published = append(published, msg)
return "confirmed", nil
}
before, err := os.ReadFile(filepath.Join(dir, "tasks/1.json"))
if err != nil {
t.Fatal(err)
}
var task map[string]any
json.Unmarshal(before, &task)
task["task_revision"] = float64(123)
updated, _ := json.Marshal(task)
code, raw := request(t, s, "PUT", "/test/config/tasks/1", updated)
if code != 200 {
t.Fatalf("update: %d %s", code, raw)
}
if len(published) != 0 {
t.Fatal("configuration update sent automatic command")
}
s.mu.RLock()
got := s.data.tasks["1"]
s.mu.RUnlock()
if !bytes.Equal(got, updated) {
t.Fatal("update not in memory")
}
code, _ = request(t, s, "PUT", "/test/config/tasks/1", []byte(`{"invalid":true}`))
if code != 400 {
t.Fatal(code)
}
task["task_id"] = "2"
bad, _ := json.Marshal(task)
code, _ = request(t, s, "PUT", "/test/config/tasks/1", bad)
if code != 400 {
t.Fatal(code)
}
code, _ = request(t, s, "PUT", "/test/config/tasks/2", updated)
if code != 404 {
t.Fatal(code)
}
for _, action := range []string{"start", "resume", "edit"} {
code, raw = request(t, s, "POST", "/test/task-controls", []byte(`{"event_id":"ctrl-`+action+`","task_id":"1","action":"`+action+`","reason":"manual test"}`))
if code != 200 {
t.Fatalf("%s %d %s", action, code, raw)
}
}
code, _ = request(t, s, "POST", "/test/task-controls", []byte(`{"event_id":"ctrl-stop","task_id":"1","action":"stop"}`))
if code != 400 {
t.Fatal(code)
}
code, raw = request(t, s, "POST", "/test/sip-refresh", []byte(`{"event_id":"sip-test","trunk_id":"trunk-mock","change_type":"enabled","revision":8}`))
if code != 200 {
t.Fatalf("sip %d %s", code, raw)
}
code, raw = request(t, s, "POST", "/test/calls", []byte(`{"event_id":"call-http","task_id":"1","callee":"123456"}`))
if code != 200 {
t.Fatalf("call %d %s", code, raw)
}
code, raw = request(t, s, "GET", "/test/events?event_id=call-http", nil)
if code != 200 || !bytes.Contains(raw, []byte(`"confirmed"`)) {
t.Fatalf("query: %d %s", code, raw)
}
code, _ = request(t, s, "POST", "/test/calls", []byte(`{"event_id":"bad","task_id":"1","callee":"123","unknown":true}`))
if code != 400 {
t.Fatal(code)
}
after, _ := os.ReadFile(filepath.Join(dir, "tasks/1.json"))
if !bytes.Equal(before, after) {
t.Fatal("file changed")
}
reloaded, err := loadSnapshots(dir, id)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(reloaded.tasks["1"], before) {
t.Fatal("restart restored memory changes")
}
}
func TestConcurrentMemoryReadAndUpdate(t *testing.T) {
dir, id := fixture(t)
data, err := loadSnapshots(dir, id)
if err != nil {
t.Fatal(err)
}
s := newService(data, "secret")
var wg sync.WaitGroup
for i := 0; i < 8; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 10; j++ {
if err := s.replace("task", "1", data.tasks["1"]); err != nil {
t.Error(err)
}
s.mu.RLock()
_, err := s.data.page("")
s.mu.RUnlock()
if err != nil {
t.Error(err)
}
}
}()
}
wg.Wait()
}