mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-14 12:02:27 +08:00
Accept service_tier=ultrafast on the OpenAI/Codex gateway, advertise it on GPT-5.6 Sol in the Codex model catalog, and include it in ChatGPT OAuth routing hints so Codex clients can select Ultrafast.
95 lines
3.8 KiB
Go
95 lines
3.8 KiB
Go
package handler
|
||
|
||
import (
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/Wei-Shaw/sub2api/internal/config"
|
||
middleware2 "github.com/Wei-Shaw/sub2api/internal/server/middleware"
|
||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
// 非法 service_tier 必须在两个 OpenAI 端点(/v1/responses、/v1/chat/completions)
|
||
// 上以 OpenAI 兼容错误结构返回 HTTP 400。这些用例在 handler 的 service_tier
|
||
// 校验处短路,不会进入账号选择/重试。
|
||
//
|
||
// 合法值(fast/priority/flex/auto/default/scale/ultrafast)与省略/null 的接受语义由
|
||
// service 层纯校验函数 TestValidateOpenAIServiceTierField 覆盖,避免 handler
|
||
// 测试走入真实账号选择/重试路径。
|
||
|
||
func newServiceTierHandlerTest(t *testing.T) *OpenAIGatewayHandler {
|
||
t.Helper()
|
||
return &OpenAIGatewayHandler{
|
||
gatewayService: &service.OpenAIGatewayService{},
|
||
billingCacheService: service.NewBillingCacheService(nil, nil, nil, nil, nil, nil, &config.Config{RunMode: config.RunModeSimple}, nil),
|
||
apiKeyService: &service.APIKeyService{},
|
||
concurrencyHelper: &ConcurrencyHelper{concurrencyService: service.NewConcurrencyService(
|
||
&helperConcurrencyCacheStub{userSeq: []bool{true}},
|
||
)},
|
||
cfg: &config.Config{},
|
||
imageLimiter: &imageConcurrencyLimiter{},
|
||
}
|
||
}
|
||
|
||
func runOpenAIHandlerServiceTierTest(t *testing.T, path, body string, handler func(h *OpenAIGatewayHandler, c *gin.Context)) *httptest.ResponseRecorder {
|
||
t.Helper()
|
||
gin.SetMode(gin.TestMode)
|
||
rec := httptest.NewRecorder()
|
||
c, _ := gin.CreateTestContext(rec)
|
||
c.Request = httptest.NewRequest(http.MethodPost, path, strings.NewReader(body))
|
||
c.Request.Header.Set("Content-Type", "application/json")
|
||
|
||
groupID := int64(6401)
|
||
userID := int64(6402)
|
||
c.Set(string(middleware2.ContextKeyAPIKey), &service.APIKey{
|
||
ID: 6403,
|
||
GroupID: &groupID,
|
||
Group: &service.Group{
|
||
ID: groupID,
|
||
Platform: service.PlatformOpenAI,
|
||
},
|
||
User: &service.User{ID: userID, Status: service.StatusActive},
|
||
})
|
||
c.Set(string(middleware2.ContextKeyUser), middleware2.AuthSubject{UserID: userID, Concurrency: 1})
|
||
|
||
handler(newServiceTierHandlerTest(t), c)
|
||
return rec
|
||
}
|
||
|
||
func TestOpenAIGatewayHandlerResponses_InvalidServiceTierRejected400(t *testing.T) {
|
||
for _, body := range []string{
|
||
`{"model":"gpt-5.5","input":"hi","service_tier":"turbo"}`,
|
||
`{"model":"gpt-5.5","input":"hi","service_tier":"SPEED"}`,
|
||
`{"model":"gpt-5.5","input":"hi","service_tier":""}`,
|
||
`{"model":"gpt-5.5","input":"hi","service_tier":123}`,
|
||
`{"model":"gpt-5.5","input":"hi","service_tier":{}}`,
|
||
} {
|
||
rec := runOpenAIHandlerServiceTierTest(t, "/v1/responses", body, func(h *OpenAIGatewayHandler, c *gin.Context) {
|
||
h.Responses(c)
|
||
})
|
||
require.Equal(t, http.StatusBadRequest, rec.Code, "body=%s", body)
|
||
require.Contains(t, rec.Body.String(), "invalid_request_error", "body=%s", body)
|
||
require.Contains(t, rec.Body.String(), "invalid service_tier", "body=%s", body)
|
||
}
|
||
}
|
||
|
||
func TestOpenAIGatewayHandlerChatCompletions_InvalidServiceTierRejected400(t *testing.T) {
|
||
for _, body := range []string{
|
||
`{"model":"gpt-5.5","messages":[{"role":"user","content":"hi"}],"service_tier":"turbo"}`,
|
||
`{"model":"gpt-5.5","messages":[{"role":"user","content":"hi"}],"service_tier":"ultra"}`,
|
||
`{"model":"gpt-5.5","messages":[{"role":"user","content":"hi"}],"service_tier":""}`,
|
||
`{"model":"gpt-5.5","messages":[{"role":"user","content":"hi"}],"service_tier":["priority"]}`,
|
||
} {
|
||
rec := runOpenAIHandlerServiceTierTest(t, "/v1/chat/completions", body, func(h *OpenAIGatewayHandler, c *gin.Context) {
|
||
h.ChatCompletions(c)
|
||
})
|
||
require.Equal(t, http.StatusBadRequest, rec.Code, "body=%s", body)
|
||
require.Contains(t, rec.Body.String(), "invalid_request_error", "body=%s", body)
|
||
require.Contains(t, rec.Body.String(), "invalid service_tier", "body=%s", body)
|
||
}
|
||
}
|