first release

This commit is contained in:
tamakyi
2026-09-18 13:05:49 +08:00
commit b327bc1230
123 changed files with 30003 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
package web
import (
"fmt"
"net/http/httptest"
"net/url"
"strings"
"testing"
)
// TestNewRowNumberingSkipsArchivedPKs 盯住「站点新建行的编号」。
//
// 编号不能只看 `MAX(places.src_pk)`:`entity_raw` 里还躺着导入时缺必填字段的 skipped 行
// (导出要把它们原样补回去,所以一直留着),那些号没有对应实体行却占着号位。
// 只看 places 的话,新行会拿到一个已经被留档占用的号 —— 导出时被当成「已存在的行」走 UPDATE,
// 于是继承对方 rond 自己写的那些列(ZRADIUS / ZTYPE_ / ZNOTE_…)。
//
// 这里造一条「只有留档、没有实体行」的 ZLOCATION,再分别走两条新建路径,断言编号绕开它。
func TestNewRowNumberingSkipsArchivedPKs(t *testing.T) {
f := newMovementFixture(t)
// 与 nextSrcPK 同一套口径,先算出「当前有效上限」
var cur int
if err := f.pg.QueryRowContext(f.ctx, `SELECT GREATEST(
(SELECT COALESCE(MAX(src_pk),0) FROM places WHERE dataset_id=$1),
COALESCE((coredata_meta->'entities'->'Location'->>'max')::int,0),
(SELECT COALESCE(MAX((raw->>'Z_PK')::numeric),0)::int FROM entity_raw
WHERE dataset_id=$1 AND entity='ZLOCATION' AND (raw->>'Z_PK') ~ '^[0-9]+$')
) FROM datasets WHERE id=$1`, f.dsID).Scan(&cur); err != nil {
t.Fatalf("算编号上限失败: %v", err)
}
stalePK := cur + 3
// 留档存的是整行 Core Data 记录,Z_PK 就在里面——nextSrcPK 正是靠它算上限
f.mustExec(t, `INSERT INTO entity_raw (dataset_id, entity, src_pk, raw, skipped)
VALUES ($1,'ZLOCATION',$2,$3::jsonb,true)`, f.dsID, stalePK,
fmt.Sprintf(`{"Z_PK":%d,"ZLATITUDE":30.0,"ZLONGITUDE":120.0}`, stalePK))
post := func(path string, form url.Values) {
t.Helper()
req := httptest.NewRequest("POST", path, strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req = req.WithContext(f.ctx)
rec := httptest.NewRecorder()
switch path {
case "/admin/edit/place":
f.srv.editPlaceCreate(rec, req)
case "/admin/photos/import":
f.srv.adminPhotosImport(rec, req)
default:
t.Fatalf("未接入的处理函数: %s", path)
}
if rec.Code >= 400 {
t.Fatalf("%s 返回 %d: %s", path, rec.Code, rec.Body.String())
}
}
srcPKOf := func(name string) int {
t.Helper()
var pk int
if err := f.pg.QueryRowContext(f.ctx, `SELECT src_pk FROM places
WHERE dataset_id=$1 AND name=$2`, f.dsID, name).Scan(&pk); err != nil {
t.Fatalf("地点 %q 没落库: %v", name, err)
}
return pk
}
// 路径一:后台「新增地点」
post("/admin/edit/place", url.Values{
"mode": {"new"}, "name": {"编号测试点-手工"}, "lat": {"30.11"}, "lon": {"120.11"},
})
if got := srcPKOf("编号测试点-手工"); got <= stalePK {
t.Errorf("手工新增的编号 %d 没绕开留档占用的 %d(旧实现会拿到 %d)", got, stalePK, cur+1)
}
// 路径二:照片导入(同一套规则)
post("/admin/photos/import", url.Values{
"n": {"1"}, "coord": {"wgs84"}, "keep_0": {"on"},
"lat_0": {"30.22"}, "lon_0": {"120.22"},
"arrival_0": {"2026-04-18T18:14"}, "name_0": {"编号测试点-照片"},
"country_0": {"CN"}, "timezone_0": {"Asia/Shanghai"},
})
if got := srcPKOf("编号测试点-照片"); got <= stalePK {
t.Errorf("照片导入的编号 %d 没绕开留档占用的 %d", got, stalePK)
}
// 留档那条本身还在(newSrcPK 绕开了它,就不该被清掉)
var n int
if err := f.pg.QueryRowContext(f.ctx, `SELECT count(*) FROM entity_raw
WHERE dataset_id=$1 AND entity='ZLOCATION' AND src_pk=$2`, f.dsID, stalePK).Scan(&n); err != nil {
t.Fatal(err)
}
if n != 1 {
t.Errorf("留档行被误清:期望 1 行,实际 %d", n)
}
}