Files
rond-with-you/internal/web/visitor_pages_test.go
2026-09-18 13:05:49 +08:00

107 lines
3.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package web
import (
"context"
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"
)
// withVisitorRegions 临时给「访客可见区域」配上一项,跑完还原。
//
// 这类 bug 只在访客视角 + 配了区域时才犯(`visitorRegions` 对站长返回空),
// 站长自己点一遍是好的,所以必须专门造这个组合。
func withVisitorRegions(t *testing.T, f *movementFixture) {
t.Helper()
var prev []byte
if err := f.pg.QueryRowContext(f.ctx, `SELECT value FROM settings WHERE key='site'`).Scan(&prev); err != nil {
t.Fatalf("读站点设置失败: %v", err)
}
write := func(v []byte) {
t.Helper()
if _, err := f.pg.ExecContext(f.ctx,
`UPDATE settings SET value=$1::jsonb WHERE key='site'`, string(v)); err != nil {
t.Fatalf("写站点设置失败: %v", err)
}
}
write([]byte(`{"regions":["河池市"],"regions_mode":"blacklist"}`))
t.Cleanup(func() { write(prev) })
}
// TestVisitorPagesWithRegionFilter 用**访客身份**把公开页面走一遍,只要求不 500。
//
// 专为「某个查询漏了区域条件、或者参数与占位符对不上」准备:这类错误犯过两次——
// 一次是 movements 的里程没跟着区域走,一次是专题的「天数」查询把上一条 SQL 的参数
// 整个传进来(多了区域那几个),pgx 报 expected N arguments, got M;
// 而专题列表会把错误吞掉,表现成「专题内容全变 0、点进去才 500」。
// 两次都只在访客视角复现,站长自己试是好的——所以这个组合必须专门测。
func TestVisitorPagesWithRegionFilter(t *testing.T) {
f := newMovementFixture(t)
withVisitorRegions(t, f)
var placeID, topicID int64
if err := f.pg.QueryRowContext(f.ctx, `SELECT id FROM places WHERE dataset_id=$1 ORDER BY id LIMIT 1`,
f.dsID).Scan(&placeID); err != nil {
t.Fatalf("取地点失败: %v", err)
}
// 专题页正是这次踩坑的地方,没有现成的就建一个覆盖几天到访的
if err := f.pg.QueryRowContext(f.ctx, `SELECT id FROM topics WHERE dataset_id=$1 ORDER BY id LIMIT 1`,
f.dsID).Scan(&topicID); err != nil {
if err := f.pg.QueryRowContext(f.ctx, `INSERT INTO topics
(dataset_id, user_id, title, start_at, end_at, enabled)
VALUES ($1,$2,'访客冒烟','2020-01-01T00:00:00+08:00','2030-01-01T00:00:00+08:00',true)
RETURNING id`, f.dsID, userFrom(f.ctx).ID).Scan(&topicID); err != nil {
t.Fatalf("建测试专题失败: %v", err)
}
t.Cleanup(func() {
f.pg.ExecContext(f.ctx, `DELETE FROM topics WHERE id=$1`, topicID)
})
}
pages := []struct {
name string
path string
handle http.HandlerFunc
param string // PathValue("id") 用真实 id 填充
}{
{"主页", "/", f.srv.home, ""},
{"地点库", "/places", f.srv.placesPage, ""},
{"地点详情", "/places/x", f.srv.placePage, strconv.FormatInt(placeID, 10)},
{"统计", "/stats", f.srv.statsPage, ""},
{"时间线", "/timeline", f.srv.timelinePage, ""},
{"年度报告", "/report", f.srv.reportPage, ""},
{"地图", "/map", f.srv.mapPage, ""},
{"专题列表", "/topics", f.srv.topicsListPage, ""},
{"专题详情", "/topics/x", f.srv.topicPage, strconv.FormatInt(topicID, 10)},
{"点位接口", "/api/points", f.srv.apiPoints, ""},
{"轨迹接口", "/api/track", f.srv.apiTrack, ""},
}
for _, p := range pages {
t.Run(p.name, func(t *testing.T) {
req := httptest.NewRequest("GET", p.path, nil)
// 访客 = 上下文里没有登录态(withUser 中间件没跑)
req = req.WithContext(context.Background())
if p.param != "" {
req.SetPathValue("id", p.param)
}
rec := httptest.NewRecorder()
p.handle(rec, req)
if rec.Code >= 500 {
t.Errorf("%s 访客访问返回 %d:%s", p.path, rec.Code, firstLine(rec.Body.String()))
}
})
}
}
func firstLine(s string) string {
if i := strings.IndexByte(s, '\n'); i >= 0 {
s = s[:i]
}
if len(s) > 200 {
s = s[:200]
}
return s
}