Files
goclaw/internal/http/tts_update_manager_test.go
viettranx 860bfff38f chore: minor formatting and cleanup across config, audio, cache, http, and provider modules
- 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
2026-04-23 19:16:12 +07:00

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())
}
}