| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- package service
- import (
- "context"
- "encoding/json"
- "testing"
- "github.com/Wei-Shaw/sub2api/internal/config"
- )
- type betaPolicySettingRepoStub struct {
- values map[string]string
- }
- func (s *betaPolicySettingRepoStub) Get(ctx context.Context, key string) (*Setting, error) {
- panic("unexpected Get call")
- }
- func (s *betaPolicySettingRepoStub) GetValue(ctx context.Context, key string) (string, error) {
- if v, ok := s.values[key]; ok {
- return v, nil
- }
- return "", ErrSettingNotFound
- }
- func (s *betaPolicySettingRepoStub) Set(ctx context.Context, key, value string) error {
- panic("unexpected Set call")
- }
- func (s *betaPolicySettingRepoStub) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) {
- panic("unexpected GetMultiple call")
- }
- func (s *betaPolicySettingRepoStub) SetMultiple(ctx context.Context, settings map[string]string) error {
- panic("unexpected SetMultiple call")
- }
- func (s *betaPolicySettingRepoStub) GetAll(ctx context.Context) (map[string]string, error) {
- panic("unexpected GetAll call")
- }
- func (s *betaPolicySettingRepoStub) Delete(ctx context.Context, key string) error {
- panic("unexpected Delete call")
- }
- func TestResolveBedrockBetaTokensForRequest_BlocksOnOriginalAnthropicToken(t *testing.T) {
- settings := &BetaPolicySettings{
- Rules: []BetaPolicyRule{
- {
- BetaToken: "advanced-tool-use-2025-11-20",
- Action: BetaPolicyActionBlock,
- Scope: BetaPolicyScopeAll,
- ErrorMessage: "advanced tool use is blocked",
- },
- },
- }
- raw, err := json.Marshal(settings)
- if err != nil {
- t.Fatalf("marshal settings: %v", err)
- }
- svc := &GatewayService{
- settingService: NewSettingService(
- &betaPolicySettingRepoStub{values: map[string]string{
- SettingKeyBetaPolicySettings: string(raw),
- }},
- &config.Config{},
- ),
- }
- account := &Account{Platform: PlatformAnthropic, Type: AccountTypeBedrock}
- _, err = svc.resolveBedrockBetaTokensForRequest(
- context.Background(),
- account,
- "advanced-tool-use-2025-11-20",
- []byte(`{"messages":[{"role":"user","content":"hi"}]}`),
- "us.anthropic.claude-opus-4-6-v1",
- )
- if err == nil {
- t.Fatal("expected raw advanced-tool-use token to be blocked before Bedrock transform")
- }
- if err.Error() != "advanced tool use is blocked" {
- t.Fatalf("unexpected error: %v", err)
- }
- }
- func TestResolveBedrockBetaTokensForRequest_FiltersAfterBedrockTransform(t *testing.T) {
- settings := &BetaPolicySettings{
- Rules: []BetaPolicyRule{
- {
- BetaToken: "tool-search-tool-2025-10-19",
- Action: BetaPolicyActionFilter,
- Scope: BetaPolicyScopeAll,
- },
- },
- }
- raw, err := json.Marshal(settings)
- if err != nil {
- t.Fatalf("marshal settings: %v", err)
- }
- svc := &GatewayService{
- settingService: NewSettingService(
- &betaPolicySettingRepoStub{values: map[string]string{
- SettingKeyBetaPolicySettings: string(raw),
- }},
- &config.Config{},
- ),
- }
- account := &Account{Platform: PlatformAnthropic, Type: AccountTypeBedrock}
- betaTokens, err := svc.resolveBedrockBetaTokensForRequest(
- context.Background(),
- account,
- "advanced-tool-use-2025-11-20",
- []byte(`{"messages":[{"role":"user","content":"hi"}]}`),
- "us.anthropic.claude-opus-4-6-v1",
- )
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- for _, token := range betaTokens {
- if token == "tool-search-tool-2025-10-19" {
- t.Fatalf("expected transformed Bedrock token to be filtered")
- }
- }
- }
- // TestResolveBedrockBetaTokensForRequest_BlocksBodyAutoInjectedComputerUse 验证:
- // 管理员 block 了 computer-use,客户端不在 header 中带该 token,
- // 但请求体包含 computer_use 工具 → 自动注入后应被 block。
- func TestResolveBedrockBetaTokensForRequest_BlocksBodyAutoInjectedComputerUse(t *testing.T) {
- settings := &BetaPolicySettings{
- Rules: []BetaPolicyRule{
- {
- BetaToken: "computer-use-2025-11-24",
- Action: BetaPolicyActionBlock,
- Scope: BetaPolicyScopeAll,
- ErrorMessage: "computer use is blocked",
- },
- },
- }
- raw, err := json.Marshal(settings)
- if err != nil {
- t.Fatalf("marshal settings: %v", err)
- }
- svc := &GatewayService{
- settingService: NewSettingService(
- &betaPolicySettingRepoStub{values: map[string]string{
- SettingKeyBetaPolicySettings: string(raw),
- }},
- &config.Config{},
- ),
- }
- account := &Account{Platform: PlatformAnthropic, Type: AccountTypeBedrock}
- // header 中不带 beta token,但 body 中有 computer_use 工具
- _, err = svc.resolveBedrockBetaTokensForRequest(
- context.Background(),
- account,
- "", // 空 header
- []byte(`{"tools":[{"type":"computer_20250124","name":"computer"}],"messages":[{"role":"user","content":"hi"}]}`),
- "us.anthropic.claude-opus-4-6-v1",
- )
- if err == nil {
- t.Fatal("expected body-injected computer-use to be blocked")
- }
- if err.Error() != "computer use is blocked" {
- t.Fatalf("unexpected error: %v", err)
- }
- }
- // TestResolveBedrockBetaTokensForRequest_BlocksBodyAutoInjectedToolSearch 验证:
- // 管理员 block 了 tool-search-tool,客户端不在 header 中带 beta token,
- // 但请求体包含 tool search 工具 → 自动注入后应被 block。
- func TestResolveBedrockBetaTokensForRequest_BlocksBodyAutoInjectedToolSearch(t *testing.T) {
- settings := &BetaPolicySettings{
- Rules: []BetaPolicyRule{
- {
- BetaToken: "tool-search-tool-2025-10-19",
- Action: BetaPolicyActionBlock,
- Scope: BetaPolicyScopeAll,
- ErrorMessage: "tool search is blocked",
- },
- },
- }
- raw, err := json.Marshal(settings)
- if err != nil {
- t.Fatalf("marshal settings: %v", err)
- }
- svc := &GatewayService{
- settingService: NewSettingService(
- &betaPolicySettingRepoStub{values: map[string]string{
- SettingKeyBetaPolicySettings: string(raw),
- }},
- &config.Config{},
- ),
- }
- account := &Account{Platform: PlatformAnthropic, Type: AccountTypeBedrock}
- // header 中不带 beta token,但 body 中有 tool_search_tool 工具
- _, err = svc.resolveBedrockBetaTokensForRequest(
- context.Background(),
- account,
- "",
- []byte(`{"tools":[{"type":"tool_search_tool_regex_20251119","name":"search"}],"messages":[{"role":"user","content":"hi"}]}`),
- "us.anthropic.claude-sonnet-4-6",
- )
- if err == nil {
- t.Fatal("expected body-injected tool-search-tool to be blocked")
- }
- if err.Error() != "tool search is blocked" {
- t.Fatalf("unexpected error: %v", err)
- }
- }
- // TestResolveBedrockBetaTokensForRequest_PassesWhenNoBlockRuleMatches 验证:
- // body 自动注入的 token 如果没有对应的 block 规则,应正常通过。
- func TestResolveBedrockBetaTokensForRequest_PassesWhenNoBlockRuleMatches(t *testing.T) {
- settings := &BetaPolicySettings{
- Rules: []BetaPolicyRule{
- {
- BetaToken: "context-1m-2025-08-07",
- Action: BetaPolicyActionBlock,
- Scope: BetaPolicyScopeAll,
- ErrorMessage: "context is blocked",
- },
- },
- }
- raw, err := json.Marshal(settings)
- if err != nil {
- t.Fatalf("marshal settings: %v", err)
- }
- svc := &GatewayService{
- settingService: NewSettingService(
- &betaPolicySettingRepoStub{values: map[string]string{
- SettingKeyBetaPolicySettings: string(raw),
- }},
- &config.Config{},
- ),
- }
- account := &Account{Platform: PlatformAnthropic, Type: AccountTypeBedrock}
- // body 中有 computer_use 工具(会注入 computer-use token),但 block 规则只针对 context-1m
- tokens, err := svc.resolveBedrockBetaTokensForRequest(
- context.Background(),
- account,
- "",
- []byte(`{"tools":[{"type":"computer_20250124","name":"computer"}],"messages":[{"role":"user","content":"hi"}]}`),
- "us.anthropic.claude-opus-4-6-v1",
- )
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- found := false
- for _, token := range tokens {
- if token == "computer-use-2025-11-24" {
- found = true
- }
- }
- if !found {
- t.Fatal("expected computer-use token to be present")
- }
- }
|