Merge pull request #2592 from router-for-me/tests
fix(tests): update test cases
This commit is contained in:
+1
-6
@@ -42,6 +42,7 @@ GEMINI.md
|
|||||||
.agents/*
|
.agents/*
|
||||||
.opencode/*
|
.opencode/*
|
||||||
.idea/*
|
.idea/*
|
||||||
|
.beads/*
|
||||||
.bmad/*
|
.bmad/*
|
||||||
_bmad/*
|
_bmad/*
|
||||||
_bmad-output/*
|
_bmad-output/*
|
||||||
@@ -49,9 +50,3 @@ _bmad-output/*
|
|||||||
# macOS
|
# macOS
|
||||||
.DS_Store
|
.DS_Store
|
||||||
._*
|
._*
|
||||||
|
|
||||||
# Opencode
|
|
||||||
.beads/
|
|
||||||
.opencode/
|
|
||||||
.cli-proxy-api/
|
|
||||||
.venv/
|
|
||||||
|
|||||||
@@ -129,11 +129,11 @@ func TestModifyResponse_GzipScenarios(t *testing.T) {
|
|||||||
wantCE: "",
|
wantCE: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "skips_non_2xx_status",
|
name: "decompresses_non_2xx_status_when_gzip_detected",
|
||||||
header: http.Header{},
|
header: http.Header{},
|
||||||
body: good,
|
body: good,
|
||||||
status: 404,
|
status: 404,
|
||||||
wantBody: good,
|
wantBody: goodJSON,
|
||||||
wantCE: "",
|
wantCE: "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,9 +56,12 @@ func TestEnsureQwenSystemMessage_MergeStringSystem(t *testing.T) {
|
|||||||
if len(parts) != 2 {
|
if len(parts) != 2 {
|
||||||
t.Fatalf("messages[0].content length = %d, want 2", len(parts))
|
t.Fatalf("messages[0].content length = %d, want 2", len(parts))
|
||||||
}
|
}
|
||||||
if parts[0].Get("text").String() != "You are Qwen Code." || parts[0].Get("cache_control.type").String() != "ephemeral" {
|
if parts[0].Get("type").String() != "text" || parts[0].Get("cache_control.type").String() != "ephemeral" {
|
||||||
t.Fatalf("messages[0].content[0] = %s, want injected system part", parts[0].Raw)
|
t.Fatalf("messages[0].content[0] = %s, want injected system part", parts[0].Raw)
|
||||||
}
|
}
|
||||||
|
if text := parts[0].Get("text").String(); text != "" && text != "You are Qwen Code." {
|
||||||
|
t.Fatalf("messages[0].content[0].text = %q, want empty string or default prompt", text)
|
||||||
|
}
|
||||||
if parts[1].Get("type").String() != "text" || parts[1].Get("text").String() != "ABCDEFG" {
|
if parts[1].Get("type").String() != "text" || parts[1].Get("text").String() != "ABCDEFG" {
|
||||||
t.Fatalf("messages[0].content[1] = %s, want text part with ABCDEFG", parts[1].Raw)
|
t.Fatalf("messages[0].content[1] = %s, want text part with ABCDEFG", parts[1].Raw)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -174,8 +174,7 @@ func (a *Applier) normalizeClaudeBudget(body []byte, budgetTokens int, modelInfo
|
|||||||
// Ensure the request satisfies Claude constraints:
|
// Ensure the request satisfies Claude constraints:
|
||||||
// 1) Determine effective max_tokens (request overrides model default)
|
// 1) Determine effective max_tokens (request overrides model default)
|
||||||
// 2) If budget_tokens >= max_tokens, reduce budget_tokens to max_tokens-1
|
// 2) If budget_tokens >= max_tokens, reduce budget_tokens to max_tokens-1
|
||||||
// 3) If the adjusted budget falls below the model minimum, try raising max_tokens
|
// 3) If the adjusted budget falls below the model minimum, leave the request unchanged
|
||||||
// (clamped to MaxCompletionTokens); disable thinking if constraints are unsatisfiable
|
|
||||||
// 4) If max_tokens came from model default, write it back into the request
|
// 4) If max_tokens came from model default, write it back into the request
|
||||||
|
|
||||||
effectiveMax, setDefaultMax := a.effectiveMaxTokens(body, modelInfo)
|
effectiveMax, setDefaultMax := a.effectiveMaxTokens(body, modelInfo)
|
||||||
@@ -194,28 +193,8 @@ func (a *Applier) normalizeClaudeBudget(body []byte, budgetTokens int, modelInfo
|
|||||||
minBudget = modelInfo.Thinking.Min
|
minBudget = modelInfo.Thinking.Min
|
||||||
}
|
}
|
||||||
if minBudget > 0 && adjustedBudget > 0 && adjustedBudget < minBudget {
|
if minBudget > 0 && adjustedBudget > 0 && adjustedBudget < minBudget {
|
||||||
// Enforcing budget_tokens < max_tokens pushed the budget below the model minimum.
|
// If enforcing the max_tokens constraint would push the budget below the model minimum,
|
||||||
// Try raising max_tokens to fit the original budget.
|
// leave the request unchanged.
|
||||||
needed := budgetTokens + 1
|
|
||||||
maxAllowed := 0
|
|
||||||
if modelInfo != nil {
|
|
||||||
maxAllowed = modelInfo.MaxCompletionTokens
|
|
||||||
}
|
|
||||||
if maxAllowed > 0 && needed > maxAllowed {
|
|
||||||
// Cannot use original budget; cap max_tokens at model limit.
|
|
||||||
needed = maxAllowed
|
|
||||||
}
|
|
||||||
cappedBudget := needed - 1
|
|
||||||
if cappedBudget < minBudget {
|
|
||||||
// Impossible to satisfy both budget >= minBudget and budget < max_tokens
|
|
||||||
// within the model's completion limit. Disable thinking entirely.
|
|
||||||
body, _ = sjson.DeleteBytes(body, "thinking")
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
body, _ = sjson.SetBytes(body, "max_tokens", needed)
|
|
||||||
if cappedBudget != budgetTokens {
|
|
||||||
body, _ = sjson.SetBytes(body, "thinking.budget_tokens", cappedBudget)
|
|
||||||
}
|
|
||||||
return body
|
return body
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
package claude
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/registry"
|
|
||||||
"github.com/tidwall/gjson"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNormalizeClaudeBudget_RaisesMaxTokens(t *testing.T) {
|
|
||||||
a := &Applier{}
|
|
||||||
modelInfo := ®istry.ModelInfo{
|
|
||||||
MaxCompletionTokens: 64000,
|
|
||||||
Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000},
|
|
||||||
}
|
|
||||||
body := []byte(`{"max_tokens":1000,"thinking":{"type":"enabled","budget_tokens":5000}}`)
|
|
||||||
|
|
||||||
out := a.normalizeClaudeBudget(body, 5000, modelInfo)
|
|
||||||
|
|
||||||
maxTok := gjson.GetBytes(out, "max_tokens").Int()
|
|
||||||
if maxTok != 5001 {
|
|
||||||
t.Fatalf("max_tokens = %d, want 5001, body=%s", maxTok, string(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeClaudeBudget_ClampsToModelMax(t *testing.T) {
|
|
||||||
a := &Applier{}
|
|
||||||
modelInfo := ®istry.ModelInfo{
|
|
||||||
MaxCompletionTokens: 64000,
|
|
||||||
Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000},
|
|
||||||
}
|
|
||||||
body := []byte(`{"max_tokens":500,"thinking":{"type":"enabled","budget_tokens":200000}}`)
|
|
||||||
|
|
||||||
out := a.normalizeClaudeBudget(body, 200000, modelInfo)
|
|
||||||
|
|
||||||
maxTok := gjson.GetBytes(out, "max_tokens").Int()
|
|
||||||
if maxTok != 64000 {
|
|
||||||
t.Fatalf("max_tokens = %d, want 64000 (capped to model limit), body=%s", maxTok, string(out))
|
|
||||||
}
|
|
||||||
budget := gjson.GetBytes(out, "thinking.budget_tokens").Int()
|
|
||||||
if budget != 63999 {
|
|
||||||
t.Fatalf("budget_tokens = %d, want 63999 (max_tokens-1), body=%s", budget, string(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeClaudeBudget_DisablesThinkingWhenUnsatisfiable(t *testing.T) {
|
|
||||||
a := &Applier{}
|
|
||||||
modelInfo := ®istry.ModelInfo{
|
|
||||||
MaxCompletionTokens: 1000,
|
|
||||||
Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000},
|
|
||||||
}
|
|
||||||
body := []byte(`{"max_tokens":500,"thinking":{"type":"enabled","budget_tokens":2000}}`)
|
|
||||||
|
|
||||||
out := a.normalizeClaudeBudget(body, 2000, modelInfo)
|
|
||||||
|
|
||||||
if gjson.GetBytes(out, "thinking").Exists() {
|
|
||||||
t.Fatalf("thinking should be removed when constraints are unsatisfiable, body=%s", string(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeClaudeBudget_NoClamping(t *testing.T) {
|
|
||||||
a := &Applier{}
|
|
||||||
modelInfo := ®istry.ModelInfo{
|
|
||||||
MaxCompletionTokens: 64000,
|
|
||||||
Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000},
|
|
||||||
}
|
|
||||||
body := []byte(`{"max_tokens":32000,"thinking":{"type":"enabled","budget_tokens":16000}}`)
|
|
||||||
|
|
||||||
out := a.normalizeClaudeBudget(body, 16000, modelInfo)
|
|
||||||
|
|
||||||
maxTok := gjson.GetBytes(out, "max_tokens").Int()
|
|
||||||
if maxTok != 32000 {
|
|
||||||
t.Fatalf("max_tokens should remain 32000, got %d, body=%s", maxTok, string(out))
|
|
||||||
}
|
|
||||||
budget := gjson.GetBytes(out, "thinking.budget_tokens").Int()
|
|
||||||
if budget != 16000 {
|
|
||||||
t.Fatalf("budget_tokens should remain 16000, got %d, body=%s", budget, string(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNormalizeClaudeBudget_AdjustsBudgetToMaxMinus1(t *testing.T) {
|
|
||||||
a := &Applier{}
|
|
||||||
modelInfo := ®istry.ModelInfo{
|
|
||||||
MaxCompletionTokens: 8192,
|
|
||||||
Thinking: ®istry.ThinkingSupport{Min: 1024, Max: 128000},
|
|
||||||
}
|
|
||||||
body := []byte(`{"max_tokens":8192,"thinking":{"type":"enabled","budget_tokens":10000}}`)
|
|
||||||
|
|
||||||
out := a.normalizeClaudeBudget(body, 10000, modelInfo)
|
|
||||||
|
|
||||||
maxTok := gjson.GetBytes(out, "max_tokens").Int()
|
|
||||||
if maxTok != 8192 {
|
|
||||||
t.Fatalf("max_tokens = %d, want 8192 (unchanged), body=%s", maxTok, string(out))
|
|
||||||
}
|
|
||||||
budget := gjson.GetBytes(out, "thinking.budget_tokens").Int()
|
|
||||||
if budget != 8191 {
|
|
||||||
t.Fatalf("budget_tokens = %d, want 8191 (max_tokens-1), body=%s", budget, string(out))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -53,8 +53,24 @@ func TestServiceApplyCoreAuthAddOrUpdate_DeleteReAddDoesNotInheritStaleRuntimeSt
|
|||||||
if disabled.NextRefreshAfter.IsZero() {
|
if disabled.NextRefreshAfter.IsZero() {
|
||||||
t.Fatalf("expected disabled auth to still carry prior NextRefreshAfter for regression setup")
|
t.Fatalf("expected disabled auth to still carry prior NextRefreshAfter for regression setup")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reconcile prunes unsupported model state during registration, so seed the
|
||||||
|
// disabled snapshot explicitly before exercising delete -> re-add behavior.
|
||||||
|
disabled.ModelStates = map[string]*coreauth.ModelState{
|
||||||
|
modelID: {
|
||||||
|
Quota: coreauth.QuotaState{BackoffLevel: 7},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if _, err := service.coreManager.Update(context.Background(), disabled); err != nil {
|
||||||
|
t.Fatalf("seed disabled auth stale ModelStates: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
disabled, ok = service.coreManager.GetByID(authID)
|
||||||
|
if !ok || disabled == nil {
|
||||||
|
t.Fatalf("expected disabled auth after stale state seeding")
|
||||||
|
}
|
||||||
if len(disabled.ModelStates) == 0 {
|
if len(disabled.ModelStates) == 0 {
|
||||||
t.Fatalf("expected disabled auth to still carry prior ModelStates for regression setup")
|
t.Fatalf("expected disabled auth to carry seeded ModelStates for regression setup")
|
||||||
}
|
}
|
||||||
|
|
||||||
service.applyCoreAuthAddOrUpdate(context.Background(), &coreauth.Auth{
|
service.applyCoreAuthAddOrUpdate(context.Background(), &coreauth.Auth{
|
||||||
|
|||||||
Reference in New Issue
Block a user