Files
go-sip/internal/ai/keyword_hangup_test.go
T

125 lines
4.3 KiB
Go

package ai
import (
"context"
"errors"
"sync"
"testing"
)
func keywordGroups() []HangupKeyword {
return []HangupKeyword{
{Name: "挂机1", Triggers: []string{"不需要", "再见"}, ClosingRemark: "好的,祝您生活愉快。"},
{Name: "挂机2", Triggers: []string{"再见", "不要联系"}, ClosingRemark: "好的,再见。"},
}
}
func TestKeywordHangupOnlyFinalUserLiteralAndOrderedGroups(t *testing.T) {
calls := 0
groups := keywordGroups()
stopper, err := NewKeywordHangup(groups, func(context.Context) error { calls++; return nil })
if err != nil {
t.Fatal(err)
}
groups[0].Triggers[1], groups[0].ClosingRemark = "改过的词", "改过的结束语"
for _, segment := range []ASRSegment{
{Source: "user", Text: "再见", Final: false},
{Source: "assistant", Text: "再见", Final: true},
{Source: "user", Text: "继续", Final: true},
{Source: "user", Text: "不 需要", Final: true},
} {
if group, err := stopper.Match(segment); err != nil || group != nil || calls != 0 {
t.Fatalf("nonfinal/nonliteral/assistant text triggered closing: %v", err)
}
}
group, err := stopper.Match(ASRSegment{Source: "user", Text: "再见,不要联系", Final: true})
if err != nil || group == nil || group.Name != "挂机1" || group.ClosingRemark != "好的,祝您生活愉快。" || calls != 0 {
t.Fatalf("first configured match must reserve frozen closing without hanging up: group=%+v calls=%d err=%v", group, calls, err)
}
if _, err := stopper.Match(ASRSegment{Source: "user", Text: "再见", Final: true}); err == nil {
t.Fatal("duplicate result allowed closing replay")
}
if err := stopper.Finish(context.Background()); err != nil || calls != 1 {
t.Fatalf("finish: %v calls=%d", err, calls)
}
if err := stopper.Finish(context.Background()); err == nil || calls != 1 {
t.Fatal("hangup retried")
}
}
func TestKeywordHangupFailureVisibleWithoutRetry(t *testing.T) {
want := errors.New("transport outcome unknown")
calls := 0
stopper, err := NewKeywordHangup(keywordGroups(), func(context.Context) error { calls++; return want })
if err != nil {
t.Fatal(err)
}
if _, err := stopper.Match(ASRSegment{Source: "user", Text: "不需要", Final: true}); err != nil {
t.Fatal(err)
}
if err := stopper.Finish(context.Background()); !errors.Is(err, want) || calls != 1 {
t.Fatalf("failure hidden: %v", err)
}
if err := stopper.Finish(context.Background()); err == nil || calls != 1 {
t.Fatal("uncertain hangup retried")
}
}
func TestKeywordHangupConcurrentReservations(t *testing.T) {
stopper, err := NewKeywordHangup(keywordGroups(), func(context.Context) error { return nil })
if err != nil {
t.Fatal(err)
}
var wg sync.WaitGroup
matched := make(chan bool, 20)
for range 20 {
wg.Go(func() {
group, _ := stopper.Match(ASRSegment{Source: "user", Text: "不需要", Final: true})
matched <- group != nil
})
}
wg.Wait()
close(matched)
count := 0
for ok := range matched {
if ok {
count++
}
}
if count != 1 {
t.Fatalf("closing reserved %d times", count)
}
}
func TestKeywordHangupRejectsInvalidInput(t *testing.T) {
for _, groups := range [][]HangupKeyword{
{{Name: "", Triggers: []string{"词"}, ClosingRemark: "结束"}},
{{Name: "组", Triggers: []string{""}, ClosingRemark: "结束"}},
{{Name: "组", Triggers: []string{string([]byte{0xff})}, ClosingRemark: "结束"}},
{{Name: "组", Triggers: []string{"词"}, ClosingRemark: " "}},
} {
if _, err := NewKeywordHangup(groups, func(context.Context) error { return nil }); err == nil {
t.Fatal("invalid group accepted")
}
}
if _, err := NewKeywordHangup(nil, nil); err == nil {
t.Fatal("missing hangup accepted")
}
stopper, _ := NewKeywordHangup(keywordGroups(), func(context.Context) error { return nil })
for _, segment := range []ASRSegment{{Source: "unknown", Final: true}, {Source: "user", Text: string([]byte{0xff}), Final: true}} {
if _, err := stopper.Match(segment); err == nil {
t.Fatal("invalid ASR input accepted")
}
}
if err := stopper.Finish(context.Background()); err == nil {
t.Fatal("hangup without keyword reservation")
}
var unavailable *KeywordHangup
if _, err := unavailable.Match(ASRSegment{}); err == nil {
t.Fatal("nil matcher accepted")
}
if err := unavailable.Finish(context.Background()); err == nil {
t.Fatal("nil finish accepted")
}
}