// schema 同步测试:保证 config.KVSchema() 的默认值/键集合与 defaults() 完全一致, // 与 settings 包 re-export 的键名常量同源。新增键时任何一处漏改都会在此失败。 package config import ( "encoding/json" "testing" ) // TestKVSchemaDefaultsMatchDefaults KVSchema 的 Default 必须 === defaults() 中同名键。 func TestKVSchemaDefaultsMatchDefaults(t *testing.T) { def := defaults() for _, e := range KVSchema() { want, ok := def[e.Key] if !ok { t.Fatalf("schema 键 %q 缺少 defaults() 默认值", e.Key) } // 类型规范化比较(JSON 序列化可比较 []string / int / float) a, _ := json.Marshal(e.Default) b, _ := json.Marshal(want) if string(a) != string(b) { t.Fatalf("键 %q 默认值不一致: schema=%s defaults=%s", e.Key, a, b) } } } // TestKVSchemaNoDuplicates 键名不得重复。 func TestKVSchemaNoDuplicates(t *testing.T) { seen := map[string]bool{} for _, e := range KVSchema() { if seen[e.Key] { t.Fatalf("schema 键 %q 重复定义", e.Key) } seen[e.Key] = true } } // TestV2NewKeysPresent v2 新增键必须在 schema 与 defaults 中同时存在。 func TestV2NewKeysPresent(t *testing.T) { def := defaults() newKeys := []string{ KeyBackgroundURL, KeyFooterText, KeyFooterBeian, KeyNotifyEnabled, KeyMaxSaveCount, KeyMaxFileSize, } for _, k := range newKeys { if _, ok := def[k]; !ok { t.Fatalf("v2 新键 %q 缺少默认值", k) } } } // TestV2AccessorDefaults v2 便捷访问器默认语义。 func TestV2AccessorDefaults(t *testing.T) { t.Setenv("FCB_DB_DRIVER", "sqlite") t.Setenv("FCB_DB_DSN", "") c, err := New() if err != nil { t.Fatalf("New: %v", err) } // 背景图:background_url 与 background 均空 → 空 if c.BackgroundURL() != "" { t.Fatalf("背景图默认应为空: %q", c.BackgroundURL()) } // legacy background 键兜底 c.ApplyKV(map[string]any{KeyBackground: "/legacy/bg.jpg"}) if c.BackgroundURL() != "/legacy/bg.jpg" { t.Fatalf("legacy background 应回落生效: %q", c.BackgroundURL()) } // max_file_size > 0 时优先于 uploadSize c.ApplyKV(map[string]any{KeyMaxFileSize: int64(1024), KeyUploadSize: int64(2048)}) if c.MaxFileSize() != 1024 { t.Fatalf("max_file_size 应优先: %d", c.MaxFileSize()) } // max_file_size = 0 回落 uploadSize c.ApplyKV(map[string]any{KeyMaxFileSize: int64(0)}) if c.MaxFileSize() != 2048 { t.Fatalf("max_file_size=0 应回落 uploadSize: %d", c.MaxFileSize()) } // 通知默认开启 if !c.NotifyEnabled() { t.Fatal("notify_enabled 默认应开启") } // 保存次数上限默认不限制 if c.MaxSaveCount() != 0 { t.Fatalf("max_save_count 默认应 0: %d", c.MaxSaveCount()) } // 页脚默认空 if c.FooterText() != "" || c.FooterBeian() != "" { t.Fatal("页脚默认应为空") } }