test: add capacity retry regressions
This commit is contained in:
parent
18790386a7
commit
ed7ef86347
@ -218,6 +218,12 @@ func TestIsOpenAITransientProcessingError(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
))
|
))
|
||||||
|
|
||||||
|
require.True(t, isOpenAITransientProcessingError(
|
||||||
|
http.StatusBadRequest,
|
||||||
|
"Selected model is at capacity. Please try a different model.",
|
||||||
|
[]byte(`{"error":{"message":"Selected model is at capacity. Please try a different model.","type":"invalid_request_error"}}`),
|
||||||
|
))
|
||||||
|
|
||||||
require.True(t, isOpenAITransientProcessingError(
|
require.True(t, isOpenAITransientProcessingError(
|
||||||
http.StatusBadRequest,
|
http.StatusBadRequest,
|
||||||
"",
|
"",
|
||||||
@ -332,3 +338,55 @@ func TestOpenAIGatewayService_Forward_TransientProcessingErrorTriggersFailover(t
|
|||||||
require.Contains(t, string(failoverErr.ResponseBody), "An error occurred while processing your request")
|
require.Contains(t, string(failoverErr.ResponseBody), "An error occurred while processing your request")
|
||||||
require.False(t, c.Writer.Written(), "service 层应返回 failover 错误给上层换号,而不是直接向客户端写响应")
|
require.False(t, c.Writer.Written(), "service 层应返回 failover 错误给上层换号,而不是直接向客户端写响应")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenAIGatewayService_Forward_ModelCapacityErrorTriggersFailoverAndSameAccountRetry(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodPost, "/v1/responses", bytes.NewReader(nil))
|
||||||
|
c.Request.Header.Set("User-Agent", "codex_cli_rs/0.1.0")
|
||||||
|
c.Request.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
upstream := &httpUpstreamRecorder{
|
||||||
|
resp: &http.Response{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Header: http.Header{
|
||||||
|
"Content-Type": []string{"application/json"},
|
||||||
|
"x-request-id": []string{"rid-capacity-400"},
|
||||||
|
},
|
||||||
|
Body: io.NopCloser(strings.NewReader(`{"error":{"message":"Selected model is at capacity. Please try a different model.","type":"invalid_request_error"}}`)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
svc := &OpenAIGatewayService{
|
||||||
|
cfg: &config.Config{
|
||||||
|
Gateway: config.GatewayConfig{ForceCodexCLI: false},
|
||||||
|
},
|
||||||
|
httpUpstream: upstream,
|
||||||
|
}
|
||||||
|
account := &Account{
|
||||||
|
ID: 1001,
|
||||||
|
Name: "codex max套餐",
|
||||||
|
Platform: PlatformOpenAI,
|
||||||
|
Type: AccountTypeAPIKey,
|
||||||
|
Concurrency: 1,
|
||||||
|
Credentials: map[string]any{
|
||||||
|
"api_key": "sk-test",
|
||||||
|
"pool_mode": true,
|
||||||
|
},
|
||||||
|
Status: StatusActive,
|
||||||
|
Schedulable: true,
|
||||||
|
RateMultiplier: f64p(1),
|
||||||
|
}
|
||||||
|
body := []byte(`{"model":"gpt-5.4","stream":false,"input":[{"type":"text","text":"hello"}]}`)
|
||||||
|
|
||||||
|
_, err := svc.Forward(context.Background(), c, account, body)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
var failoverErr *UpstreamFailoverError
|
||||||
|
require.ErrorAs(t, err, &failoverErr)
|
||||||
|
require.Equal(t, http.StatusBadRequest, failoverErr.StatusCode)
|
||||||
|
require.True(t, failoverErr.RetryableOnSameAccount)
|
||||||
|
require.Contains(t, string(failoverErr.ResponseBody), "Selected model is at capacity")
|
||||||
|
require.False(t, c.Writer.Written(), "service 层应返回 failover 错误给上层重试/换号,而不是直接向客户端写响应")
|
||||||
|
}
|
||||||
|
|||||||
@ -1116,6 +1116,47 @@ func TestOpenAIStreamingResponseFailedBeforeOutputReturnsFailover(t *testing.T)
|
|||||||
require.Empty(t, rec.Body.String())
|
require.Empty(t, rec.Body.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOpenAIStreamingResponseFailedBeforeOutputCapacityErrorReturnsFailover(t *testing.T) {
|
||||||
|
gin.SetMode(gin.TestMode)
|
||||||
|
cfg := &config.Config{
|
||||||
|
Gateway: config.GatewayConfig{
|
||||||
|
StreamDataIntervalTimeout: 0,
|
||||||
|
StreamKeepaliveInterval: 0,
|
||||||
|
MaxLineSize: defaultMaxLineSize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
svc := &OpenAIGatewayService{cfg: cfg}
|
||||||
|
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
c, _ := gin.CreateTestContext(rec)
|
||||||
|
c.Request = httptest.NewRequest(http.MethodPost, "/", nil)
|
||||||
|
|
||||||
|
resp := &http.Response{
|
||||||
|
StatusCode: http.StatusOK,
|
||||||
|
Body: io.NopCloser(strings.NewReader(strings.Join([]string{
|
||||||
|
"event: response.created",
|
||||||
|
`data: {"type":"response.created","response":{"id":"resp_1"}}`,
|
||||||
|
"",
|
||||||
|
"event: response.in_progress",
|
||||||
|
`data: {"type":"response.in_progress","response":{"id":"resp_1"}}`,
|
||||||
|
"",
|
||||||
|
"event: response.failed",
|
||||||
|
`data: {"type":"response.failed","error":{"message":"Selected model is at capacity. Please try a different model.","type":"invalid_request_error"}}`,
|
||||||
|
"",
|
||||||
|
}, "\n"))),
|
||||||
|
Header: http.Header{"X-Request-Id": []string{"rid-capacity-failed"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := svc.handleStreamingResponse(c.Request.Context(), resp, c, &Account{ID: 1, Platform: PlatformOpenAI, Name: "acc"}, time.Now(), "model", "model")
|
||||||
|
require.Error(t, err)
|
||||||
|
var failoverErr *UpstreamFailoverError
|
||||||
|
require.ErrorAs(t, err, &failoverErr)
|
||||||
|
require.Equal(t, http.StatusBadGateway, failoverErr.StatusCode)
|
||||||
|
require.Contains(t, string(failoverErr.ResponseBody), "Selected model is at capacity")
|
||||||
|
require.False(t, c.Writer.Written())
|
||||||
|
require.Empty(t, rec.Body.String())
|
||||||
|
}
|
||||||
|
|
||||||
func TestOpenAIStreamingPreambleOnlyMissingTerminalReturnsFailover(t *testing.T) {
|
func TestOpenAIStreamingPreambleOnlyMissingTerminalReturnsFailover(t *testing.T) {
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
cfg := &config.Config{
|
cfg := &config.Config{
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user