package ari import ( "context" "net/http" "net/http/httptest" "strings" "testing" "ai-operator/internal/config" ) func TestActions(t *testing.T) { var methods []string server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { methods = append(methods, r.Method+" "+r.URL.Path) switch { case r.Method == http.MethodPost && strings.HasSuffix(r.URL.Path, "/answer"): w.WriteHeader(http.StatusNoContent) case r.Method == http.MethodDelete: w.WriteHeader(http.StatusNoContent) case r.Method == http.MethodGet: w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte(`{"id":"c1","state":"Up"}`)) } })) defer server.Close() client := NewClient(config.AsteriskConfig{ARIURL: server.URL, ARIUser: "u", ARIPassword: "p"}) if err := client.AnswerChannel(context.Background(), "c1"); err != nil { t.Fatal(err) } if err := client.HangupChannel(context.Background(), "c1"); err != nil { t.Fatal(err) } ch, err := client.GetChannel(context.Background(), "c1") if err != nil || ch.ID != "c1" { t.Fatalf("get %v %v", ch, err) } } func TestHangup404NonFatalAndBadStatus(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotFound) })) defer server.Close() client := NewClient(config.AsteriskConfig{ARIURL: server.URL, ARIUser: "u", ARIPassword: "secret"}) if err := client.HangupChannel(context.Background(), "gone"); err != nil { t.Fatal(err) } if err := client.AnswerChannel(context.Background(), "gone"); err == nil || strings.Contains(err.Error(), "secret") { t.Fatalf("bad err: %v", err) } } func TestHandoffActions(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) w.WriteHeader(http.StatusNoContent) })) defer server.Close() client := NewClient(config.AsteriskConfig{ARIURL: server.URL, ARIUser: "u", ARIPassword: "secret"}) if err := client.RedirectChannel(context.Background(), "c 1", "PJSIP/operator"); err != nil { t.Fatal(err) } if err := client.ContinueInDialplan(context.Background(), "c 1", "handoff", "100", 1); err != nil { t.Fatal(err) } got := strings.Join(seen, "\n") if !strings.Contains(got, "/channels/c 1/redirect?endpoint=PJSIP%2Foperator") { t.Fatalf("redirect not encoded: %s", got) } if !strings.Contains(got, "/channels/c 1/continue?context=handoff&extension=100&priority=1") { t.Fatalf("continue not encoded: %s", got) } } func TestHandoffActionErrorNoSecret(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusForbidden) })) defer server.Close() client := NewClient(config.AsteriskConfig{ARIURL: server.URL, ARIUser: "u", ARIPassword: "secret-password"}) err := client.RedirectChannel(context.Background(), "c1", "PJSIP/user:pass@example") if err == nil { t.Fatal("expected error") } if strings.Contains(err.Error(), "secret-password") { t.Fatalf("error leaked password: %v", err) } }