mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-08-16 06:23:05 +00:00
- config: align struct tag formatting in TelemetryConfig for readability - audio tests: modernize test patterns - cache, http, hooks, providers: small style and cleanup passes - No functional changes
133 lines
4.2 KiB
Go
133 lines
4.2 KiB
Go
package http
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/audio"
|
|
)
|
|
|
|
// TestTTSHandler_UpdateManager_SwapsProvider verifies that UpdateManager changes
|
|
// the underlying provider.
|
|
func TestTTSHandler_UpdateManager_SwapsProvider(t *testing.T) {
|
|
setupTestToken(t, "") // dev mode
|
|
|
|
// Initial provider "mock-a"
|
|
providerA := &mockTTSProvider{name: "mock-a"}
|
|
mgrA := audio.NewManager(audio.ManagerConfig{Primary: "mock-a"})
|
|
mgrA.RegisterTTS(providerA)
|
|
|
|
h := NewTTSHandler(mgrA)
|
|
mux := http.NewServeMux()
|
|
h.RegisterRoutes(mux)
|
|
|
|
// First request uses mock-a
|
|
req1 := httptest.NewRequest("POST", "/v1/tts/synthesize",
|
|
ttsBody(t, map[string]string{"text": "hello"}))
|
|
req1.Header.Set("Content-Type", "application/json")
|
|
rr1 := httptest.NewRecorder()
|
|
mux.ServeHTTP(rr1, req1)
|
|
if rr1.Code != http.StatusOK {
|
|
t.Fatalf("first request: want 200, got %d: %s", rr1.Code, rr1.Body.String())
|
|
}
|
|
|
|
// Swap to provider "mock-b"
|
|
providerB := &mockTTSProvider{name: "mock-b"}
|
|
mgrB := audio.NewManager(audio.ManagerConfig{Primary: "mock-b"})
|
|
mgrB.RegisterTTS(providerB)
|
|
h.UpdateManager(mgrB)
|
|
|
|
// Second request should use mock-b
|
|
req2 := httptest.NewRequest("POST", "/v1/tts/synthesize",
|
|
ttsBody(t, map[string]string{"text": "hello", "provider": "mock-b"}))
|
|
req2.Header.Set("Content-Type", "application/json")
|
|
rr2 := httptest.NewRecorder()
|
|
mux.ServeHTTP(rr2, req2)
|
|
if rr2.Code != http.StatusOK {
|
|
t.Fatalf("second request: want 200, got %d: %s", rr2.Code, rr2.Body.String())
|
|
}
|
|
|
|
// Old provider should no longer be accessible after swap
|
|
req3 := httptest.NewRequest("POST", "/v1/tts/synthesize",
|
|
ttsBody(t, map[string]string{"text": "hello", "provider": "mock-a"}))
|
|
req3.Header.Set("Content-Type", "application/json")
|
|
rr3 := httptest.NewRecorder()
|
|
mux.ServeHTTP(rr3, req3)
|
|
if rr3.Code != http.StatusNotFound {
|
|
t.Errorf("third request (old provider): want 404, got %d", rr3.Code)
|
|
}
|
|
}
|
|
|
|
// TestTTSHandler_UpdateManager_ConcurrentSafe verifies no race condition
|
|
// when calling UpdateManager while requests are in flight.
|
|
// Note: Uses stateless mock to avoid false race detection in mock's capturedOpts.
|
|
func TestTTSHandler_UpdateManager_ConcurrentSafe(t *testing.T) {
|
|
setupTestToken(t, "") // dev mode
|
|
|
|
// Use stateless mock to avoid race on capturedOpts
|
|
mgr := audio.NewManager(audio.ManagerConfig{Primary: "mock"})
|
|
mgr.RegisterTTS(&mockTTSProvider{name: "mock", stateless: true})
|
|
|
|
h := NewTTSHandler(mgr)
|
|
mux := http.NewServeMux()
|
|
h.RegisterRoutes(mux)
|
|
|
|
var wg sync.WaitGroup
|
|
const numRequests = 10
|
|
|
|
// Spawn concurrent requests
|
|
for i := range numRequests {
|
|
wg.Add(1)
|
|
go func(idx int) {
|
|
defer wg.Done()
|
|
body := ttsBody(t, map[string]string{"text": "hello"})
|
|
req := httptest.NewRequest("POST", "/v1/tts/synthesize", body)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rr := httptest.NewRecorder()
|
|
mux.ServeHTTP(rr, req)
|
|
// Either 200 or 404 is acceptable during swap
|
|
}(i)
|
|
}
|
|
|
|
// Mid-way call UpdateManager with new manager
|
|
wg.Go(func() {
|
|
newMgr := audio.NewManager(audio.ManagerConfig{Primary: "mock-new"})
|
|
newMgr.RegisterTTS(&mockTTSProvider{name: "mock-new", stateless: true})
|
|
h.UpdateManager(newMgr)
|
|
})
|
|
|
|
wg.Wait()
|
|
// If we reach here without panic/race in handler code, test passes.
|
|
// Note: The handler's mutex protects manager access; mock races are test artifacts.
|
|
}
|
|
|
|
// TestTTSHandler_UpdateManager_NilManagerNoop verifies UpdateManager(nil)
|
|
// does not panic and keeps the old manager.
|
|
func TestTTSHandler_UpdateManager_NilManagerNoop(t *testing.T) {
|
|
setupTestToken(t, "") // dev mode
|
|
|
|
provider := &mockTTSProvider{name: "mock"}
|
|
mgr := audio.NewManager(audio.ManagerConfig{Primary: "mock"})
|
|
mgr.RegisterTTS(provider)
|
|
|
|
h := NewTTSHandler(mgr)
|
|
mux := http.NewServeMux()
|
|
h.RegisterRoutes(mux)
|
|
|
|
// Call with nil — should not panic
|
|
h.UpdateManager(nil)
|
|
|
|
// Request should still work with original manager
|
|
req := httptest.NewRequest("POST", "/v1/tts/synthesize",
|
|
ttsBody(t, map[string]string{"text": "hello"}))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rr := httptest.NewRecorder()
|
|
mux.ServeHTTP(rr, req)
|
|
|
|
if rr.Code != http.StatusOK {
|
|
t.Errorf("after nil update: want 200, got %d: %s", rr.Code, rr.Body.String())
|
|
}
|
|
}
|