package web import ( "os" "strings" "testing" ) // ---------- F5:地图脚本里的 localStorage 访问必须走兜底 ---------- // // 隐私模式 / 禁用 Cookie 的浏览器里 localStorage 读写都会抛异常,裸调用会把整段脚本打断 // (地图白屏、图层不出来,且控制台之外看不出来)。统一走 lsGet/lsSet。 func TestMapLocalStorageGoesThroughGuard(t *testing.T) { b, err := staticFS.ReadFile("static/js/rond-map.js") if err != nil { t.Fatalf("读取 rond-map.js 失败: %v", err) } src := string(b) if !strings.Contains(src, "function lsGet(k)") || !strings.Contains(src, "function lsSet(k, v)") { t.Fatal("rond-map.js 里找不到 lsGet/lsSet 兜底(写法变了,这条守卫失效)") } if n := strings.Count(src, "window.localStorage."); n != 2 { t.Errorf("裸 localStorage 访问应只剩 lsGet/lsSet 里那 2 处,实际 %d 处"+ "(其余一律要走 lsGet/lsSet,否则隐私模式下整段脚本会抛异常)", n) } } // ---------- F6:配置未就绪时的第二次 preview 不能丢点 ---------- func TestMapPreviewQueuesPendingPoints(t *testing.T) { b, err := staticFS.ReadFile("static/js/rond-map.js") if err != nil { t.Fatalf("读取 rond-map.js 失败: %v", err) } src := string(b) body := funcBody(src, "preview: function (opts)") if body == "" { t.Fatal("在 rond-map.js 里找不到 preview(写法变了,这条守卫失效)") } queue := strings.Index(body, "previewQueue[opts.id] = points;") live := strings.Index(body, "if (live) {") if queue < 0 || live < 0 || queue > live { t.Error("preview:必须在 `if (live)` 之前就把本次的点记进 previewQueue," + "否则配置未就绪时第二次 preview 的点会被静默丢掉") } if !strings.Contains(body, "if (previewQueue[opts.id]) points = previewQueue[opts.id];") { t.Error("preview:配置回调里没把 previewQueue 排队的点取回来画") } if strings.Contains(src, "previewPending") { t.Error("rond-map.js 里不该再有 previewPending(已由 previewQueue 取代)") } } // ---------- F8:EXIF 全 0 时间不能变成 1899-11-30 ---------- func TestExifRejectsOutOfRangeDate(t *testing.T) { b, err := staticFS.ReadFile("static/js/photo-exif.js") if err != nil { t.Fatalf("读取 photo-exif.js 失败: %v", err) } src := string(b) guard := strings.Index(src, "Y >= 1900") mk := strings.Index(src, "res.shot = new Date(Y, Mo - 1, D, H, Mi, S);") flag := strings.Index(src, "res.hasTime = true;") if guard < 0 || mk < 0 || flag < 0 || guard > mk || guard > flag { t.Error("photo-exif.js:拍摄时间必须先过范围校验再赋给 res.shot / hasTime" + "(否则「0000:00:00」会被 Date 归一成 1899-11-30,被当成到达时间写进库)") } } // ---------- F9:findExifItemID 读 count 前必须卡住宽度 ---------- func TestExifItemIDChecksBoundsBeforeRead(t *testing.T) { b, err := staticFS.ReadFile("static/js/photo-exif.js") if err != nil { t.Fatalf("读取 photo-exif.js 失败: %v", err) } body := funcBody(string(b), "function findExifItemID(") if body == "" { t.Fatal("在 photo-exif.js 里找不到 findExifItemID(写法变了,这条守卫失效)") } bounds := strings.Index(body, "p + 2 > u8.length") read := strings.Index(body, "dv.getUint16(p)") if bounds < 0 || read < 0 || bounds > read { t.Error("findExifItemID:读 count 之前必须按 ver 卡住宽度(p+2 / p+4)——" + "末尾恰好相等(start+4 == length)时旧守卫会放行,越界会抛 RangeError") } } // ---------- F16:数据编辑页每个筛选表单都要带上全部筛选条件 ---------- // // 三个表单各自只放自己那几个控件,其余条件靠隐藏域带着;漏一个,用户一提交 // 就会把别的列表的筛选悄悄清掉(原来「到访」表单只带 vq+back)。 func TestEditSearchFormsCarryAllFilters(t *testing.T) { b, err := tmplFS.ReadFile("templates/admin/edit.html") if err != nil { t.Fatalf("读取 edit.html 失败: %v", err) } src := string(b) const open = `