grok_audio_test.go 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package service
  2. import (
  3. "context"
  4. "io"
  5. "sync/atomic"
  6. "testing"
  7. "github.com/Wei-Shaw/sub2api/internal/pkg/xai"
  8. "github.com/stretchr/testify/require"
  9. )
  10. func TestBuildGrokVoiceURL_UsesAPIDefaultForCLIProxyBase(t *testing.T) {
  11. account := &Account{
  12. Platform: PlatformGrok,
  13. Type: AccountTypeOAuth,
  14. Credentials: map[string]any{
  15. "base_url": xai.DefaultCLIBaseURL,
  16. },
  17. }
  18. url, err := buildGrokVoiceURL(account, nil, "tts")
  19. require.NoError(t, err)
  20. require.Equal(t, xai.DefaultBaseURL+"/tts", url)
  21. url, err = buildGrokVoiceURL(account, nil, "realtime")
  22. require.NoError(t, err)
  23. require.Equal(t, xai.DefaultBaseURL+"/realtime", url)
  24. }
  25. func TestBuildGrokVoiceURL_EmptyBaseFallsBackToAPI(t *testing.T) {
  26. account := &Account{
  27. Platform: PlatformGrok,
  28. Type: AccountTypeOAuth,
  29. Credentials: map[string]any{},
  30. }
  31. url, err := buildGrokVoiceURL(account, nil, "stt")
  32. require.NoError(t, err)
  33. require.Equal(t, xai.DefaultBaseURL+"/stt", url)
  34. }
  35. func TestBuildGrokVoiceURL_RequiresEndpoint(t *testing.T) {
  36. account := &Account{Platform: PlatformGrok, Type: AccountTypeOAuth}
  37. _, err := buildGrokVoiceURL(account, nil, " ")
  38. require.Error(t, err)
  39. }
  40. func TestBuildGrokVoiceURL_EncodesCustomVoicePathSegments(t *testing.T) {
  41. account := &Account{Platform: PlatformGrok, Type: AccountTypeOAuth}
  42. got, err := buildGrokVoiceURL(account, nil, "custom-voices/nlbqfwie/audio")
  43. require.NoError(t, err)
  44. require.Equal(t, xai.DefaultBaseURL+"/custom-voices/nlbqfwie/audio", got)
  45. _, err = buildGrokVoiceURL(account, nil, "custom-voices/../audio")
  46. require.Error(t, err)
  47. }
  48. func TestForwardGrokVoice_RejectsNonGrok(t *testing.T) {
  49. svc := &OpenAIGatewayService{}
  50. _, err := svc.ForwardGrokVoice(context.Background(), nil, &Account{Platform: PlatformOpenAI}, "tts", []byte(`{}`), "application/json")
  51. require.Error(t, err)
  52. require.Contains(t, err.Error(), "not supported")
  53. }
  54. func TestAwaitGrokRealtimeAudioObservedReadsFlagAfterRelayExits(t *testing.T) {
  55. errCh := make(chan error, 1)
  56. var observed atomic.Bool
  57. go func() {
  58. observed.Store(true)
  59. errCh <- io.EOF
  60. }()
  61. got, err := awaitGrokRealtimeAudioObserved(errCh, &observed)
  62. require.ErrorIs(t, err, io.EOF)
  63. require.True(t, got, "audioObserved must be read after the relay returns, not before <-errCh")
  64. }
  65. func TestGrokRealtimeEventHasAudio(t *testing.T) {
  66. require.False(t, grokRealtimeEventHasAudio([]byte(`{"type":"session.created"}`)))
  67. require.False(t, grokRealtimeEventHasAudio([]byte(`{"type":"response.audio_transcript.delta","delta":"hi"}`)))
  68. require.False(t, grokRealtimeEventHasAudio([]byte(`{"type":"response.audio.delta","delta":""}`)))
  69. require.True(t, grokRealtimeEventHasAudio([]byte(`{"type":"response.audio.delta","delta":"abc"}`)))
  70. require.True(t, grokRealtimeEventHasAudio([]byte(`{"type":"response.output_audio.delta","audio":"abc"}`)))
  71. }
  72. func TestForwardGrokVoice_RejectsUnknownEndpoint(t *testing.T) {
  73. svc := &OpenAIGatewayService{}
  74. _, err := svc.ForwardGrokVoice(context.Background(), nil, &Account{Platform: PlatformGrok}, "unknown", []byte(`{}`), "application/json")
  75. require.Error(t, err)
  76. require.Contains(t, err.Error(), "unsupported")
  77. }