Files
team-api/relay/handler/shadow_signals_test.go
T
Qian 2ef5a40c26 新的调度方式 (#14)
重构模型渠道调度系统
2026-08-01 22:55:08 +08:00

99 lines
3.2 KiB
Go

package handler
import (
"encoding/json"
"errors"
"fmt"
"testing"
"time"
"github.com/qianfree/team-api/relay/constant"
"github.com/qianfree/team-api/relaykit/dispatch"
)
func TestExtractSessionSignals(t *testing.T) {
body := []byte(`{
"model": "claude-sonnet",
"metadata": {"user_id": "user_abc_session_11111111-2222-3333-4444-555555555555"},
"previous_response_id": "resp_123",
"conversation_id": "conv_456"
}`)
var raw map[string]json.RawMessage
if err := json.Unmarshal(body, &raw); err != nil {
t.Fatal(err)
}
sig := extractSessionSignals(raw)
if sig.AnthropicUserID != "user_abc_session_11111111-2222-3333-4444-555555555555" {
t.Errorf("AnthropicUserID = %q", sig.AnthropicUserID)
}
if sig.PreviousResponseID != "resp_123" {
t.Errorf("PreviousResponseID = %q", sig.PreviousResponseID)
}
if sig.ConversationID != "conv_456" {
t.Errorf("ConversationID = %q", sig.ConversationID)
}
// 无信号的请求体
var empty map[string]json.RawMessage
_ = json.Unmarshal([]byte(`{"model":"gpt-4o"}`), &empty)
sig = extractSessionSignals(empty)
if sig.AnthropicUserID != "" || sig.PreviousResponseID != "" || sig.ConversationID != "" {
t.Errorf("空请求体不应提取出信号: %+v", sig)
}
// metadata 非对象(畸形)不 panic
var malformed map[string]json.RawMessage
_ = json.Unmarshal([]byte(`{"metadata": "not-an-object"}`), &malformed)
_ = extractSessionSignals(malformed)
}
func TestReplayabilityForMode(t *testing.T) {
pol := dispatch.DefaultRoutingPolicy().Replay
tests := []struct {
mode string
want dispatch.Replayability
}{
{"images_generations", dispatch.ReplayUnsafe},
{"images_edits", dispatch.ReplayUnsafe},
{"video_generations", dispatch.ReplayUnsafe},
{"embeddings", dispatch.ReplaySafe},
{"rerank", dispatch.ReplaySafe},
{"chat_completions", dispatch.ReplayCostly},
{"claude_messages", dispatch.ReplayCostly},
{"", dispatch.ReplayCostly},
}
for _, tt := range tests {
if got := pol.ReplayabilityForMode(tt.mode); got != tt.want {
t.Errorf("ReplayabilityForMode(%q) = %v, want %v", tt.mode, got, tt.want)
}
}
// relayModeString 与默认策略取值必须对齐(防止改字符串后 Unsafe 保护静默失效)
for _, mode := range []constant.RelayMode{constant.RelayModeImagesGenerations, constant.RelayModeImagesEdits, constant.RelayModeVideoGenerations} {
if got := pol.ReplayabilityForMode(relayModeString(mode)); got != dispatch.ReplayUnsafe {
t.Errorf("relayModeString(%v)=%q 未命中 UnsafeModes", mode, relayModeString(mode))
}
}
}
func TestRetryAfterOf(t *testing.T) {
// RelayError 携带 Retry-After → 提取
err := constant.NewUpstreamError(429, "rate limited", nil).WithRetryAfter(1500 * time.Millisecond)
if got := retryAfterOf(err); got != 1500*time.Millisecond {
t.Errorf("retryAfterOf = %v, want 1.5s", got)
}
// 包装后仍可解包
wrapped := fmt.Errorf("do response: %w", err)
if got := retryAfterOf(wrapped); got != 1500*time.Millisecond {
t.Errorf("包装后 retryAfterOf = %v, want 1.5s", got)
}
// 普通 error / nil → 0
if got := retryAfterOf(errors.New("x")); got != 0 {
t.Errorf("普通 error 应返回 0, got %v", got)
}
if got := retryAfterOf(nil); got != 0 {
t.Errorf("nil 应返回 0, got %v", got)
}
}