95 lines
3.7 KiB
Go
95 lines
3.7 KiB
Go
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)
|
||
}
|
||
}
|