Files
FileShare/server/internal/settings/manager_test.go
T
SKYMirror 9686fe887a FileCodeBox Go 重写版 v2.5.6(安全审计修复版)
Go 1.27.1 (Gin+GORM) + Vue 3 文件快传服务:

- 安全审计全部修复(docs/security-audit-2026-09-05.md):
  bcrypt 密码哈希与自动升级、presign 直传服务端大小/内容校验、
  全局请求体上限、依赖升级(govulncheck 0 命中)、janitor 后台清理、
  管理端审计动作落库、/admin CORS 收紧、通知内容白名单净化、
  会话默认 7 天、限流缓存故障降级、robots.txt 端点等
- 前端:取件链接复制修复(不再重复拼接提取码)、markdown 净化器加固
- Redis 支持库号(FCB_REDIS_DB / redis://…/db URL)
- 文档:docs/api/* 与 openapi.yaml 同步最新行为(robots.txt、
  提码 5 位起、chunk 32MiB 上限、admin 审计动作等)

验证:gofmt/go vet/go test 全绿;二进制端到端冒烟通过
2026-09-05 04:22:41 +08:00

151 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// settings 包双方言测试:Manager 全流程(ensure 行、KV 读写合并、Reload、
// UpdateKV 屏蔽内部键、SystemStart)分别在 sqlite(默认)与 postgresFCB_TEST_PG_DSN)上执行。
package settings_test
import (
"context"
"os"
"path/filepath"
"testing"
"gorm.io/gorm"
"filecodebox/internal/config"
"filecodebox/internal/database"
"filecodebox/internal/settings"
)
// pgTestDSN 返回 Postgres 测试连接串;未设置 FCB_TEST_PG_DSN 时跳过。
func pgTestDSN(t *testing.T) string {
t.Helper()
dsn := os.Getenv("FCB_TEST_PG_DSN")
if dsn == "" {
t.Skip("未设置 FCB_TEST_PG_DSN,跳过 Postgres 双方言用例")
}
return dsn
}
// newTestManager 按方言构造库 + Manager(已完成 Migrate)。
func newTestManager(t *testing.T, driver, dsn string) (*settings.Manager, *gorm.DB, func()) {
t.Helper()
if dsn == "" {
dsn = filepath.Join(t.TempDir(), "settings-test.db")
}
ctx := context.Background()
db, err := database.Open(ctx, database.Options{Driver: driver, DSN: dsn})
if err != nil {
t.Fatalf("[%s] Open: %v", driver, err)
}
if err := database.Migrate(ctx, db); err != nil {
_ = database.Close(db)
t.Fatalf("[%s] Migrate: %v", driver, err)
}
// Config 仅作内存载体(驱动不回连数据库),postgres 模式给占位 DSN 以通过校验
t.Setenv("FCB_DB_DRIVER", driver)
if driver == "postgres" {
t.Setenv("FCB_DB_DSN", dsn)
} else {
t.Setenv("FCB_DB_DSN", "")
}
cfg, err := config.New()
if err != nil {
_ = database.Close(db)
t.Fatalf("[%s] config.New: %v", driver, err)
}
mgr, err := settings.NewManager(ctx, db, cfg)
if err != nil {
_ = database.Close(db)
t.Fatalf("[%s] NewManager: %v", driver, err)
}
return mgr, db, func() { _ = database.Close(db) }
}
// runManagerSuite 双方言共用的 Manager 行为断言。
func runManagerSuite(t *testing.T, mgr *settings.Manager) {
t.Helper()
ctx := context.Background()
// 1. 初始:未初始化(admin_token 空)
if mgr.IsInitialized() {
t.Fatal("初始 admin_token 为空应视为未初始化")
}
// 2. UpdateKV 写入策略键 → Reload 后读取生效
patch := map[string]any{
settings.KeyBackgroundURL: "https://example.com/bg.png",
settings.KeyFooterText: "自建部署,仅供内部演示",
settings.KeyFooterBeian: "京ICP备2024000001号-1",
settings.KeyNotifyEnabled: 0,
settings.KeyMaxSaveSeconds: 86400,
"_internal_secret": "must-drop", // 下划线内部键必须被拒
}
if err := mgr.UpdateKV(ctx, patch); err != nil {
t.Fatalf("UpdateKV 失败: %v", err)
}
if err := mgr.Reload(ctx); err != nil {
t.Fatalf("Reload 失败: %v", err)
}
cfg := mgr.Get()
if got := cfg.GetString(settings.KeyBackgroundURL); got != "https://example.com/bg.png" {
t.Fatalf("background_url 未生效: %q", got)
}
if got := cfg.GetString(settings.KeyFooterBeian); got != "京ICP备2024000001号-1" {
t.Fatalf("footer_beian 未生效: %q", got)
}
if cfg.GetBool(settings.KeyNotifyEnabled) {
t.Fatal("notify_enabled=0 应生效")
}
if got := cfg.MaxSaveSeconds(); got != 86400 {
t.Fatalf("max_save_seconds 未生效: %d", got)
}
if _, ok := cfg.Get("_internal_secret"); ok {
t.Fatal("下划线内部键不应进入运行时配置")
}
// 3. KV 合并语义:二次 UpdateKV 不覆盖未提及键
if err := mgr.UpdateKV(ctx, map[string]any{settings.KeyNotifyEnabled: 1}); err != nil {
t.Fatalf("二次 UpdateKV: %v", err)
}
if err := mgr.Reload(ctx); err != nil {
t.Fatalf("二次 Reload: %v", err)
}
cfg = mgr.Get()
if !cfg.GetBool(settings.KeyNotifyEnabled) {
t.Fatal("notify_enabled 二次写入应生效")
}
if got := cfg.GetString(settings.KeyFooterText); got == "" {
t.Fatal("二次写入不应清空 footer_text")
}
// 4. SystemStartsys_start 键写入且为毫秒时间戳
mgr.SystemStart(ctx)
// 5. 敏感键判定(双模式一致)
if !settings.IsSensitiveKey("admin_token") || !settings.IsSensitiveKey("jwt_secret") {
t.Fatal("admin_token/jwt_secret 应为敏感键")
}
if settings.IsSensitiveKey("footer_text") {
t.Fatal("footer_text 不应为敏感键")
}
// 6. KV schema 表完整性:全部键可从默认值读取
for _, e := range settings.KVSchema() {
if _, ok := cfg.Get(e.Key); !ok {
t.Fatalf("schema 键 %q 在默认配置中不存在", e.Key)
}
}
}
func TestManagerSQLite(t *testing.T) {
mgr, _, closeFn := newTestManager(t, "sqlite", "")
defer closeFn()
runManagerSuite(t, mgr)
}
func TestManagerPostgres(t *testing.T) {
dsn := pgTestDSN(t)
mgr, _, closeFn := newTestManager(t, "postgres", dsn)
defer closeFn()
runManagerSuite(t, mgr)
}