package ari import ( "context" "net/http" "net/http/httptest" "strings" "testing" "ai-operator/internal/config" ) func TestBridgeActionsAndExternalMedia(t *testing.T) { var seen []string server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { seen = append(seen, r.Method+" "+r.URL.Path+"?"+r.URL.RawQuery) switch { case r.Method == http.MethodPost && r.URL.Path == "/bridges": w.WriteHeader(http.StatusCreated) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/addChannel"): w.WriteHeader(http.StatusNoContent) case r.Method == http.MethodPost && strings.HasSuffix(r.URL.Path, "/play"): w.WriteHeader(http.StatusCreated) case r.Method == http.MethodPost && strings.Contains(r.URL.Path, "/removeChannel"): w.WriteHeader(http.StatusNotFound) case r.Method == http.MethodDelete && strings.HasPrefix(r.URL.Path, "/bridges/"): w.WriteHeader(http.StatusNotFound) case r.Method == http.MethodPost && r.URL.Path == "/channels/externalMedia": w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"id":"aiop-media-1"}`)) case r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/variable"): w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"value":"conn-1"}`)) default: t.Fatalf("unexpected request %s %s", r.Method, r.URL.String()) } })) defer server.Close() c := NewClient(config.AsteriskConfig{ARIURL: server.URL, ARIUser: "u", ARIPassword: "p"}) ctx := context.Background() if err := c.CreateBridge(ctx, "b1", "mixing,dtmf_events"); err != nil { t.Fatal(err) } if err := c.AddChannelToBridge(ctx, "b1", "c1"); err != nil { t.Fatal(err) } if err := c.PlayChannel(ctx, "c1", "sound:hello-world"); err != nil { t.Fatal(err) } if err := c.RemoveChannelFromBridge(ctx, "b1", "c1"); err != nil { t.Fatal(err) } if err := c.DeleteBridge(ctx, "b1"); err != nil { t.Fatal(err) } ch, err := c.CreateExternalMediaChannel(ctx, ExternalMediaRequest{ChannelID: "aiop-media-1", App: "ai-operator", ExternalHost: "INCOMING", Encapsulation: "none", Transport: "websocket", ConnectionType: "server", Format: "slin16", Direction: "both", Data: "call-1"}) if err != nil || ch.ID != "aiop-media-1" { t.Fatalf("external=%+v err=%v", ch, err) } v, err := c.GetChannelVariable(ctx, "aiop-media-1", "MEDIA_WEBSOCKET_CONNECTION_ID") if err != nil || v != "conn-1" { t.Fatalf("var=%q err=%v", v, err) } joined := strings.Join(seen, "\n") for _, want := range []string{"transport=websocket", "encapsulation=none", "external_host=INCOMING", "connection_type=server", "format=slin16", "direction=both", "channelId=aiop-media-1", "media=sound%3Ahello-world"} { if !strings.Contains(joined, want) { t.Fatalf("missing %s in %s", want, joined) } } } func TestGetChannelVariableErrors(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"value":""}`)) })) defer server.Close() c := NewClient(config.AsteriskConfig{ARIURL: server.URL, ARIUser: "u", ARIPassword: "secret"}) _, err := c.GetChannelVariable(context.Background(), "c1", "MISSING") if err == nil || strings.Contains(err.Error(), "secret") { t.Fatalf("err=%v", err) } }