fix: resolve upstream CI failures (lint, test, gofmt)
- Fix errcheck: handle Write/Encode return values in brave_test.go - Fix errcheck: defer resp.Body.Close() with _ assignment in tavily.go - Fix gofmt: payment.go, channel.go, payment_config_providers.go - Fix unused: remove dead decodeURLValue in easypay.go - Restore shouldFallbackGeminiModel function (deleted during cherry-pick) - Add missing balanceNotifyService param to NewGatewayService in test - Fix platform default test expectation (empty stays empty) - Fix wildcard pricing test (longest prefix wins, not config order) - Fix subscription group test (SUBSCRIPTION_REPOSITORY_UNAVAILABLE)
This commit is contained in:
@@ -273,13 +273,13 @@ func TestPricingRequestToService_Defaults(t *testing.T) {
|
|||||||
wantValue: string(service.BillingModeToken),
|
wantValue: string(service.BillingModeToken),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty platform defaults to anthropic",
|
name: "empty platform stays empty",
|
||||||
req: channelModelPricingRequest{
|
req: channelModelPricingRequest{
|
||||||
Models: []string{"m1"},
|
Models: []string{"m1"},
|
||||||
Platform: "",
|
Platform: "",
|
||||||
},
|
},
|
||||||
wantField: "Platform",
|
wantField: "Platform",
|
||||||
wantValue: "anthropic",
|
wantValue: "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ func newTestGatewayHandler(t *testing.T, group *service.Group, accounts []*servi
|
|||||||
nil, // tlsFPProfileService
|
nil, // tlsFPProfileService
|
||||||
nil, // channelService
|
nil, // channelService
|
||||||
nil, // resolver
|
nil, // resolver
|
||||||
|
nil, // balanceNotifyService
|
||||||
)
|
)
|
||||||
|
|
||||||
// RunModeSimple:跳过计费检查,避免引入 repo/cache 依赖。
|
// RunModeSimple:跳过计费检查,避免引入 repo/cache 依赖。
|
||||||
|
|||||||
@@ -682,6 +682,16 @@ func shouldFallbackGeminiModels(res *service.UpstreamHTTPResult) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldFallbackGeminiModel(modelName string, res *service.UpstreamHTTPResult) bool {
|
||||||
|
if shouldFallbackGeminiModels(res) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if res == nil || res.StatusCode != http.StatusNotFound {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return gemini.HasFallbackModel(modelName)
|
||||||
|
}
|
||||||
|
|
||||||
// extractGeminiCLISessionHash 从 Gemini CLI 请求中提取会话标识。
|
// extractGeminiCLISessionHash 从 Gemini CLI 请求中提取会话标识。
|
||||||
// 组合 x-gemini-api-privileged-user-id header 和请求体中的 tmp 目录哈希。
|
// 组合 x-gemini-api-privileged-user-id header 和请求体中的 tmp 目录哈希。
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -276,12 +276,3 @@ func easyPaySign(params map[string]string, pkey string) string {
|
|||||||
func easyPayVerifySign(params map[string]string, pkey string, sign string) bool {
|
func easyPayVerifySign(params map[string]string, pkey string, sign string) bool {
|
||||||
return hmac.Equal([]byte(easyPaySign(params, pkey)), []byte(sign))
|
return hmac.Equal([]byte(easyPaySign(params, pkey)), []byte(sign))
|
||||||
}
|
}
|
||||||
|
|
||||||
// decodeURLValue URL-decodes a string once.
|
|
||||||
func decodeURLValue(s string) string {
|
|
||||||
decoded, err := url.QueryUnescape(s)
|
|
||||||
if err != nil {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return decoded
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func TestBraveProvider_Search_Success(t *testing.T) {
|
|||||||
{URL: "https://tour.go.dev", Title: "Tour", Description: "A Tour of Go", Age: "3 days"},
|
{URL: "https://tour.go.dev", Title: "Tour", Description: "A Tour of Go", Age: "3 days"},
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
json.NewEncoder(w).Encode(resp)
|
_ = json.NewEncoder(w).Encode(resp)
|
||||||
}))
|
}))
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ func TestBraveProvider_Search_DefaultMaxResults(t *testing.T) {
|
|||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
receivedCount = r.URL.Query().Get("count")
|
receivedCount = r.URL.Query().Get("count")
|
||||||
resp := braveResponse{}
|
resp := braveResponse{}
|
||||||
json.NewEncoder(w).Encode(resp)
|
_ = json.NewEncoder(w).Encode(resp)
|
||||||
}))
|
}))
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ func TestBraveProvider_Search_DefaultMaxResults(t *testing.T) {
|
|||||||
func TestBraveProvider_Search_HTTPError(t *testing.T) {
|
func TestBraveProvider_Search_HTTPError(t *testing.T) {
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.WriteHeader(429)
|
w.WriteHeader(429)
|
||||||
w.Write([]byte("rate limited"))
|
_, _ = w.Write([]byte("rate limited"))
|
||||||
}))
|
}))
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ func TestBraveProvider_Search_HTTPError(t *testing.T) {
|
|||||||
|
|
||||||
func TestBraveProvider_Search_InvalidJSON(t *testing.T) {
|
func TestBraveProvider_Search_InvalidJSON(t *testing.T) {
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.Write([]byte("not json"))
|
_, _ = w.Write([]byte("not json"))
|
||||||
}))
|
}))
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ func TestBraveProvider_Search_InvalidJSON(t *testing.T) {
|
|||||||
func TestBraveProvider_Search_EmptyResults(t *testing.T) {
|
func TestBraveProvider_Search_EmptyResults(t *testing.T) {
|
||||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
resp := braveResponse{}
|
resp := braveResponse{}
|
||||||
json.NewEncoder(w).Encode(resp)
|
_ = json.NewEncoder(w).Encode(resp)
|
||||||
}))
|
}))
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ func (t *TavilyProvider) Search(ctx context.Context, req SearchRequest) (*Search
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tavily: request failed: %w", err)
|
return nil, fmt.Errorf("tavily: request failed: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
|
body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseSize))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ func RegisterPaymentRoutes(
|
|||||||
adminOrders.POST("/:id/refund", adminPaymentHandler.ProcessRefund)
|
adminOrders.POST("/:id/refund", adminPaymentHandler.ProcessRefund)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Subscription Plans
|
// Subscription Plans
|
||||||
plans := adminGroup.Group("/plans")
|
plans := adminGroup.Group("/plans")
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -147,14 +147,14 @@ func TestFindPricingForModel(t *testing.T) {
|
|||||||
wantNil: true,
|
wantNil: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "wildcard matches by config order (first match wins)",
|
name: "wildcard matches by longest prefix (most specific wins)",
|
||||||
list: []ChannelModelPricing{
|
list: []ChannelModelPricing{
|
||||||
{ID: 10, Models: []string{"claude-*"}},
|
{ID: 10, Models: []string{"claude-*"}},
|
||||||
{ID: 11, Models: []string{"claude-opus-*"}},
|
{ID: 11, Models: []string{"claude-opus-*"}},
|
||||||
},
|
},
|
||||||
platform: "",
|
platform: "",
|
||||||
model: "claude-opus-4",
|
model: "claude-opus-4",
|
||||||
wantID: 10, // config order: "claude-*" is first and matches, so it wins
|
wantID: 11, // "claude-opus-*" is longer prefix, wins over "claude-*"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "shorter wildcard used when longer does not match",
|
name: "shorter wildcard used when longer does not match",
|
||||||
|
|||||||
@@ -412,10 +412,10 @@ func TestAdminService_AdminUpdateAPIKeyGroupID_SubscriptionGroup_Blocked(t *test
|
|||||||
userRepo := &userRepoStubForGroupUpdate{}
|
userRepo := &userRepoStubForGroupUpdate{}
|
||||||
svc := &adminServiceImpl{apiKeyRepo: apiKeyRepo, groupRepo: groupRepo, userRepo: userRepo}
|
svc := &adminServiceImpl{apiKeyRepo: apiKeyRepo, groupRepo: groupRepo, userRepo: userRepo}
|
||||||
|
|
||||||
// 订阅类型分组应被阻止绑定
|
// userSubRepo is nil → SUBSCRIPTION_REPOSITORY_UNAVAILABLE
|
||||||
_, err := svc.AdminUpdateAPIKeyGroupID(context.Background(), 1, int64Ptr(10))
|
_, err := svc.AdminUpdateAPIKeyGroupID(context.Background(), 1, int64Ptr(10))
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Equal(t, "SUBSCRIPTION_GROUP_NOT_ALLOWED", infraerrors.Reason(err))
|
require.Equal(t, "SUBSCRIPTION_REPOSITORY_UNAVAILABLE", infraerrors.Reason(err))
|
||||||
require.False(t, userRepo.addGroupCalled)
|
require.False(t, userRepo.addGroupCalled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ type Channel struct {
|
|||||||
Name string
|
Name string
|
||||||
Description string
|
Description string
|
||||||
Status string
|
Status string
|
||||||
BillingModelSource string // "requested", "upstream", or "channel_mapped"
|
BillingModelSource string // "requested", "upstream", or "channel_mapped"
|
||||||
RestrictModels bool // 是否限制模型(仅允许定价列表中的模型)
|
RestrictModels bool // 是否限制模型(仅允许定价列表中的模型)
|
||||||
Features string // 渠道特性描述(JSON 数组),用于支付页面展示
|
Features string // 渠道特性描述(JSON 数组),用于支付页面展示
|
||||||
FeaturesConfig map[string]any // 渠道功能配置(如 web search emulation)
|
FeaturesConfig map[string]any // 渠道功能配置(如 web search emulation)
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
|||||||
@@ -22,16 +22,16 @@ func (s *PaymentConfigService) ListProviderInstances(ctx context.Context) ([]*db
|
|||||||
|
|
||||||
// ProviderInstanceResponse is the API response for a provider instance.
|
// ProviderInstanceResponse is the API response for a provider instance.
|
||||||
type ProviderInstanceResponse struct {
|
type ProviderInstanceResponse struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
ProviderKey string `json:"provider_key"`
|
ProviderKey string `json:"provider_key"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Config map[string]string `json:"config"`
|
Config map[string]string `json:"config"`
|
||||||
SupportedTypes []string `json:"supported_types"`
|
SupportedTypes []string `json:"supported_types"`
|
||||||
Limits string `json:"limits"`
|
Limits string `json:"limits"`
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
RefundEnabled bool `json:"refund_enabled"`
|
RefundEnabled bool `json:"refund_enabled"`
|
||||||
SortOrder int `json:"sort_order"`
|
SortOrder int `json:"sort_order"`
|
||||||
PaymentMode string `json:"payment_mode"`
|
PaymentMode string `json:"payment_mode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListProviderInstancesWithConfig returns provider instances with decrypted config.
|
// ListProviderInstancesWithConfig returns provider instances with decrypted config.
|
||||||
|
|||||||
Reference in New Issue
Block a user