08ed43385d
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>
61 lines
1.9 KiB
Go
61 lines
1.9 KiB
Go
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-3:App.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 watcher(M-config-3): before=%d after=%d", before, after)
|
||
}
|
||
}
|