144 lines
4.3 KiB
Go
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")
|
|
}
|
|
}
|