150 lines
5.4 KiB
Go
150 lines
5.4 KiB
Go
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)
|
||
}
|
||
}
|