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

144 lines
4.3 KiB
Go

package main
import (
"context"
"encoding/json"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func TestCommandIdentityDedupAndUnknown(t *testing.T) {
dir, id := fixture(t)
data, err := loadSnapshots(dir, id)
if err != nil {
t.Fatal(err)
}
svc := newService(data, "secret")
sends := 0
svc.publish = func(_ context.Context, route string, raw []byte) (string, error) {
sends++
if !strings.HasSuffix(route, ".task.1.in") {
t.Fatal(route)
}
var msg envelope
if err := json.Unmarshal(raw, &msg); err != nil {
t.Fatal(err)
}
if msg.EventID != "source/numberID:42" {
t.Fatal(msg.EventID)
}
return "unknown", errors.New("confirmation missing")
}
req := commandInput{EventID: "source/numberID:42", TaskID: "1", Callee: "123456"}
first, err := svc.send(context.Background(), "call.execute", req, time.Now())
if err != nil {
t.Fatal(err)
}
if first.Status != "unknown" {
t.Fatal(first)
}
second, err := svc.send(context.Background(), "call.execute", req, time.Now())
if err != nil || second.Status != "unknown" || sends != 1 {
t.Fatalf("duplicate sends=%d result=%+v err=%v", sends, second, err)
}
req.Callee = "999"
if _, err = svc.send(context.Background(), "call.execute", req, time.Now()); err == nil {
t.Fatal("accepted conflicting source identity")
}
req.EventID = ""
req.Callee = "123456"
if _, err = svc.send(context.Background(), "call.execute", req, time.Now()); err == nil {
t.Fatal("accepted empty source identity")
}
}
func TestFeedbackValidationAndSaveBeforeAck(t *testing.T) {
dir, id := fixture(t)
data, err := loadSnapshots(dir, id)
if err != nil {
t.Fatal(err)
}
svc := newService(data, "secret")
svc.publish = func(context.Context, string, []byte) (string, error) { return "confirmed", nil }
if _, err = svc.send(context.Background(), "call.execute", commandInput{EventID: "call-example", TaskID: "1", Callee: "15003164745"}, time.Now()); err != nil {
t.Fatal(err)
}
svc.feedbackDir = t.TempDir()
raw, err := os.ReadFile("../../contracts/schema/examples/mq-execute-ack.json")
if err != nil {
t.Fatal(err)
}
ack := false
if err = svc.receive(raw, func() error {
files, e := os.ReadDir(svc.feedbackDir)
if e != nil || len(files) != 1 {
t.Fatalf("ACK before save: %v %v", files, e)
}
ack = true
return nil
}); err != nil {
t.Fatal(err)
}
if !ack {
t.Fatal("no ACK")
}
records := svc.events("call-example")
if len(records) != 1 || records[0].Validation != "valid" {
t.Fatalf("%+v", records)
}
if err = svc.receive(raw, func() error { return nil }); err != nil {
t.Fatal(err)
}
if got := svc.events("call-example"); len(got) != 1 || got[0].Duplicates != 1 {
t.Fatalf("duplicates %+v", got)
}
result, err := os.ReadFile("../../contracts/schema/examples/mq-result-no-recording.json")
if err != nil {
t.Fatal(err)
}
var msg envelope
json.Unmarshal(result, &msg)
msg.Payload["task_id"] = "1"
msg.Payload["outcome"] = "answered" // Regression: keep every schema-valid outcome visible.
result, _ = json.Marshal(msg)
if err = svc.receive(result, func() error { return nil }); err != nil {
t.Fatal(err)
}
if records := svc.events("call-example"); len(records) != 2 || records[1].Outcome != "answered" {
t.Fatalf("receipt overwritten or valid outcome hidden: %+v", records)
}
msg.Payload["callee"] = "999"
result, _ = json.Marshal(msg)
if err = svc.receive(result, func() error { return nil }); err != nil {
t.Fatal(err)
}
got := svc.events("call-example")
if len(got) != 3 || got[2].Validation != "invalid" {
t.Fatalf("conflict=%+v", got)
}
bad := []byte(`{"event_id":"bad","token":"must-not-leak"}`)
if err = svc.receive(bad, func() error { return nil }); err != nil {
t.Fatal(err)
}
encoded, _ := json.Marshal(svc.events("bad"))
if strings.Contains(string(encoded), "must-not-leak") {
t.Fatal("raw body in query")
}
// A regular file instead of a directory forces saving to fail before ACK.
svc.feedbackDir = filepath.Join(svc.feedbackDir, "file")
os.WriteFile(svc.feedbackDir, []byte("x"), 0600)
ack = false
if err = svc.receive(raw, func() error { ack = true; return nil }); err == nil || ack {
t.Fatalf("save failure swallowed or ACKed: %v %v", err, ack)
}
fresh := newService(data, "secret")
fresh.feedbackDir = svc.feedbackDir
if len(fresh.events("call-example")) != 0 {
t.Fatal("restored history")
}
}