package logic import ( "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "micro-bundle/internal/dto" ) // ---- CreateTaskWorkLog 范围校验 ---- func TestCreateTaskWorkLog_Validation(t *testing.T) { t.Parallel() base := func() *dto.CreateTaskWorkLogRequest { return &dto.CreateTaskWorkLogRequest{ AssignRecordsUUID: "uuid-1", WorkUUID: "work-1", OperationType: 1, // 1-加 TaskType: 1, // 1-视频 TaskCount: 1, } } tests := []struct { name string mutate func(r *dto.CreateTaskWorkLogRequest) wantContain string }{ // ReturnError(nil, "参数错误", ...) 实际返回的 msg 统一是 "参数错误", // 详细说明只在 print 字段(不入错误链),所以断言统一为 "参数错误", // 区分点放在测试名上。 {"OperationType为0", func(r *dto.CreateTaskWorkLogRequest) { r.OperationType = 0 }, "参数错误"}, {"OperationType为5", func(r *dto.CreateTaskWorkLogRequest) { r.OperationType = 5 }, "参数错误"}, {"TaskType为0", func(r *dto.CreateTaskWorkLogRequest) { r.TaskType = 0 }, "参数错误"}, {"TaskType为7", func(r *dto.CreateTaskWorkLogRequest) { r.TaskType = 7 }, "参数错误"}, {"TaskCount为负", func(r *dto.CreateTaskWorkLogRequest) { r.TaskCount = -1 }, "参数错误"}, } for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() r := base() tc.mutate(r) err := CreateTaskWorkLog(r) require.Error(t, err) assert.Contains(t, err.Error(), tc.wantContain) }) } } // ---- BatchAssignTask 校验 ---- func TestBatchAssignTask_EmptyItems(t *testing.T) { t.Parallel() // nil 和 空 slice 都走 `ReturnError(nil, "参数错误", "批量指派项不能为空")`, // 实际返回的 msg 是 "参数错误"。 // nil slice err := BatchAssignTask(nil) require.Error(t, err) assert.Contains(t, err.Error(), "参数错误") // 空 slice err = BatchAssignTask([]*dto.BatchAssignItem{}) require.Error(t, err) assert.Contains(t, err.Error(), "参数错误") } func TestBatchAssignTask_NilItem(t *testing.T) { t.Parallel() // 同样 `ReturnError(nil, "参数错误", "存在空的指派项")`。 err := BatchAssignTask([]*dto.BatchAssignItem{nil}) require.Error(t, err) assert.Contains(t, err.Error(), "参数错误") } func TestBatchAssignTask_NegativeCount(t *testing.T) { t.Parallel() make := func(mutate func(*dto.BatchAssignItem)) *dto.BatchAssignItem { it := &dto.BatchAssignItem{ SubNum: "S001", TaskAssignee: "员工A", TaskAssigneeNum: "E001", AssignVideoCount: 1, } mutate(it) return it } tests := []struct { name string mutate func(*dto.BatchAssignItem) }{ {"Video为负", func(it *dto.BatchAssignItem) { it.AssignVideoCount = -1 }}, {"Post为负", func(it *dto.BatchAssignItem) { it.AssignPostCount = -1 }}, {"Data为负", func(it *dto.BatchAssignItem) { it.AssignDataCount = -1 }}, {"VideoScript为负", func(it *dto.BatchAssignItem) { it.AssignVideoScriptCount = -1 }}, {"Report为负", func(it *dto.BatchAssignItem) { it.AssignReportCount = -1 }}, } for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() err := BatchAssignTask([]*dto.BatchAssignItem{make(tc.mutate)}) require.Error(t, err) assert.Contains(t, err.Error(), "指派数量不能小于0") }) } } func TestBatchAssignTask_SingleItemAllZero(t *testing.T) { t.Parallel() it := &dto.BatchAssignItem{SubNum: "S001", TaskAssignee: "员工A", TaskAssigneeNum: "E001"} err := BatchAssignTask([]*dto.BatchAssignItem{it}) require.Error(t, err) assert.Contains(t, err.Error(), "指派数量不能全部为0") } // 注:logic 中"全部指派项数量都为 0"的外层兜底(`if !hasPositive`)实际不可达: // 循环中每项的"单项全 0"检查会先返回,所以该分支永远走不到。 // 保留这条注释供后人参考,不写不可达测试。 // ---- TerminateTaskByUUID / BatchTerminateTaskByUUIDs ---- func TestTerminateTaskByUUID_Empty(t *testing.T) { t.Parallel() err := TerminateTaskByUUID("") require.Error(t, err) assert.Contains(t, err.Error(), "参数错误") } func TestBatchTerminateTaskByUUIDs_Empty(t *testing.T) { t.Parallel() success, failed, failedUUIDs, err := BatchTerminateTaskByUUIDs(nil) require.Error(t, err) assert.Equal(t, 0, success) assert.Equal(t, 0, failed) assert.Nil(t, failedUUIDs) assert.Contains(t, err.Error(), "参数错误") success, failed, failedUUIDs, err = BatchTerminateTaskByUUIDs([]string{}) require.Error(t, err) assert.Equal(t, 0, success) assert.Equal(t, 0, failed) assert.Nil(t, failedUUIDs) } // ---- RevertTaskCompletionByUUIDItem ---- func TestRevertTaskCompletionByUUIDItem_Empty(t *testing.T) { t.Parallel() for _, in := range []string{"", " ", "\t\n"} { err := RevertTaskCompletionByUUIDItem(in) require.Error(t, err, "input=%q", in) assert.Contains(t, err.Error(), "参数错误") } } // ---- GetTaskActualStatusByUUID ---- func TestGetTaskActualStatusByUUID_Empty(t *testing.T) { t.Parallel() for _, in := range []string{"", " "} { status, err := GetTaskActualStatusByUUID(in) require.Error(t, err, "input=%q", in) assert.Equal(t, 0, status) assert.Contains(t, err.Error(), "查询参数错误") } } // ---- AddHiddenTaskAssignee ---- func TestAddHiddenTaskAssignee_Empty(t *testing.T) { t.Parallel() tests := []struct { name, assignee, num string }{ {"都为空", "", ""}, {"名称空", "", "E001"}, {"账号空", "员工A", ""}, {"全空格", " ", " "}, } for _, tc := range tests { tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() err := AddHiddenTaskAssignee(tc.assignee, tc.num) require.Error(t, err) assert.Contains(t, err.Error(), "参数错误") }) } } // ---- buildDefaultPendingLayout (私有函数,同包内可直接测) ---- func TestBuildDefaultPendingLayout(t *testing.T) { t.Parallel() got := buildDefaultPendingLayout() require.NotEmpty(t, got) var rows []map[string]any err := json.Unmarshal([]byte(got), &rows) require.NoError(t, err, "应当是合法 JSON") // 33 个字段是当前硬编码的默认值;如未来调整,只改这一个数字。 assert.Len(t, rows, 33) // 每个字段必有 fieldKey / fieldValue / sortOrNot / status for i, row := range rows { assert.Contains(t, row, "fieldKey", "第 %d 行缺 fieldKey", i) assert.Contains(t, row, "fieldValue", "第 %d 行缺 fieldValue", i) assert.Contains(t, row, "sortOrNot", "第 %d 行缺 sortOrNot", i) assert.Contains(t, row, "status", "第 %d 行缺 status", i) } } // GetPendingTaskLayout 的 dao 降级路径(失败时回退默认布局)留作下一阶段, // 那里需要 sqlmock 配合 dao 验证。纯函数 buildDefaultPendingLayout 已被 // TestBuildDefaultPendingLayout 覆盖。