| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- package service
- import (
- "context"
- "encoding/json"
- "reflect"
- "testing"
- "github.com/Wei-Shaw/sub2api/internal/config"
- "github.com/Wei-Shaw/sub2api/internal/pkg/openai"
- "github.com/stretchr/testify/require"
- )
- func TestGetCodexRestrictionPolicy(t *testing.T) {
- svc := NewSettingService(&codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyMinCodexVersion: "0.141.0",
- SettingKeyMaxCodexVersion: "0.200.0",
- SettingKeyCodexCLIOnlyWhitelist: `[{"originator":"opencode","ua_contains":["opencode/"]}]`,
- SettingKeyCodexCLIOnlyBlacklist: `[{"originator":"evil"}]`,
- }}, &config.Config{})
- pol := svc.GetCodexRestrictionPolicy(context.Background())
- require.Equal(t, "0.141.0", pol.MinCodexVersion)
- require.Equal(t, "0.200.0", pol.MaxCodexVersion)
- require.Len(t, pol.Whitelist, 1)
- require.Equal(t, "opencode", pol.Whitelist[0].Originator)
- require.Equal(t, []string{"opencode/"}, pol.Whitelist[0].UAContains)
- require.Len(t, pol.Blacklist, 1)
- require.Equal(t, "evil", pol.Blacklist[0].Originator)
- }
- func TestGetCodexRestrictionPolicy_DefaultsSafe(t *testing.T) {
- svc := NewSettingService(&codexPolicyMigrationRepoStub{values: map[string]string{}}, &config.Config{})
- pol := svc.GetCodexRestrictionPolicy(context.Background())
- require.Empty(t, pol.MinCodexVersion)
- require.Empty(t, pol.Whitelist)
- require.Empty(t, pol.Blacklist)
- }
- func TestGetCodexRestrictionPolicy_InvalidJSONSafe(t *testing.T) {
- svc := NewSettingService(&codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyCodexCLIOnlyWhitelist: "not-json",
- SettingKeyCodexCLIOnlyBlacklist: "{bad",
- }}, &config.Config{})
- pol := svc.GetCodexRestrictionPolicy(context.Background())
- require.Empty(t, pol.Whitelist, "非法 JSON → 安全空名单")
- require.Empty(t, pol.Blacklist, "非法 JSON → 安全空名单")
- }
- type codexPolicyMigrationRepoStub struct {
- values map[string]string
- sets map[string]string
- }
- func (s *codexPolicyMigrationRepoStub) Get(ctx context.Context, key string) (*Setting, error) {
- panic("unused")
- }
- func (s *codexPolicyMigrationRepoStub) GetValue(ctx context.Context, key string) (string, error) {
- if v, ok := s.values[key]; ok {
- return v, nil
- }
- return "", ErrSettingNotFound
- }
- func (s *codexPolicyMigrationRepoStub) Set(ctx context.Context, key, value string) error {
- if s.sets == nil {
- s.sets = map[string]string{}
- }
- s.sets[key] = value
- s.values[key] = value
- return nil
- }
- func (s *codexPolicyMigrationRepoStub) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) {
- panic("unused")
- }
- func (s *codexPolicyMigrationRepoStub) SetMultiple(ctx context.Context, settings map[string]string) error {
- panic("unused")
- }
- func (s *codexPolicyMigrationRepoStub) GetAll(ctx context.Context) (map[string]string, error) {
- panic("unused")
- }
- func (s *codexPolicyMigrationRepoStub) Delete(ctx context.Context, key string) error {
- panic("unused")
- }
- func TestMigrateOpenAIAllowClaudeCodeCodexPluginSetting(t *testing.T) {
- t.Run("legacy true appends Claude Code entry to whitelist", func(t *testing.T) {
- repo := &codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyOpenAIAllowClaudeCodeCodexPlugin: "true",
- SettingKeyCodexCLIOnlyWhitelist: `[{"originator":"opencode","ua_contains":["opencode/"]}]`,
- }}
- svc := NewSettingService(repo, &config.Config{})
- require.NoError(t, svc.MigrateOpenAIAllowClaudeCodeCodexPluginSetting(context.Background()))
- raw := repo.sets[SettingKeyCodexCLIOnlyWhitelist]
- require.NotEmpty(t, raw)
- var entries []struct {
- Originator string `json:"originator"`
- UAContains []string `json:"ua_contains"`
- }
- require.NoError(t, json.Unmarshal([]byte(raw), &entries))
- require.Len(t, entries, 2)
- require.Equal(t, "opencode", entries[0].Originator)
- require.Equal(t, "Claude Code", entries[1].Originator)
- require.Equal(t, []string{"Claude Code/"}, entries[1].UAContains)
- })
- t.Run("legacy true does not duplicate existing Claude Code entry", func(t *testing.T) {
- repo := &codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyOpenAIAllowClaudeCodeCodexPlugin: "true",
- SettingKeyCodexCLIOnlyWhitelist: `[{"originator":"Claude Code","ua_contains":["Claude Code/"]}]`,
- }}
- svc := NewSettingService(repo, &config.Config{})
- require.NoError(t, svc.MigrateOpenAIAllowClaudeCodeCodexPluginSetting(context.Background()))
- _, wrote := repo.sets[SettingKeyCodexCLIOnlyWhitelist]
- require.False(t, wrote)
- })
- }
- func TestGetCodexRestrictionPolicy_AllowAppServerClients(t *testing.T) {
- t.Run("显式 true 开启", func(t *testing.T) {
- svc := NewSettingService(&codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyCodexCLIOnlyAllowAppServerClients: "true",
- }}, &config.Config{})
- require.True(t, svc.GetCodexRestrictionPolicy(context.Background()).AllowAppServerClients)
- })
- t.Run("缺失默认 false", func(t *testing.T) {
- svc := NewSettingService(&codexPolicyMigrationRepoStub{values: map[string]string{}}, &config.Config{})
- require.False(t, svc.GetCodexRestrictionPolicy(context.Background()).AllowAppServerClients)
- })
- t.Run("非 true 值视为 false", func(t *testing.T) {
- svc := NewSettingService(&codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyCodexCLIOnlyAllowAppServerClients: "1",
- }}, &config.Config{})
- require.False(t, svc.GetCodexRestrictionPolicy(context.Background()).AllowAppServerClients)
- })
- }
- func TestGetCodexRestrictionPolicy_EngineFingerprintSignals(t *testing.T) {
- t.Run("键缺失 → 默认种子(只勾x-codex-)", func(t *testing.T) {
- svc := NewSettingService(&codexPolicyMigrationRepoStub{values: map[string]string{}}, &config.Config{})
- pol := svc.GetCodexRestrictionPolicy(context.Background())
- require.True(t, len(pol.EngineFingerprintSignals) > 0)
- require.True(t, openaiEngineSignalsEqual(pol.EngineFingerprintSignals, openai.DefaultEngineFingerprintSignals))
- })
- t.Run("显式配置 → 原样采用", func(t *testing.T) {
- raw := `[{"type":"header_exact","match":["session-id"],"required":true}]`
- svc := NewSettingService(&codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyCodexCLIOnlyEngineFingerprintSignals: raw,
- }}, &config.Config{})
- pol := svc.GetCodexRestrictionPolicy(context.Background())
- require.Len(t, pol.EngineFingerprintSignals, 1)
- require.Equal(t, "session-id", pol.EngineFingerprintSignals[0].Match[0])
- })
- t.Run("非法JSON → 回落默认种子", func(t *testing.T) {
- svc := NewSettingService(&codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyCodexCLIOnlyEngineFingerprintSignals: "not json",
- }}, &config.Config{})
- pol := svc.GetCodexRestrictionPolicy(context.Background())
- require.True(t, openaiEngineSignalsEqual(pol.EngineFingerprintSignals, openai.DefaultEngineFingerprintSignals))
- })
- }
- func openaiEngineSignalsEqual(a, b []openai.EngineFingerprintSignal) bool {
- return reflect.DeepEqual(a, b)
- }
- func TestMigrateCodexBodyFingerprintToSignals(t *testing.T) {
- t.Run("信号键已存在 → 不动", func(t *testing.T) {
- repo := &codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyCodexCLIOnlyEngineFingerprintSignals: `[{"type":"header_exact","match":["session-id"],"required":true}]`,
- SettingKeyCodexCLIOnlyAllowBodyEngineFingerprint: "true",
- }}
- svc := NewSettingService(repo, &config.Config{})
- require.NoError(t, svc.MigrateCodexBodyFingerprintToSignals(context.Background()))
- require.Equal(t, `[{"type":"header_exact","match":["session-id"],"required":true}]`, repo.values[SettingKeyCodexCLIOnlyEngineFingerprintSignals])
- })
- t.Run("信号键缺失 + 旧body=true → 写种子且body行Required=true", func(t *testing.T) {
- repo := &codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyCodexCLIOnlyAllowBodyEngineFingerprint: "true",
- }}
- svc := NewSettingService(repo, &config.Config{})
- require.NoError(t, svc.MigrateCodexBodyFingerprintToSignals(context.Background()))
- sigs, ok := openai.ParseEngineFingerprintSignals(repo.values[SettingKeyCodexCLIOnlyEngineFingerprintSignals])
- require.True(t, ok)
- var bodyReq bool
- for _, s := range sigs {
- if s.Type == openai.FingerprintSignalBodyPath {
- bodyReq = s.Required
- }
- }
- require.True(t, bodyReq)
- })
- t.Run("信号键缺失 + 旧body=false/缺 → 写种子(body不勾)", func(t *testing.T) {
- repo := &codexPolicyMigrationRepoStub{values: map[string]string{}}
- svc := NewSettingService(repo, &config.Config{})
- require.NoError(t, svc.MigrateCodexBodyFingerprintToSignals(context.Background()))
- require.Equal(t, openai.DefaultEngineFingerprintSignalsJSON(), repo.values[SettingKeyCodexCLIOnlyEngineFingerprintSignals])
- })
- }
- func TestMigrateGrokDefaultTextModel(t *testing.T) {
- t.Run("upgrades legacy built-in default", func(t *testing.T) {
- repo := &codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyGrokDefaultTextModel: "grok-4.5",
- }}
- svc := NewSettingService(repo, &config.Config{})
- require.NoError(t, svc.MigrateGrokDefaultTextModel(context.Background()))
- require.Equal(t, "grok-4.6", repo.values[SettingKeyGrokDefaultTextModel])
- require.Equal(t, "grok-4.6", repo.sets[SettingKeyGrokDefaultTextModel])
- })
- t.Run("does not overwrite an explicit model", func(t *testing.T) {
- repo := &codexPolicyMigrationRepoStub{values: map[string]string{
- SettingKeyGrokDefaultTextModel: "grok-4.3",
- }}
- svc := NewSettingService(repo, &config.Config{})
- require.NoError(t, svc.MigrateGrokDefaultTextModel(context.Background()))
- require.Equal(t, "grok-4.3", repo.values[SettingKeyGrokDefaultTextModel])
- _, wrote := repo.sets[SettingKeyGrokDefaultTextModel]
- require.False(t, wrote)
- })
- t.Run("missing setting is left for normal defaults", func(t *testing.T) {
- repo := &codexPolicyMigrationRepoStub{values: map[string]string{}}
- svc := NewSettingService(repo, &config.Config{})
- require.NoError(t, svc.MigrateGrokDefaultTextModel(context.Background()))
- _, wrote := repo.sets[SettingKeyGrokDefaultTextModel]
- require.False(t, wrote)
- })
- }
|