mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-11-01 16:29:29 +00:00
- openai_ws_forwarder_ingress: resolve billing tier from the local upstreamResponseModelObserver (upstream echo first) instead of the raw request payload, matching the HTTP->WS bridge and WS v2 forwarder. - openai_ws_http_bridge_test: add fast-alias + upstream default case proving the local observer's echoed tier wins. - handler tests: keep only invalid service_tier -> 400 (short-circuits at validation); valid/omitted semantics covered by the pure service-level validation tests, avoiding real account selection in tests.
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)与省略/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)
|
||
}
|
||
}
|