package web import ( "html/template" "path/filepath" "strings" "testing" "rond-with-you/internal/config" ) // TestSmokeRenderAllPages 把启动自检搬进 go test:每个页面都要能用「最小可用数据」渲染到底。 // // 比 TestPageTemplatesParse 更强的地方在于**执行**:模板能解析不代表渲染得完, // 「字段不存在 / 对着 nil 解引用」只在执行期暴露,而线上表现是「HTTP 200 + 半张页面」。 // 2026-10-09 的 /admin/settings 截断就是这么来的(新模板引用了旧结构体里没有的字段)。 func TestSmokeRenderAllPages(t *testing.T) { srv := &Server{cfg: &config.Config{}} pages := make(map[string]pageSet, len(pageFiles)) for key, def := range pageFiles { tpl, err := template.New(filepath.Base(def.files[0])).Funcs(templateFuncs()). ParseFS(tmplFS, def.files...) if err != nil { t.Fatalf("解析模板 %s 失败: %v", key, err) } pages[key] = pageSet{t: tpl, entry: def.entry} } srv.pages = pages if err := srv.smokeRender(); err != nil { t.Fatal(err) } // 定义性不变量:pageFiles 里每加一个页面,就必须在 pageProbe 里配一份自检数据, // 否则那一页会悄悄逃过自检(smokeRender 会把缺项报成失败,这里再独立钉一次)。 for key := range pageFiles { if _, ok := pageProbe[key]; !ok { t.Errorf("页面 %s 没有自检数据(pageProbe 缺项)——新加页面时要一起补", key) } } for key := range pageProbe { if _, ok := pageFiles[key]; !ok { t.Errorf("pageProbe 里的 %s 不是已注册页面(改名后忘了同步?)", key) } } } // TestSmokeRenderCatchesFieldDrift 变异验证:把页面数据换成**字段更少**的类型时必须报错。 // // 光断言「当前代码自检通过」证明不了自检有效——它可能只是什么都没检查。 // 这里用 Page 顶替 SettingsData(模板引用的 .Modules / .S3Info 都不在 Page 上), // 自检必须红,且错误信息里要能看到是哪一页。 func TestSmokeRenderCatchesFieldDrift(t *testing.T) { srv := &Server{cfg: &config.Config{}} pages := make(map[string]pageSet, len(pageFiles)) for key, def := range pageFiles { tpl, err := template.New(filepath.Base(def.files[0])).Funcs(templateFuncs()). ParseFS(tmplFS, def.files...) if err != nil { t.Fatalf("解析模板 %s 失败: %v", key, err) } pages[key] = pageSet{t: tpl, entry: def.entry} } srv.pages = pages orig := pageProbe["admin/settings"] pageProbe["admin/settings"] = func() any { return probePage("admin") } t.Cleanup(func() { pageProbe["admin/settings"] = orig }) err := srv.smokeRender() if err == nil { t.Fatal("页面数据缺字段时自检必须报错,否则它什么也挡不住") } if !strings.Contains(err.Error(), "admin/settings") { t.Errorf("自检报错要指名是哪个页面,实际为:%v", err) } }