package callruntime import ( "context" "errors" "strings" "testing" "time" "github.com/CyCoreSystems/ari/v5" ) type eventSubscription struct{ events chan ari.Event } func (s eventSubscription) Events() <-chan ari.Event { return s.events } func (s eventSubscription) Cancel() {} func events(values ...ari.Event) eventSubscription { s := eventSubscription{make(chan ari.Event, len(values))} for _, event := range values { s.events <- event } close(s.events) return s } func TestWaitForAnswerUsesOnlyTargetChannel(t *testing.T) { for _, tc := range []struct { name string event ari.Event wantError string }{ {name: "stasis", event: &ari.StasisStart{EventData: ari.EventData{Type: "StasisStart"}, Channel: ari.ChannelData{ID: "call"}}}, {name: "up", event: &ari.ChannelStateChange{EventData: ari.EventData{Type: "ChannelStateChange"}, Channel: ari.ChannelData{ID: "call", State: "Up"}}}, {name: "hangup", event: &ari.ChannelHangupRequest{EventData: ari.EventData{Type: "ChannelHangupRequest"}, Channel: ari.ChannelData{ID: "call", State: "Down"}, Cause: 17}, wantError: "cause=17"}, {name: "destroyed", event: &ari.ChannelDestroyed{EventData: ari.EventData{Type: "ChannelDestroyed"}, Channel: ari.ChannelData{ID: "call"}}, wantError: "ended before StasisStart"}, } { t.Run(tc.name, func(t *testing.T) { other := &ari.StasisStart{EventData: ari.EventData{Type: "StasisStart"}, Channel: ari.ChannelData{ID: "another-call"}} err := waitForStasisStart(context.Background(), events(nil, other, tc.event), "call", time.Second) if tc.wantError == "" && err != nil { t.Fatal(err) } if tc.wantError != "" && (err == nil || !strings.Contains(err.Error(), tc.wantError)) { t.Fatalf("unexpected answer result: %v", err) } }) } if err := waitForStasisStart(context.Background(), events(), "call", time.Second); err == nil || !strings.Contains(err.Error(), "subscription closed") { t.Fatal("closed subscription accepted") } ctx, cancel := context.WithCancel(context.Background()) cancel() if err := waitForStasisStart(ctx, eventSubscription{make(chan ari.Event)}, "call", time.Second); !errors.Is(err, context.Canceled) { t.Fatalf("cancellation lost: %v", err) } } func TestLifecycleCancellationIsScopedToChannel(t *testing.T) { for _, kind := range []string{"ChannelHangupRequest", "ChannelDestroyed"} { t.Run(kind, func(t *testing.T) { var event ari.Event if kind == "ChannelDestroyed" { event = &ari.ChannelDestroyed{EventData: ari.EventData{Type: kind}, Channel: ari.ChannelData{ID: "call"}} } else { event = &ari.ChannelHangupRequest{EventData: ari.EventData{Type: kind}, Channel: ari.ChannelData{ID: "call"}} } ctx, cancel := context.WithCancel(context.Background()) defer cancel() other := &ari.ChannelDestroyed{EventData: ari.EventData{Type: "ChannelDestroyed"}, Channel: ari.ChannelData{ID: "other"}} watchChannelLifecycle(ctx, events(other, event), "call", cancel) if !errors.Is(ctx.Err(), context.Canceled) { t.Fatal("matching hangup failed to cancel") } }) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() watchChannelLifecycle(ctx, events(&ari.ChannelDestroyed{EventData: ari.EventData{Type: "ChannelDestroyed"}, Channel: ari.ChannelData{ID: "other"}}), "call", cancel) if ctx.Err() != nil { t.Fatal("another call ended this call") } watchChannelLifecycle(ctx, events(nil), "call", cancel) cancel() watchChannelLifecycle(ctx, eventSubscription{make(chan ari.Event)}, "call", cancel) }