internal/config/client_test.go
Rama main · réplica de lectura del repositorio original.
package config
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
"gopkg.in/yaml.v3"
)
func TestClientCodexEnableApplyPatch(t *testing.T) {
for _, tc := range []struct {
name string
raw string
want bool
}{
{"omitted", "config-version: 8\n", false},
{"disabled", "client: {codex: {enable-apply-patch: false}}\n", false},
{"enabled", "client: {codex: {enable-apply-patch: true}}\n", true},
} {
t.Run(tc.name, func(t *testing.T) {
if errValidate := ValidateV8Config([]byte(tc.raw)); errValidate != nil {
t.Fatalf("validate: %v", errValidate)
}
cfg, errParse := ParseConfigBytes([]byte(tc.raw))
if errParse != nil {
t.Fatalf("parse: %v", errParse)
}
if cfg.Client.Codex.EnableApplyPatch != tc.want {
t.Fatalf("EnableApplyPatch = %t, want %t", cfg.Client.Codex.EnableApplyPatch, tc.want)
}
if cfg.OAuthOnlyFields["client.codex.enable-apply-patch"] {
t.Fatal("client setting must not be OAuth-only")
}
var sdkCfg SDKConfig
if errUnmarshal := yaml.Unmarshal([]byte(tc.raw), &sdkCfg); errUnmarshal != nil {
t.Fatalf("decode SDK config: %v", errUnmarshal)
}
if sdkCfg.Client.Codex.EnableApplyPatch != tc.want {
t.Fatalf("SDK EnableApplyPatch = %t, want %t", sdkCfg.Client.Codex.EnableApplyPatch, tc.want)
}
data, errMarshal := json.Marshal(cfg)
if errMarshal != nil {
t.Fatalf("encode JSON: %v", errMarshal)
}
var decoded Config
if errUnmarshal := json.Unmarshal(data, &decoded); errUnmarshal != nil {
t.Fatalf("decode JSON: %v", errUnmarshal)
}
if decoded.Client.Codex.EnableApplyPatch != tc.want {
t.Fatal("JSON round trip changed client setting")
}
migrated, _, errNormalize := NormalizeConfigLayout([]byte(tc.raw), true)
if errNormalize != nil {
t.Fatalf("normalize: %v", errNormalize)
}
if errValidate := ValidateV8Config(migrated); errValidate != nil {
t.Fatalf("validate normalized config: %v", errValidate)
}
if errUnmarshal := yaml.Unmarshal(migrated, &decoded); errUnmarshal != nil {
t.Fatalf("decode normalized config: %v", errUnmarshal)
}
if decoded.Client.Codex.EnableApplyPatch != tc.want {
t.Fatal("normalization changed client setting")
}
})
}
for _, raw := range []string{
"client: {codex: {enable-apply-patch: invalid}}\n",
"client: {codex: {unknown: true}}\n",
} {
if errValidate := ValidateV8Config([]byte(raw)); errValidate == nil {
t.Fatalf("accepted invalid config %q", raw)
}
}
}
func TestClientCodexEnableApplyPatchSave(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.yaml")
if errWrite := os.WriteFile(path, []byte("config-version: 8\n"), 0600); errWrite != nil {
t.Fatalf("write: %v", errWrite)
}
cfg := &Config{}
for _, enabled := range []bool{true, false} {
cfg.Client.Codex.EnableApplyPatch = enabled
if errSave := SaveConfigPreserveComments(path, cfg, true); errSave != nil {
t.Fatalf("save: %v", errSave)
}
data, errRead := os.ReadFile(path)
if errRead != nil {
t.Fatalf("read: %v", errRead)
}
if errValidate := ValidateV8Config(data); errValidate != nil {
t.Fatalf("validate saved config: %v", errValidate)
}
loaded, errLoad := LoadConfig(path)
if errLoad != nil {
t.Fatalf("load: %v", errLoad)
}
if loaded.Client.Codex.EnableApplyPatch != enabled {
t.Fatalf("saved EnableApplyPatch = %t, want %t", loaded.Client.Codex.EnableApplyPatch, enabled)
}
}
}
func TestClientCodexWebSearchResultCache(t *testing.T) {
for _, tc := range []struct {
name string
raw string
want CodexWebSearchResultCacheConfig
wantTTL time.Duration
wantLimit int
}{
{
name: "omitted",
raw: "config-version: 8\n",
want: CodexWebSearchResultCacheConfig{},
wantTTL: DefaultCodexWebSearchResultCacheTTL,
wantLimit: DefaultCodexWebSearchResultCacheLimit,
},
{
name: "enabled with integer seconds ttl",
raw: "client: {codex: {web-search-result-cache: {enabled: true, ttl: 1800, limit: 64}}}\n",
want: CodexWebSearchResultCacheConfig{Enabled: true, TTL: "1800", Limit: 64},
wantTTL: 30 * time.Minute,
wantLimit: 64,
},
{
name: "enabled with 30m ttl",
raw: "client: {codex: {web-search-result-cache: {enabled: true, ttl: 30m, limit: 128}}}\n",
want: CodexWebSearchResultCacheConfig{Enabled: true, TTL: "30m", Limit: 128},
wantTTL: 30 * time.Minute,
wantLimit: 128,
},
{
name: "disabled with 24h ttl",
raw: "client: {codex: {web-search-result-cache: {enabled: false, ttl: 24h, limit: 1024}}}\n",
want: CodexWebSearchResultCacheConfig{Enabled: false, TTL: "24h", Limit: 1024},
wantTTL: 24 * time.Hour,
wantLimit: 1024,
},
{
name: "null fields fall back to defaults",
raw: "client: {codex: {web-search-result-cache: {enabled: null, ttl: null, limit: null}}}\n",
want: CodexWebSearchResultCacheConfig{},
wantTTL: DefaultCodexWebSearchResultCacheTTL,
wantLimit: DefaultCodexWebSearchResultCacheLimit,
},
} {
t.Run(tc.name, func(t *testing.T) {
if errValidate := ValidateV8Config([]byte(tc.raw)); errValidate != nil {
t.Fatalf("validate: %v", errValidate)
}
cfg, errParse := ParseConfigBytes([]byte(tc.raw))
if errParse != nil {
t.Fatalf("parse: %v", errParse)
}
got := cfg.Client.Codex.WebSearchResultCache
if got != tc.want {
t.Fatalf("WebSearchResultCache = %+v, want %+v", got, tc.want)
}
if got.EffectiveTTL() != tc.wantTTL {
t.Fatalf("EffectiveTTL() = %v, want %v", got.EffectiveTTL(), tc.wantTTL)
}
if got.EffectiveLimit() != tc.wantLimit {
t.Fatalf("EffectiveLimit() = %d, want %d", got.EffectiveLimit(), tc.wantLimit)
}
var sdkCfg SDKConfig
if errUnmarshal := yaml.Unmarshal([]byte(tc.raw), &sdkCfg); errUnmarshal != nil {
t.Fatalf("decode SDK config: %v", errUnmarshal)
}
if sdkCfg.Client.Codex.WebSearchResultCache != tc.want {
t.Fatalf("SDK WebSearchResultCache = %+v, want %+v", sdkCfg.Client.Codex.WebSearchResultCache, tc.want)
}
data, errMarshal := json.Marshal(cfg)
if errMarshal != nil {
t.Fatalf("encode JSON: %v", errMarshal)
}
var decoded Config
if errUnmarshal := json.Unmarshal(data, &decoded); errUnmarshal != nil {
t.Fatalf("decode JSON: %v", errUnmarshal)
}
if decoded.Client.Codex.WebSearchResultCache != tc.want {
t.Fatalf("JSON round trip WebSearchResultCache = %+v, want %+v", decoded.Client.Codex.WebSearchResultCache, tc.want)
}
migrated, _, errNormalize := NormalizeConfigLayout([]byte(tc.raw), true)
if errNormalize != nil {
t.Fatalf("normalize: %v", errNormalize)
}
if errValidate := ValidateV8Config(migrated); errValidate != nil {
t.Fatalf("validate normalized config: %v", errValidate)
}
if errUnmarshal := yaml.Unmarshal(migrated, &decoded); errUnmarshal != nil {
t.Fatalf("decode normalized config: %v", errUnmarshal)
}
if decoded.Client.Codex.WebSearchResultCache != tc.want {
t.Fatalf("normalized WebSearchResultCache = %+v, want %+v", decoded.Client.Codex.WebSearchResultCache, tc.want)
}
})
}
var fromJSONNumber Config
if errUnmarshal := json.Unmarshal([]byte(`{"client":{"codex":{"web-search-result-cache":{"enabled":true,"ttl":1800,"limit":256}}}}`), &fromJSONNumber); errUnmarshal != nil {
t.Fatalf("unmarshal JSON number ttl: %v", errUnmarshal)
}
if fromJSONNumber.Client.Codex.WebSearchResultCache.EffectiveTTL() != 30*time.Minute || fromJSONNumber.Client.Codex.WebSearchResultCache.EffectiveLimit() != 256 {
t.Fatalf("JSON number ttl config = %+v", fromJSONNumber.Client.Codex.WebSearchResultCache)
}
for _, raw := range []string{
"client: {codex: {web-search-result-cache: false}}\n",
"client: {codex: {web-search-result-cache: {unknown: true}}}\n",
"client: {codex: {web-search-result-cache: {enabled: invalid}}}\n",
"client: {codex: {web-search-result-cache: {ttl: invalid}}}\n",
"client: {codex: {web-search-result-cache: {ttl: -30m}}}\n",
"client: {codex: {web-search-result-cache: {ttl: -10}}}\n",
"client: {codex: {web-search-result-cache: {ttl: true}}}\n",
"client: {codex: {web-search-result-cache: {limit: -1}}}\n",
"client: {codex: {web-search-result-cache: {limit: invalid}}}\n",
} {
if errValidate := ValidateV8Config([]byte(raw)); errValidate == nil {
t.Fatalf("accepted invalid config %q", raw)
}
if _, errParse := ParseConfigBytes([]byte(raw)); errParse == nil {
t.Fatalf("ParseConfigBytes accepted invalid config %q", raw)
}
}
}
func TestClientCodexWebSearchResultCacheSave(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.yaml")
if errWrite := os.WriteFile(path, []byte("config-version: 8\n"), 0600); errWrite != nil {
t.Fatalf("write: %v", errWrite)
}
cfg := &Config{}
for _, want := range []CodexWebSearchResultCacheConfig{
{Enabled: true, TTL: "30m", Limit: 128},
{Enabled: false, TTL: "24h", Limit: 1024},
{Enabled: false, TTL: "", Limit: 0},
} {
cfg.Client.Codex.WebSearchResultCache = want
if errSave := SaveConfigPreserveComments(path, cfg, true); errSave != nil {
t.Fatalf("save: %v", errSave)
}
data, errRead := os.ReadFile(path)
if errRead != nil {
t.Fatalf("read: %v", errRead)
}
if errValidate := ValidateV8Config(data); errValidate != nil {
t.Fatalf("validate saved config: %v", errValidate)
}
loaded, errLoad := LoadConfig(path)
if errLoad != nil {
t.Fatalf("load: %v", errLoad)
}
if loaded.Client.Codex.WebSearchResultCache != want {
t.Fatalf("saved WebSearchResultCache = %+v, want %+v", loaded.Client.Codex.WebSearchResultCache, want)
}
}
}