aliyun sensor server
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
gotemplate "html/template"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -12,9 +14,37 @@ import (
|
||||
|
||||
"github.com/tamakyi/TamaBox/internal/conf"
|
||||
"github.com/tamakyi/TamaBox/internal/db"
|
||||
"github.com/tamakyi/TamaBox/internal/i18n"
|
||||
"github.com/tamakyi/TamaBox/internal/security/moderation"
|
||||
templatepkg "github.com/tamakyi/TamaBox/internal/template"
|
||||
)
|
||||
|
||||
// 审核严格度下拉框的解析:不依赖属性的书写顺序或是否存在额外属性
|
||||
// (模板可能被页面编辑器注入 data-* 属性),因此用正则而不是精确子串匹配。
|
||||
var (
|
||||
strictnessOptionRe = regexp.MustCompile(`<option value="(lax|standard|strict)"([^>]*)>`)
|
||||
selectedAttrRe = regexp.MustCompile(`\bselected\b`)
|
||||
)
|
||||
|
||||
// selectedStrictnessOptions 返回渲染结果中被标记 selected 的档位值(去重后升序)。
|
||||
// 三个面板各渲染一份下拉框,正常情况下应得到恰好一个值;返回多个说明面板之间不一致。
|
||||
func selectedStrictnessOptions(out string) []string {
|
||||
seen := map[string]struct{}{}
|
||||
var values []string
|
||||
for _, m := range strictnessOptionRe.FindAllStringSubmatch(out, -1) {
|
||||
if !selectedAttrRe.MatchString(m[2]) {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[m[1]]; ok {
|
||||
continue
|
||||
}
|
||||
seen[m[1]] = struct{}{}
|
||||
values = append(values, m[1])
|
||||
}
|
||||
sort.Strings(values)
|
||||
return values
|
||||
}
|
||||
|
||||
// renderWithData 用给定数据渲染模板(复刻生产环境 Templater + FuncMap)。
|
||||
func renderWithData(t *testing.T, name string, data map[string]interface{}) error {
|
||||
t.Helper()
|
||||
@@ -73,6 +103,8 @@ type mockLoggedUser struct {
|
||||
Language string
|
||||
HarassmentSetting string
|
||||
BlockWords string
|
||||
// 审核严格度档位(lax / standard / strict),设置页三个功能面板共用一份。
|
||||
ModerationStrictness string
|
||||
Tags string
|
||||
ShowInHome bool
|
||||
Acfunlink string
|
||||
@@ -97,7 +129,8 @@ func profileBaseData() map[string]interface{} {
|
||||
Qrcodepdpcolor: "#000", Qrcodebackcolor: "#fff", Dotscale: "0.35",
|
||||
Backgroundimagealpha: "0.8", Notify: "email", Language: "zh-CN",
|
||||
HarassmentSetting: "none", BlockWords: "广告,spam", Tags: "杂谈",
|
||||
ChatMaxMessages: 20, DailyMessageLimit: 0,
|
||||
ModerationStrictness: "standard",
|
||||
ChatMaxMessages: 20, DailyMessageLimit: 0,
|
||||
},
|
||||
"CSRFTokenHTML": gotemplate.HTML(`<input type="hidden" name="_csrf" value="x">`),
|
||||
"IsUserPage": false,
|
||||
@@ -207,6 +240,26 @@ func TestRenderUserProfileTabs(t *testing.T) {
|
||||
t.Fatalf("account: 屏蔽词输入缺 data-tb-max 注入,want %q", wantBlock)
|
||||
}
|
||||
}
|
||||
// 审核严格度:提问 / 私聊 / 投票三个面板都必须渲染同一份设置,且把每个档位的
|
||||
// 拦截范围平铺展示出来 —— 要求是用户按下保存前就知道哪些内容会被拦、哪些会放行。
|
||||
if tab == "question" || tab == "chat" || tab == "vote" {
|
||||
if got := selectedStrictnessOptions(out); len(got) != 1 || got[0] != "standard" {
|
||||
t.Fatalf("%s: 审核严格度未按当前档位渲染 selected 状态,got %v", tab, got)
|
||||
}
|
||||
for _, key := range []string{
|
||||
"admin.moderationStrictnessScope.lax",
|
||||
"admin.moderationStrictnessScope.standard",
|
||||
"admin.moderationStrictnessScope.strict",
|
||||
} {
|
||||
if want := i18n.T("zh-CN", key); !strings.Contains(out, want) {
|
||||
t.Fatalf("%s: 档位拦截范围缺失 %s,want %q", tab, key, want)
|
||||
}
|
||||
}
|
||||
// 底线拦截是强制项、任何档位不可关闭,界面上必须明确告知。
|
||||
if want := i18n.T("zh-CN", "admin.moderationStrictnessBaseline"); !strings.Contains(out, want) {
|
||||
t.Fatalf("%s: 缺少底线拦截说明,want %q", tab, want)
|
||||
}
|
||||
}
|
||||
if tab == "chat" {
|
||||
// 私聊面板的 number 输入框 min/max 与 conf.Limits 同源
|
||||
wantChat := fmt.Sprintf(`name="chat_max_messages" class="uk-input uk-width-small" type="number" min="1" max="%d"`, conf.Limits.ChatMaxMessages)
|
||||
@@ -232,6 +285,46 @@ func TestRenderUserProfileTabs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestRenderProfileStrictnessReflectsEffectiveTier 覆盖「档位回显」这个易错点。
|
||||
//
|
||||
// 存量账号的 moderation_strictness 为空值(语义是"跟随站点默认")。若模板直接拿该字段值
|
||||
// 去和三个 <option> 逐个比较,会全部不匹配 —— 浏览器随后默认选中第一项「宽松」,
|
||||
// 界面显示的档位就与真实生效的档位(站点默认,通常为严格)相反,把箱主误导到最松的一档。
|
||||
// 因此回显必须走「最终生效档位」。
|
||||
func TestRenderProfileStrictnessReflectsEffectiveTier(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
stored string
|
||||
want []string
|
||||
}{
|
||||
{name: "未设置:回显生效档位,而非浏览器默认选中的宽松", stored: "", want: []string{string(moderation.StrictnessFor(""))}},
|
||||
{name: "宽松", stored: "lax", want: []string{"lax"}},
|
||||
{name: "标准", stored: "standard", want: []string{"standard"}},
|
||||
{name: "严格", stored: "strict", want: []string{"strict"}},
|
||||
{name: "非法值:回退严格", stored: "bogus", want: []string{string(moderation.StrictnessFor("bogus"))}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
base := profileBaseData()
|
||||
base["ActiveProfileTab"] = "question"
|
||||
base["LoggedUser"].(*mockLoggedUser).ModerationStrictness = tc.stored
|
||||
|
||||
out, err := renderToString(t, "user/profile", base)
|
||||
if err != nil {
|
||||
t.Fatalf("render user/profile failed: %v", err)
|
||||
}
|
||||
|
||||
got := selectedStrictnessOptions(out)
|
||||
if fmt.Sprint(got) != fmt.Sprint(tc.want) {
|
||||
t.Fatalf("档位回显不符:stored=%q got=%v want=%v", tc.stored, got, tc.want)
|
||||
}
|
||||
// 核心回归点:未设置时绝不能显示为「宽松」,那是浏览器默认选中首项的陷阱。
|
||||
if tc.stored == "" && len(got) == 1 && got[0] == "lax" {
|
||||
t.Fatal("未设置档位时回显为「宽松」—— 与真实生效档位相反")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestRenderUserQuestionList 模拟 /user/questions 的数据渲染,复现 500 错误。
|
||||
func TestRenderUserQuestionList(t *testing.T) {
|
||||
type mockItem struct {
|
||||
|
||||
Reference in New Issue
Block a user