项目文件夹

文件
杭州明婳科技 08ed43385d fix(config): 模块 01 评审修复 + 对抗复核
config 模块(glm_check_report_module_01_config.md)11 项 finding 修复:

- H-config-1: Set(cfg) 用 mapstructure 重建 m.v,消除 Get/GetString/viper 视图分裂(C1)
- M-config-1: 热重载回调独立 recover,panic 不杀 watcher、不阻断后续回调(C9)
- M-config-2: DB SSL/TLS -- Postgres SSLMode(默认 prefer)+ MySQL TLS/TLSRootCA;
  database.ensureMySQLTLSRegistered 注册私有 CA 命名配置,fail-fast 不回退明文。
  O-1 文档准确性修正:同名注册会覆盖 master,改为 per-host 不同名 + 自建 replica DSN 指引
- M-config-3: App.Shutdown closeResources 停 configManager watcher(C7)
- M-config-4: Clone 切片/map 字段反射守卫测试
- M-config-5: WithConfig/WithConfigPath 全局可见性文档强化
- L-config-2: watchLoop 增 ctx 逃生通道,StopWatcher cancel+Close 双重退出
- L-config-3/4/5/6: Validate 连接池交叉校验、哨兵 errors.New、去冗余 TrimSpace、nil receiver 防御

含契约级回归测试(同源一致性/生命周期闭环/扩展点防御),-race/vet/build 全绿。
附 module 01 评审报告 + 全局结构图 + 扫描收敛协议。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 12:54:52 +08:00

61 行
1.9 KiB
Go

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
package xlgo_test
import (
"os"
"path/filepath"
"runtime"
"testing"
"time"
xlgo "github.com/EthanCodeCraft/xlgo-core"
"github.com/EthanCodeCraft/xlgo-core/config"
)
// TestAppShutdownStopsConfigWatcher_Mconfig3 固化 M-config-3App.Shutdown 须停止其
// configManager 的热重载 watcher,避免关闭后遗留监听 goroutine(违反 C7 生命周期契约)。
//
// Init 后 resolveConfig 把 a.configManager 提升为全局默认,故经包级 config.StartWatcher
// 在其上启动 watcher(等价于用户对 configManager 调 LoadWithWatch/StartWatcher);
// Shutdown 后断言 watchLoop goroutine 已退出。
func TestAppShutdownStopsConfigWatcher_Mconfig3(t *testing.T) {
dir := filepath.Join(os.TempDir(), "xlgo_app_mconfig3")
if err := os.MkdirAll(dir, 0755); err != nil {
t.Fatalf("MkdirAll: %v", err)
}
defer os.RemoveAll(dir)
cfgPath := filepath.Join(dir, "config.yaml")
if err := os.WriteFile(cfgPath, []byte("app:\n name: m3\n env: dev\nserver:\n port: 18080\n"), 0644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
app := xlgo.New(xlgo.WithConfigPath(cfgPath))
if err := app.Init(); err != nil {
t.Fatalf("Init: %v", err)
}
// 经全局默认(=a.configManager)启动 watcher,模拟用户开启热重载
if err := config.StartWatcher(); err != nil {
t.Fatalf("StartWatcher: %v", err)
}
defer config.SetDefaultManager(nil)
time.Sleep(120 * time.Millisecond) // 等 watchLoop 就绪
before := runtime.NumGoroutine()
if err := app.Shutdown(); err != nil {
t.Fatalf("Shutdown: %v", err)
}
// watchLoop goroutine 应随 Shutdown 退出
deadline := time.Now().Add(2 * time.Second)
for time.Now().Before(deadline) {
if runtime.NumGoroutine() < before {
break
}
time.Sleep(20 * time.Millisecond)
}
if after := runtime.NumGoroutine(); after >= before {
t.Errorf("App.Shutdown 未停止 config watcherM-config-3: before=%d after=%d", before, after)
}
}