Files
qitongxue-api/internal/logic/event_import_test.go
2026-09-29 10:57:01 +08:00

150 lines
5.4 KiB
Go
Raw 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 logic
import (
"testing"
"tool-api/internal/consts"
)
// TestClassifyImportRows 覆盖判据:同号同名→update、同名异号→conflict、缺号/格式错→error、新号→new。
func TestClassifyImportRows(t *testing.T) {
existing := []existingEmployee{
{Id: 1, Name: "李四", Phone: "13900000002"},
{Id: 2, Name: "王五", Phone: "13700000003"},
}
rows := []importRow{
{Name: "张三", Phone: "13800000001"}, // new
{Name: "李四", Phone: "13900000002"}, // update(同手机号)
{Name: "王五", Phone: "18800000009"}, // conflict(同名不同号)
{Name: "赵六", Phone: ""}, // error(缺号)
{Name: "钱七", Phone: "abc123"}, // error(格式非法)
}
got := classifyImportRows(rows, existing)
want := []string{
consts.ImportTypeNew,
consts.ImportTypeUpdate,
consts.ImportTypeConflict,
consts.ImportTypeError,
consts.ImportTypeError,
}
if len(got) != len(want) {
t.Fatalf("len=%d want %d", len(got), len(want))
}
for i := range want {
if got[i].Type != want[i] {
t.Fatalf("row %d type=%s want %s", i, got[i].Type, want[i])
}
}
if got[1].MatchedEmployeeId != 1 {
t.Fatalf("update 应命中现有员工 id=1,got %d", got[1].MatchedEmployeeId)
}
if got[2].MatchedEmployeeId != 2 {
t.Fatalf("conflict 应命中同名员工 id=2,got %d", got[2].MatchedEmployeeId)
}
}
// TestClassifyImportRowsPhoneNormalized 手机号去空格后再判重。
func TestClassifyImportRowsPhoneNormalized(t *testing.T) {
existing := []existingEmployee{{Id: 7, Name: "李四", Phone: "13900000002"}}
got := classifyImportRows([]importRow{{Name: "李四", Phone: " 139 0000 0002 "}}, existing)
if got[0].Type != consts.ImportTypeUpdate || got[0].MatchedEmployeeId != 7 {
t.Fatalf("去空格后应命中同一人,got type=%s id=%d", got[0].Type, got[0].MatchedEmployeeId)
}
}
// TestPlanImportOpsUnresolvedConflict 未裁决冲突 → unresolved>0(确认接口据此返回 4008)。
func TestPlanImportOpsUnresolvedConflict(t *testing.T) {
rows := []classifiedRow{
{Idx: 0, Type: consts.ImportTypeConflict, Name: "王五", Phone: "18800000009", MatchedEmployeeId: 2},
{Idx: 1, Type: consts.ImportTypeNew, Name: "张三", Phone: "13800000001"},
{Idx: 2, Type: consts.ImportTypeError, Name: "赵六", Phone: ""},
{Idx: 3, Type: consts.ImportTypeUpdate, Name: "李四", Phone: "13900000002", MatchedEmployeeId: 1},
}
ops, unresolved := planImportOps(rows, nil)
if unresolved != 1 {
t.Fatalf("未裁决冲突计数=%d want 1", unresolved)
}
for _, op := range ops {
if op.RowIdx == 0 {
t.Fatalf("未裁决的冲突行不应产生操作,got %v", op)
}
}
}
// TestPlanImportOpsMergeCreateIgnore 覆盖三种裁决各自的结果。
func TestPlanImportOpsMergeCreateIgnore(t *testing.T) {
base := []classifiedRow{
{Idx: 0, Type: consts.ImportTypeConflict, Name: "王五", Phone: "18800000009", MatchedEmployeeId: 2},
}
// merge → update 现有员工(含更新手机号)
ops, unresolved := planImportOps(base, map[int]string{0: consts.ImportActionMerge})
if unresolved != 0 || len(ops) != 1 {
t.Fatalf("merge: unresolved=%d ops=%d", unresolved, len(ops))
}
if ops[0].Kind != "update" || ops[0].UpdateId != 2 || ops[0].Phone != "18800000009" {
t.Fatalf("merge 应为 update 更新 id=2 且手机号为新号,got %+v", ops[0])
}
// create → insert(手机号不同,不违反 uk_ent_phone)
ops, unresolved = planImportOps(base, map[int]string{0: consts.ImportActionCreate})
if unresolved != 0 || len(ops) != 1 || ops[0].Kind != "insert" {
t.Fatalf("create 应为 insert,got unresolved=%d ops=%+v", unresolved, ops)
}
// ignore → skip
ops, unresolved = planImportOps(base, map[int]string{0: consts.ImportActionIgnore})
if unresolved != 0 || len(ops) != 1 || ops[0].Kind != "skip" {
t.Fatalf("ignore 应为 skip,got unresolved=%d ops=%+v", unresolved, ops)
}
// 非法动作 → 视为未裁决
_, unresolved = planImportOps(base, map[int]string{0: "whatever"})
if unresolved != 1 {
t.Fatalf("非法裁决动作应按未裁决处理,got unresolved=%d", unresolved)
}
}
// TestReimportSameFileAllUpdate 覆盖判据④:重复导入同一文件 → 全部为 update(不重复新增)。
func TestReimportSameFileAllUpdate(t *testing.T) {
rows := []importRow{
{Name: "张三", Phone: "13800000001"},
{Name: "李四", Phone: "13900000002"},
}
// 首次导入:库中无人 → 全部 new
first := classifyImportRows(rows, nil)
for _, c := range first {
if c.Type != consts.ImportTypeNew {
t.Fatalf("首次导入应为 new,got %s", c.Type)
}
}
// 再次导入同一文件:库中已有这两人 → 全部 update
existing := []existingEmployee{
{Id: 1, Name: "张三", Phone: "13800000001"},
{Id: 2, Name: "李四", Phone: "13900000002"},
}
second := classifyImportRows(rows, existing)
for _, c := range second {
if c.Type != consts.ImportTypeUpdate {
t.Fatalf("重复导入应全部为 update,got %s(row %d)", c.Type, c.Idx)
}
}
}
// TestCountClassified 预览计数口径。
func TestCountClassified(t *testing.T) {
rows := []classifiedRow{
{Type: consts.ImportTypeNew},
{Type: consts.ImportTypeNew},
{Type: consts.ImportTypeUpdate},
{Type: consts.ImportTypeError},
{Type: consts.ImportTypeConflict},
}
c := countClassified(rows)
if c.inserted != 2 || c.updated != 1 || c.skipped != 1 || c.conflict != 1 {
t.Fatalf("counts=%+v want inserted2/updated1/skipped1/conflict1", c)
}
}