feat(sora): 强制Sora走curl_cffi sidecar并完善校验测试
This commit is contained in:
@@ -4,6 +4,7 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
@@ -639,3 +640,144 @@ func TestSoraDirectClient_PostVideoForWatermarkFree(t *testing.T) {
|
||||
require.Equal(t, "/backend-api/sentinel/req", upstream.calls[0].Path)
|
||||
require.Equal(t, "/backend/project_y/post", upstream.calls[1].Path)
|
||||
}
|
||||
|
||||
type soraClientFallbackUpstream struct {
|
||||
doWithTLSCalls int32
|
||||
respBody string
|
||||
respStatusCode int
|
||||
err error
|
||||
}
|
||||
|
||||
func (u *soraClientFallbackUpstream) Do(_ *http.Request, _ string, _ int64, _ int) (*http.Response, error) {
|
||||
return nil, errors.New("unexpected Do call")
|
||||
}
|
||||
|
||||
func (u *soraClientFallbackUpstream) DoWithTLS(_ *http.Request, _ string, _ int64, _ int, _ bool) (*http.Response, error) {
|
||||
atomic.AddInt32(&u.doWithTLSCalls, 1)
|
||||
if u.err != nil {
|
||||
return nil, u.err
|
||||
}
|
||||
statusCode := u.respStatusCode
|
||||
if statusCode <= 0 {
|
||||
statusCode = http.StatusOK
|
||||
}
|
||||
body := u.respBody
|
||||
if body == "" {
|
||||
body = `{"ok":true}`
|
||||
}
|
||||
return newSoraClientMockResponse(statusCode, body), nil
|
||||
}
|
||||
|
||||
func TestSoraDirectClient_DoHTTP_UsesCurlCFFISidecarWhenEnabled(t *testing.T) {
|
||||
var captured soraCurlCFFISidecarRequest
|
||||
sidecar := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
require.Equal(t, "/request", r.URL.Path)
|
||||
raw, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, json.Unmarshal(raw, &captured))
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"status_code": http.StatusOK,
|
||||
"headers": map[string]any{
|
||||
"Content-Type": "application/json",
|
||||
"X-Sidecar": []string{"yes"},
|
||||
},
|
||||
"body_base64": base64.StdEncoding.EncodeToString([]byte(`{"ok":true}`)),
|
||||
})
|
||||
}))
|
||||
defer sidecar.Close()
|
||||
|
||||
upstream := &soraClientFallbackUpstream{}
|
||||
cfg := &config.Config{
|
||||
Sora: config.SoraConfig{
|
||||
Client: config.SoraClientConfig{
|
||||
BaseURL: "https://sora.chatgpt.com/backend",
|
||||
CurlCFFISidecar: config.SoraCurlCFFISidecarConfig{
|
||||
Enabled: true,
|
||||
BaseURL: sidecar.URL,
|
||||
Impersonate: "chrome131",
|
||||
TimeoutSeconds: 15,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
client := NewSoraDirectClient(cfg, upstream, nil)
|
||||
req, err := http.NewRequest(http.MethodPost, "https://sora.chatgpt.com/backend/me", strings.NewReader("hello-sidecar"))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("User-Agent", "test-ua")
|
||||
|
||||
resp, err := client.doHTTP(req, "http://127.0.0.1:18080", &Account{ID: 1})
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.JSONEq(t, `{"ok":true}`, string(body))
|
||||
require.Equal(t, int32(0), atomic.LoadInt32(&upstream.doWithTLSCalls))
|
||||
require.Equal(t, "http://127.0.0.1:18080", captured.ProxyURL)
|
||||
require.Equal(t, "chrome131", captured.Impersonate)
|
||||
require.Equal(t, "https://sora.chatgpt.com/backend/me", captured.URL)
|
||||
decodedReqBody, err := base64.StdEncoding.DecodeString(captured.BodyBase64)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "hello-sidecar", string(decodedReqBody))
|
||||
}
|
||||
|
||||
func TestSoraDirectClient_DoHTTP_CurlCFFISidecarFailureReturnsError(t *testing.T) {
|
||||
sidecar := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusBadGateway)
|
||||
_, _ = w.Write([]byte(`{"error":"boom"}`))
|
||||
}))
|
||||
defer sidecar.Close()
|
||||
|
||||
upstream := &soraClientFallbackUpstream{respBody: `{"fallback":true}`}
|
||||
cfg := &config.Config{
|
||||
Sora: config.SoraConfig{
|
||||
Client: config.SoraClientConfig{
|
||||
BaseURL: "https://sora.chatgpt.com/backend",
|
||||
CurlCFFISidecar: config.SoraCurlCFFISidecarConfig{
|
||||
Enabled: true,
|
||||
BaseURL: sidecar.URL,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
client := NewSoraDirectClient(cfg, upstream, nil)
|
||||
req, err := http.NewRequest(http.MethodGet, "https://sora.chatgpt.com/backend/me", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = client.doHTTP(req, "", &Account{ID: 2})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "sora curl_cffi sidecar")
|
||||
require.Equal(t, int32(0), atomic.LoadInt32(&upstream.doWithTLSCalls))
|
||||
}
|
||||
|
||||
func TestSoraDirectClient_DoHTTP_CurlCFFISidecarDisabledUsesLegacyStack(t *testing.T) {
|
||||
upstream := &soraClientFallbackUpstream{respBody: `{"legacy":true}`}
|
||||
cfg := &config.Config{
|
||||
Sora: config.SoraConfig{
|
||||
Client: config.SoraClientConfig{
|
||||
BaseURL: "https://sora.chatgpt.com/backend",
|
||||
CurlCFFISidecar: config.SoraCurlCFFISidecarConfig{
|
||||
Enabled: false,
|
||||
BaseURL: "http://127.0.0.1:18080",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
client := NewSoraDirectClient(cfg, upstream, nil)
|
||||
req, err := http.NewRequest(http.MethodGet, "https://sora.chatgpt.com/backend/me", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := client.doHTTP(req, "", &Account{ID: 3})
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{"legacy":true}`, string(body))
|
||||
require.Equal(t, int32(1), atomic.LoadInt32(&upstream.doWithTLSCalls))
|
||||
}
|
||||
|
||||
func TestConvertSidecarHeaderValue_NilAndSlice(t *testing.T) {
|
||||
require.Nil(t, convertSidecarHeaderValue(nil))
|
||||
require.Equal(t, []string{"a", "b"}, convertSidecarHeaderValue([]any{"a", " ", "b"}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user