Files
go-sip/deploys/test/saas-mock/hangup_config_test.go
T

127 lines
4.0 KiB
Go

package main
import (
"context"
"encoding/json"
"errors"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"time"
"git.ipao.vip/rogee/go-sip/internal/ai"
"git.ipao.vip/rogee/go-sip/internal/configread"
"git.ipao.vip/rogee/go-sip/internal/contract"
)
func TestSaaSMockServesApprovedHangupGroupsOverHTTP(t *testing.T) {
data, err := loadDataset(testDataDir(t), testDispatcher)
if err != nil {
t.Fatal(err)
}
server := httptest.NewTLSServer(data.handler("test-only-secret"))
defer server.Close()
client, err := configread.NewClient(server.URL, testDispatcher, "test-only-secret", server.Client())
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
sip, err := client.ReadSIP(ctx)
if err != nil {
t.Fatal(err)
}
providers, err := client.ReadProviders(ctx)
if err != nil {
t.Fatal(err)
}
snapshot, err := client.ReadTask(ctx, "task-full", 1001, sip, providers)
if err != nil {
t.Fatal(err)
}
bound, err := ai.Bind(snapshot.Task, snapshot.Providers)
if err != nil {
t.Fatal(err)
}
if len(bound.HangupKeywords) != 1 || bound.HangupKeywords[0].Name != "结束通话" || len(bound.HangupKeywords[0].Triggers) != 1 || bound.HangupKeywords[0].Triggers[0] != "不用了" || bound.HangupKeywords[0].ClosingRemark != "好的,祝您生活愉快。" {
t.Fatalf("HTTP task snapshot lost the approved hangup group: %+v", bound.HangupKeywords)
}
}
// Both unit and live-broker checks use this to enforce the HTTP/MQ boundary.
func verifyExecutePayload(body []byte, taskID, callee string) error {
var message struct {
Payload map[string]any `json:"payload"`
}
if err := json.Unmarshal(body, &message); err != nil {
return err
}
if len(message.Payload) != 2 || message.Payload["task_id"] != taskID || message.Payload["callee"] != callee {
return errors.New("Mock MQ command must contain only task_id and the original callee")
}
return nil
}
func TestSaaSMockMQRejectsInlineHangupConfiguration(t *testing.T) {
data, err := loadDataset(testDataDir(t), testDispatcher)
if err != nil {
t.Fatal(err)
}
_, body, err := buildExecute(data, "event-config-boundary", "task-full", "13900000000", time.Date(2026, 10, 4, 2, 0, 0, 0, time.UTC))
if err != nil {
t.Fatal(err)
}
if err := verifyExecutePayload(body, "task-full", "13900000000"); err != nil {
t.Fatal(err)
}
for _, field := range []string{"agent", "hangup_keywords", "closingRemark"} {
t.Run(field, func(t *testing.T) {
var message map[string]any
if err := json.Unmarshal(body, &message); err != nil {
t.Fatal(err)
}
message["payload"].(map[string]any)[field] = map[string]any{"name": "挂机1", "triggers": []string{"再见"}, "closingRemark": "好的,再见。"}
bad, err := json.Marshal(message)
if err != nil {
t.Fatal(err)
}
if err := contract.ValidateCurrent("mq", bad); err == nil {
t.Fatal("MQ accepted inline configuration instead of the task snapshot")
}
})
}
}
func TestSaaSMockRejectsLegacyAndIncompleteHangupSnapshots(t *testing.T) {
for name, groups := range map[string]any{
"legacy-strings": []string{"不用了"},
"missing-closing": []any{map[string]any{"name": "挂机1", "triggers": []string{"不用了"}}},
"empty-triggers": []any{map[string]any{"name": "挂机1", "triggers": []string{}, "closingRemark": "好的,再见。"}},
"blank-closing": []any{map[string]any{"name": "挂机1", "triggers": []string{"不用了"}, "closingRemark": " "}},
} {
t.Run(name, func(t *testing.T) {
root := testDataDir(t)
path := filepath.Join(root, "tasks", "task-full.json")
body, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var task map[string]any
if err := json.Unmarshal(body, &task); err != nil {
t.Fatal(err)
}
task["agent"].(map[string]any)["conversation"].(map[string]any)["hangup_keywords"] = groups
body, err = json.Marshal(task)
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(path, body, 0600); err != nil {
t.Fatal(err)
}
if _, err := loadDataset(root, testDispatcher); err == nil {
t.Fatal("removed or incomplete hangup structure was accepted")
}
})
}
}