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:
erio
2026-04-14 12:11:08 +08:00
parent 4aa0070e3d
commit 6a08efeef9
11 changed files with 35 additions and 34 deletions

View File

@@ -273,13 +273,13 @@ func TestPricingRequestToService_Defaults(t *testing.T) {
wantValue: string(service.BillingModeToken),
},
{
name: "empty platform defaults to anthropic",
name: "empty platform stays empty",
req: channelModelPricingRequest{
Models: []string{"m1"},
Platform: "",
},
wantField: "Platform",
wantValue: "anthropic",
wantValue: "",
},
}

View File

@@ -168,6 +168,7 @@ func newTestGatewayHandler(t *testing.T, group *service.Group, accounts []*servi
nil, // tlsFPProfileService
nil, // channelService
nil, // resolver
nil, // balanceNotifyService
)
// RunModeSimple跳过计费检查避免引入 repo/cache 依赖。

View File

@@ -682,6 +682,16 @@ func shouldFallbackGeminiModels(res *service.UpstreamHTTPResult) bool {
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 请求中提取会话标识。
// 组合 x-gemini-api-privileged-user-id header 和请求体中的 tmp 目录哈希。
//

View File

@@ -276,12 +276,3 @@ func easyPaySign(params map[string]string, pkey string) string {
func easyPayVerifySign(params map[string]string, pkey string, sign string) bool {
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
}

View File

@@ -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"},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(resp)
_ = json.NewEncoder(w).Encode(resp)
}))
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) {
receivedCount = r.URL.Query().Get("count")
resp := braveResponse{}
json.NewEncoder(w).Encode(resp)
_ = json.NewEncoder(w).Encode(resp)
}))
defer srv.Close()
@@ -70,7 +70,7 @@ func TestBraveProvider_Search_DefaultMaxResults(t *testing.T) {
func TestBraveProvider_Search_HTTPError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(429)
w.Write([]byte("rate limited"))
_, _ = w.Write([]byte("rate limited"))
}))
defer srv.Close()
@@ -86,7 +86,7 @@ func TestBraveProvider_Search_HTTPError(t *testing.T) {
func TestBraveProvider_Search_InvalidJSON(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Write([]byte("not json"))
_, _ = w.Write([]byte("not json"))
}))
defer srv.Close()
@@ -103,7 +103,7 @@ func TestBraveProvider_Search_InvalidJSON(t *testing.T) {
func TestBraveProvider_Search_EmptyResults(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
resp := braveResponse{}
json.NewEncoder(w).Encode(resp)
_ = json.NewEncoder(w).Encode(resp)
}))
defer srv.Close()

View File

@@ -60,7 +60,7 @@ func (t *TavilyProvider) Search(ctx context.Context, req SearchRequest) (*Search
if err != nil {
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))
if err != nil {

View File

@@ -78,7 +78,6 @@ func RegisterPaymentRoutes(
adminOrders.POST("/:id/refund", adminPaymentHandler.ProcessRefund)
}
// Subscription Plans
plans := adminGroup.Group("/plans")
{

View File

@@ -147,14 +147,14 @@ func TestFindPricingForModel(t *testing.T) {
wantNil: true,
},
{
name: "wildcard matches by config order (first match wins)",
name: "wildcard matches by longest prefix (most specific wins)",
list: []ChannelModelPricing{
{ID: 10, Models: []string{"claude-*"}},
{ID: 11, Models: []string{"claude-opus-*"}},
},
platform: "",
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",

View File

@@ -412,10 +412,10 @@ func TestAdminService_AdminUpdateAPIKeyGroupID_SubscriptionGroup_Blocked(t *test
userRepo := &userRepoStubForGroupUpdate{}
svc := &adminServiceImpl{apiKeyRepo: apiKeyRepo, groupRepo: groupRepo, userRepo: userRepo}
// 订阅类型分组应被阻止绑定
// userSubRepo is nil → SUBSCRIPTION_REPOSITORY_UNAVAILABLE
_, err := svc.AdminUpdateAPIKeyGroupID(context.Background(), 1, int64Ptr(10))
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)
}

View File

@@ -37,8 +37,8 @@ type Channel struct {
Name string
Description string
Status string
BillingModelSource string // "requested", "upstream", or "channel_mapped"
RestrictModels bool // 是否限制模型(仅允许定价列表中的模型)
BillingModelSource string // "requested", "upstream", or "channel_mapped"
RestrictModels bool // 是否限制模型(仅允许定价列表中的模型)
Features string // 渠道特性描述JSON 数组),用于支付页面展示
FeaturesConfig map[string]any // 渠道功能配置(如 web search emulation
CreatedAt time.Time

View File

@@ -22,16 +22,16 @@ func (s *PaymentConfigService) ListProviderInstances(ctx context.Context) ([]*db
// ProviderInstanceResponse is the API response for a provider instance.
type ProviderInstanceResponse struct {
ID int64 `json:"id"`
ProviderKey string `json:"provider_key"`
Name string `json:"name"`
Config map[string]string `json:"config"`
SupportedTypes []string `json:"supported_types"`
Limits string `json:"limits"`
Enabled bool `json:"enabled"`
RefundEnabled bool `json:"refund_enabled"`
SortOrder int `json:"sort_order"`
PaymentMode string `json:"payment_mode"`
ID int64 `json:"id"`
ProviderKey string `json:"provider_key"`
Name string `json:"name"`
Config map[string]string `json:"config"`
SupportedTypes []string `json:"supported_types"`
Limits string `json:"limits"`
Enabled bool `json:"enabled"`
RefundEnabled bool `json:"refund_enabled"`
SortOrder int `json:"sort_order"`
PaymentMode string `json:"payment_mode"`
}
// ListProviderInstancesWithConfig returns provider instances with decrypted config.