110 lines
3.8 KiB
Go
110 lines
3.8 KiB
Go
package ai
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os/exec"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"git.ipao.vip/rogee/go-sip/internal/media"
|
|
)
|
|
|
|
func mockBailianTTS(t *testing.T, pcm []byte, observe func(string)) (string, *atomic.Int32) {
|
|
t.Helper()
|
|
wav, _, err := media.EncodeMonoWAV(pcm, 1024)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
calls := &atomic.Int32{}
|
|
var server *httptest.Server
|
|
server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/audio" && r.Method == http.MethodGet {
|
|
_, _ = w.Write(wav)
|
|
return
|
|
}
|
|
if r.URL.Path != "/api/v1/services/aigc/multimodal-generation/generation" || r.Method != http.MethodPost {
|
|
http.Error(w, "unexpected endpoint", http.StatusNotFound)
|
|
return
|
|
}
|
|
calls.Add(1)
|
|
var request struct {
|
|
Input struct {
|
|
Text string `json:"text"`
|
|
} `json:"input"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
|
http.Error(w, "invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
if observe != nil {
|
|
observe(request.Input.Text)
|
|
}
|
|
w.Header().Set("Content-Type", "application/json")
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"output": map[string]any{"audio": map[string]any{"url": server.URL + "/audio?sig=fixture"}}})
|
|
}))
|
|
t.Cleanup(server.Close)
|
|
return server.URL + "/api/v1/services/aigc/multimodal-generation/generation", calls
|
|
}
|
|
|
|
func TestBailianTTSDownloadFailureDoesNotLeakSignedURLOrRetry(t *testing.T) {
|
|
if _, err := exec.LookPath("ffmpeg"); err != nil {
|
|
t.Skip("Bailian TTS conversion requires ffmpeg")
|
|
}
|
|
var calls, downloads atomic.Int32
|
|
var server *httptest.Server
|
|
server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/audio" {
|
|
downloads.Add(1)
|
|
http.Error(w, "private-data", http.StatusForbidden)
|
|
return
|
|
}
|
|
calls.Add(1)
|
|
_ = json.NewEncoder(w).Encode(map[string]any{"output": map[string]any{"audio": map[string]any{"url": server.URL + "/audio?sig=private-signature"}}})
|
|
}))
|
|
defer server.Close()
|
|
task, providers := currentFixture(t, "full_ai")
|
|
p := providers["tts-example"]
|
|
p.Endpoint = server.URL + "/api/v1/services/aigc/multimodal-generation/generation"
|
|
providers[p.ProviderRef] = p
|
|
bound, err := Bind(task, providers)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = bound.Synthesize(context.Background(), "批准的回复")
|
|
if err == nil || !strings.Contains(err.Error(), "HTTP 403") || strings.Contains(err.Error(), "private-signature") || strings.Contains(err.Error(), "private-data") || strings.Contains(err.Error(), p.Credential) || calls.Load() != 1 || downloads.Load() != 1 {
|
|
t.Fatalf("download failure must be explicit without leaking/retrying: calls=%d downloads=%d err=%v", calls.Load(), downloads.Load(), err)
|
|
}
|
|
}
|
|
|
|
func TestBailianTTSDoesNotFollowChargeableRedirect(t *testing.T) {
|
|
if _, err := exec.LookPath("ffmpeg"); err != nil {
|
|
t.Skip("Bailian TTS conversion requires ffmpeg")
|
|
}
|
|
var calls, redirected atomic.Int32
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/charged-again" {
|
|
redirected.Add(1)
|
|
return
|
|
}
|
|
calls.Add(1)
|
|
http.Redirect(w, r, "/charged-again", http.StatusTemporaryRedirect)
|
|
}))
|
|
defer server.Close()
|
|
task, providers := currentFixture(t, "full_ai")
|
|
p := providers["tts-example"]
|
|
p.Endpoint = server.URL + "/api/v1/services/aigc/multimodal-generation/generation"
|
|
providers[p.ProviderRef] = p
|
|
bound, err := Bind(task, providers)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err = bound.Synthesize(context.Background(), "批准的回复")
|
|
if err == nil || !strings.Contains(err.Error(), "HTTP 307") || calls.Load() != 1 || redirected.Load() != 0 {
|
|
t.Fatalf("TTS redirect cannot create a second provider request: calls=%d redirected=%d err=%v", calls.Load(), redirected.Load(), err)
|
|
}
|
|
}
|