sync: migrate ai-operator to Gitea (2026-08-10)
This commit is contained in:
@@ -0,0 +1,173 @@
|
||||
package call
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"ai-operator/internal/asterisk/ari"
|
||||
)
|
||||
|
||||
type fakeActions struct{ answered, hungup, played []string }
|
||||
|
||||
func (f *fakeActions) AnswerChannel(ctx context.Context, channelID string) error {
|
||||
f.answered = append(f.answered, channelID)
|
||||
return nil
|
||||
}
|
||||
func (f *fakeActions) HangupChannel(ctx context.Context, channelID string) error {
|
||||
f.hungup = append(f.hungup, channelID)
|
||||
return nil
|
||||
}
|
||||
func (f *fakeActions) GetChannel(ctx context.Context, channelID string) (*ari.ARIChannel, error) {
|
||||
return &ari.ARIChannel{ID: channelID}, nil
|
||||
}
|
||||
func (f *fakeActions) PlayChannel(ctx context.Context, channelID string, media string) error {
|
||||
f.played = append(f.played, channelID+" "+media)
|
||||
return nil
|
||||
}
|
||||
|
||||
func startEvent(id string, args []string) ari.StasisStartEvent {
|
||||
return ari.StasisStartEvent{BaseEvent: ari.BaseEvent{Type: ari.EventStasisStart}, Args: args, Channel: ari.ARIChannel{ID: id, Caller: ari.ARICallerID{Number: "+77771234567"}}}
|
||||
}
|
||||
|
||||
func TestManagerObserveOnly(t *testing.T) {
|
||||
fake := &fakeActions{}
|
||||
store := NewSessionStore()
|
||||
m := NewManager(DefaultManagerConfig(ManagerModeObserveOnly, time.Millisecond), fake, store, nil)
|
||||
if err := m.HandleEvent(context.Background(), startEvent("c1", []string{"test"})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(fake.answered) != 0 || len(fake.hungup) != 0 {
|
||||
t.Fatal("observe-only controlled channel")
|
||||
}
|
||||
if store.Count() != 1 {
|
||||
t.Fatalf("count=%d", store.Count())
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerCallControlTestRoute(t *testing.T) {
|
||||
fake := &fakeActions{}
|
||||
m := NewManager(DefaultManagerConfig(ManagerModeCallControl, 5*time.Millisecond), fake, NewSessionStore(), nil)
|
||||
if err := m.HandleEvent(context.Background(), startEvent("c1", []string{"test"})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(fake.answered) != 1 {
|
||||
t.Fatalf("answered=%v", fake.answered)
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
if len(fake.hungup) != 1 {
|
||||
t.Fatalf("hungup=%v", fake.hungup)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerPlaybackDiagnostic(t *testing.T) {
|
||||
fake := &fakeActions{}
|
||||
cfg := DefaultManagerConfig(ManagerModeCallControl, 5*time.Millisecond)
|
||||
cfg.MediaTestMode = "playback"
|
||||
m := NewManager(cfg, fake, NewSessionStore(), nil)
|
||||
if err := m.HandleEvent(context.Background(), startEvent("c1", []string{"test"})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(fake.answered) != 1 {
|
||||
t.Fatalf("answered=%v", fake.answered)
|
||||
}
|
||||
if len(fake.played) != 1 || fake.played[0] != "c1 sound:hello-world" {
|
||||
t.Fatalf("played=%v", fake.played)
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerRejectsProductionAndCleans(t *testing.T) {
|
||||
fake := &fakeActions{}
|
||||
store := NewSessionStore()
|
||||
m := NewManager(DefaultManagerConfig(ManagerModeCallControl, time.Millisecond), fake, store, nil)
|
||||
if err := m.HandleEvent(context.Background(), startEvent("c1", []string{"production"})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(fake.answered) != 0 || len(fake.hungup) != 1 {
|
||||
t.Fatalf("answered=%v hungup=%v", fake.answered, fake.hungup)
|
||||
}
|
||||
_ = m.HandleEvent(context.Background(), ari.StasisEndEvent{BaseEvent: ari.BaseEvent{Type: ari.EventStasisEnd}, Channel: ari.ARIChannel{ID: "c1"}})
|
||||
if store.Count() != 0 {
|
||||
t.Fatalf("count=%d", store.Count())
|
||||
}
|
||||
_ = m.HandleEvent(context.Background(), startEvent("c2", []string{"test"}))
|
||||
_ = m.HandleEvent(context.Background(), ari.ChannelDestroyedEvent{BaseEvent: ari.BaseEvent{Type: ari.EventChannelDestroyed}, Channel: ari.ARIChannel{ID: "c2"}})
|
||||
if store.Count() != 0 {
|
||||
t.Fatalf("count=%d", store.Count())
|
||||
}
|
||||
}
|
||||
|
||||
type fakeMediaStarter struct{ calls int }
|
||||
|
||||
func (f *fakeMediaStarter) StartCallMedia(ctx context.Context, session *CallSession) (MediaClient, error) {
|
||||
f.calls++
|
||||
session.MediaConnected = true
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestManagerMediaIntegrationAndMediaChannelIgnored(t *testing.T) {
|
||||
fake := &fakeActions{}
|
||||
media := &fakeMediaStarter{}
|
||||
cfg := DefaultManagerConfig(ManagerModeCallControl, time.Millisecond)
|
||||
cfg.MediaEnabled = true
|
||||
cfg.MediaStarter = media
|
||||
m := NewManager(cfg, fake, NewSessionStore(), nil)
|
||||
if err := m.HandleEvent(context.Background(), startEvent("c1", []string{"test"})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if media.calls != 1 {
|
||||
t.Fatalf("media calls=%d", media.calls)
|
||||
}
|
||||
if err := m.HandleEvent(context.Background(), startEvent("aiop-media-c1", []string{"test"})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if m.Store().Count() != 1 {
|
||||
t.Fatalf("media channel treated as caller, count=%d", m.Store().Count())
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerDialogueIntegration(t *testing.T) {
|
||||
fake := &fakeActions{}
|
||||
started := false
|
||||
ended := false
|
||||
cfg := DefaultManagerConfig(ManagerModeCallControl, time.Millisecond)
|
||||
cfg.StartDialogue = func(ctx context.Context, session *CallSession) (string, error) {
|
||||
started = true
|
||||
if session.Route != "test" {
|
||||
t.Fatalf("route=%s", session.Route)
|
||||
}
|
||||
return "state prompt", nil
|
||||
}
|
||||
cfg.EndDialogue = func(ctx context.Context, callID string, reason string) error {
|
||||
ended = true
|
||||
return nil
|
||||
}
|
||||
m := NewManager(cfg, fake, NewSessionStore(), nil)
|
||||
if err := m.HandleEvent(context.Background(), startEvent("c1", []string{"test"})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !started {
|
||||
t.Fatal("dialogue not started")
|
||||
}
|
||||
_ = m.HandleEvent(context.Background(), ari.StasisEndEvent{BaseEvent: ari.BaseEvent{Type: ari.EventStasisEnd}, Channel: ari.ARIChannel{ID: "c1"}})
|
||||
if !ended {
|
||||
t.Fatal("dialogue not ended")
|
||||
}
|
||||
}
|
||||
|
||||
func TestManagerDoesNotStartDialogueForProduction(t *testing.T) {
|
||||
fake := &fakeActions{}
|
||||
started := false
|
||||
cfg := DefaultManagerConfig(ManagerModeCallControl, time.Millisecond)
|
||||
cfg.StartDialogue = func(ctx context.Context, session *CallSession) (string, error) {
|
||||
started = true
|
||||
return "", nil
|
||||
}
|
||||
m := NewManager(cfg, fake, NewSessionStore(), nil)
|
||||
if err := m.HandleEvent(context.Background(), startEvent("c1", []string{"production"})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if started {
|
||||
t.Fatal("dialogue started for production route")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user