chore: 并入 iotlcm-worker-svr 历史到 worker-svr/(原提交 96f0f4e)
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
package allocate
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-worker-svr/internal/model/entity"
|
||||
)
|
||||
|
||||
func TestAllocateProcessingRetryAfterHasLargeBatchDefault(t *testing.T) {
|
||||
if got := allocateProcessingRetryAfter(context.Background()); got < 30*time.Minute {
|
||||
t.Fatalf("allocateProcessingRetryAfter() = %s, want at least 30m", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAllocateParentRequiresTypeAndRole(t *testing.T) {
|
||||
parent := &entity.BatchMain{
|
||||
BatchType: consts.BatchTypeAllocate,
|
||||
BatchInfo: gjson.New(`{"jobRole":"allocate_parent"}`),
|
||||
}
|
||||
if !isAllocateParent(parent) {
|
||||
t.Fatal("expected allocate parent metadata to be recognized")
|
||||
}
|
||||
parent.SourceBatchNo = "ALC-CHILD"
|
||||
if isAllocateParent(parent) {
|
||||
t.Fatal("child batch must not be recognized as allocate parent")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsAllocateDeadlock(t *testing.T) {
|
||||
if !isAllocateDeadlock(errors.New("Error 1213 (40001): Deadlock found when trying to get lock")) {
|
||||
t.Fatal("expected MySQL deadlock to be retryable")
|
||||
}
|
||||
if isAllocateDeadlock(errors.New("validation failed")) {
|
||||
t.Fatal("validation errors must not be treated as deadlocks")
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveAllocateParentStartTime(t *testing.T) {
|
||||
parentStart := gtime.New("2026-09-29 12:59:00")
|
||||
childStart := gtime.New("2026-09-29 12:59:32")
|
||||
fallback := gtime.New("2026-09-29 13:00:21")
|
||||
|
||||
if got := resolveAllocateParentStartTime(parentStart, childStart, fallback); got != parentStart {
|
||||
t.Fatalf("parent start time = %v, want existing parent time %v", got, parentStart)
|
||||
}
|
||||
if got := resolveAllocateParentStartTime(nil, childStart, fallback); got != childStart {
|
||||
t.Fatalf("parent start time = %v, want earliest child time %v", got, childStart)
|
||||
}
|
||||
if got := resolveAllocateParentStartTime(nil, nil, fallback); got != fallback {
|
||||
t.Fatalf("parent start time = %v, want fallback time %v", got, fallback)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocateBatchSizesRemainBounded(t *testing.T) {
|
||||
if allocateCardQueryBatchSize <= 0 || allocateCardQueryBatchSize > 1000 {
|
||||
t.Fatalf("allocateCardQueryBatchSize = %d", allocateCardQueryBatchSize)
|
||||
}
|
||||
if allocateCardUpdateBatchSize <= 0 || allocateCardUpdateBatchSize > 1000 {
|
||||
t.Fatalf("allocateCardUpdateBatchSize = %d", allocateCardUpdateBatchSize)
|
||||
}
|
||||
if allocateDetailUpdateBatchSize <= 0 || allocateDetailUpdateBatchSize > 1000 {
|
||||
t.Fatalf("allocateDetailUpdateBatchSize = %d", allocateDetailUpdateBatchSize)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildDetailUpdateStatementUsesBoundParameters(t *testing.T) {
|
||||
task := &Task{Batch: Batch{BatchNo: "ALC-1", CreatedBy: 7}}
|
||||
details := []*Detail{
|
||||
{ID: 11, DetailStatus: consts.BatchDetailStatusPending, Reason: atomicNotExecutedReason},
|
||||
{ID: 12, DetailStatus: consts.BatchDetailStatusRunning, Reason: "second"},
|
||||
}
|
||||
statement, args := buildDetailUpdateStatement(task, details, gtime.Now())
|
||||
if strings.Contains(statement, details[0].Reason) {
|
||||
t.Fatal("detail reason was interpolated into SQL")
|
||||
}
|
||||
if !strings.Contains(statement, "CASE") || !strings.Contains(statement, "IN (?,?)") {
|
||||
t.Fatalf("unexpected statement: %s", statement)
|
||||
}
|
||||
if len(args) != len(details)*7+10 {
|
||||
t.Fatalf("argument count = %d, want %d", len(args), len(details)*7+10)
|
||||
}
|
||||
if args[1] != consts.BatchDetailStatusNotExecuted || args[3] != consts.BatchDetailStatusFailed {
|
||||
t.Fatalf("status parameters = %v, %v; want not executed and failed", args[1], args[3])
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeAtomicParentResultRejectsPartialSuccess(t *testing.T) {
|
||||
task := &Task{Details: []*Detail{
|
||||
{ICCID: "8986000000000000001"},
|
||||
{ICCID: "8986000000000000002", Reason: "目标客户不存在"},
|
||||
}}
|
||||
|
||||
successCount, failCount := normalizeAtomicParentResult(task)
|
||||
if successCount != 0 || failCount != 1 {
|
||||
t.Fatalf("normalizeAtomicParentResult() = %d, %d; want 0, 1", successCount, failCount)
|
||||
}
|
||||
for _, detail := range task.Details {
|
||||
if detail.Success() {
|
||||
t.Fatalf("detail %s remained successful", detail.ICCID)
|
||||
}
|
||||
}
|
||||
if allocationDetailStatus(task.Details[0]) != consts.BatchDetailStatusNotExecuted || allocationDetailStatus(task.Details[1]) != consts.BatchDetailStatusFailed {
|
||||
t.Fatalf("wrong detail statuses: %+v", task.Details)
|
||||
}
|
||||
rows := task.ResultRows()
|
||||
if rows[0].Status != "未执行" || rows[1].Status != "失败" {
|
||||
t.Fatalf("wrong result file statuses: %+v", rows)
|
||||
}
|
||||
status, result, _ := parentBatchResult(successCount, failCount)
|
||||
if status != consts.BatchAsyncStatusFailed || result != consts.BatchRunResultFailed {
|
||||
t.Fatalf("parentBatchResult() = %d, %d; want failed, failed", status, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocateChildValidationDoesNotReportExecutedSuccess(t *testing.T) {
|
||||
task := &Task{Batch: Batch{BatchNo: "child", ParentBatchNo: "parent"}}
|
||||
detail := &Detail{ID: 1, ICCID: "valid"}
|
||||
_, args := buildDetailUpdateStatement(task, []*Detail{detail}, gtime.Now())
|
||||
if args[1] != consts.BatchDetailStatusRunning {
|
||||
t.Fatalf("validated child status = %v, want running until parent commits", args[1])
|
||||
}
|
||||
task.Batch.ParentBatchNo = ""
|
||||
_, args = buildDetailUpdateStatement(task, []*Detail{detail}, gtime.Now())
|
||||
if args[1] != consts.BatchDetailStatusSuccess {
|
||||
t.Fatalf("committed parent status = %v, want success", args[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateTaskCollectsAllCardFailuresBeforeBlockingBatch(t *testing.T) {
|
||||
task := &Task{
|
||||
Batch: Batch{CreatedDept: 100, TargetDeptID: 200},
|
||||
TargetsByID: map[int64]*Target{200: {DeptID: 200, ParentID: 100}},
|
||||
Details: []*Detail{
|
||||
{ICCID: "missing"},
|
||||
{ICCID: "valid", Card: &Card{ICCID: "valid", DeptID: 100}},
|
||||
{ICCID: "stopped", Card: &Card{ICCID: "stopped", DeptID: 100, BossCardStatus: consts.SimBossCardStatusStopped, PackageStatus: consts.PackageStatusExpired}},
|
||||
},
|
||||
}
|
||||
validateTask(task)
|
||||
if task.Details[0].Reason != "ICCID不存在或不属于当前租户" {
|
||||
t.Fatalf("first failure was lost: %q", task.Details[0].Reason)
|
||||
}
|
||||
if task.Details[1].Reason != atomicNotExecutedReason || allocationDetailStatus(task.Details[1]) != consts.BatchDetailStatusNotExecuted {
|
||||
t.Fatalf("validated card was not blocked: %+v", task.Details[1])
|
||||
}
|
||||
for _, reason := range []string{"卡已停机,不能划拨", "套餐已过期,不能划拨"} {
|
||||
if !strings.Contains(task.Details[2].Reason, reason) {
|
||||
t.Fatalf("later card was not fully validated: %q", task.Details[2].Reason)
|
||||
}
|
||||
}
|
||||
if successes, failures := normalizeAtomicParentResult(task); successes != 0 || failures != 2 {
|
||||
t.Fatalf("got successes=%d failures=%d, want 0 and 2", successes, failures)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDetailAllowsCurrentHolderReallocation(t *testing.T) {
|
||||
task := &Task{
|
||||
Batch: Batch{
|
||||
CreatedDept: 100,
|
||||
},
|
||||
TargetsByID: map[int64]*Target{
|
||||
200: {DeptID: 200, ParentID: 100},
|
||||
},
|
||||
}
|
||||
detail := &Detail{
|
||||
ICCID: "8986000000000000000",
|
||||
TargetDeptID: 200,
|
||||
Card: &Card{
|
||||
ICCID: "8986000000000000000",
|
||||
DeptID: 100,
|
||||
AllocationBatchNo: "ALC202608050001",
|
||||
PackageStatus: consts.PackageStatusEffective,
|
||||
},
|
||||
}
|
||||
|
||||
validateDetail(task, detail)
|
||||
if detail.Reason != "" {
|
||||
t.Fatalf("expected current holder reallocation to pass, got %q", detail.Reason)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDetailRejectsAncestorReallocationAfterCardMoved(t *testing.T) {
|
||||
task := &Task{
|
||||
Batch: Batch{
|
||||
CreatedDept: 100,
|
||||
},
|
||||
TargetsByID: map[int64]*Target{
|
||||
300: {DeptID: 300, ParentID: 100},
|
||||
},
|
||||
}
|
||||
detail := &Detail{
|
||||
ICCID: "8986000000000000000",
|
||||
TargetDeptID: 300,
|
||||
Card: &Card{
|
||||
ICCID: "8986000000000000000",
|
||||
DeptID: 200,
|
||||
AllocationBatchNo: "ALC202608050001",
|
||||
PackageStatus: consts.PackageStatusEffective,
|
||||
},
|
||||
}
|
||||
|
||||
validateDetail(task, detail)
|
||||
if detail.Reason == "" {
|
||||
t.Fatal("expected ancestor reallocation to be rejected after card moved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateTaskMarksWholeBatchFailed(t *testing.T) {
|
||||
task := &Task{
|
||||
Details: []*Detail{
|
||||
{ICCID: "8986000000000000001"},
|
||||
{ICCID: "8986000000000000002", Reason: "目标客户不存在"},
|
||||
},
|
||||
}
|
||||
|
||||
validateTask(task)
|
||||
|
||||
for _, detail := range task.Details {
|
||||
if detail.Success() {
|
||||
t.Fatalf("detail %s remained successful after atomic failure", detail.ICCID)
|
||||
}
|
||||
}
|
||||
if task.FailureReason == "" {
|
||||
t.Fatal("expected batch failure reason")
|
||||
}
|
||||
status, result := batchResult(countResult(task))
|
||||
if status != consts.BatchAsyncStatusFailed || result != consts.BatchRunResultFailed {
|
||||
t.Fatalf("batchResult() = %d, %d; want failed, failed", status, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDetailRejectsChangedCardICCID(t *testing.T) {
|
||||
task := &Task{Batch: Batch{CreatedDept: 100}, TargetsByID: map[int64]*Target{200: {DeptID: 200, ParentID: 100}}}
|
||||
detail := &Detail{ICCID: "8986000000000000001", TargetDeptID: 200, Card: &Card{ICCID: "8986000000000000002", DeptID: 100, PackageStatus: consts.PackageStatusEffective}}
|
||||
|
||||
validateDetail(task, detail)
|
||||
if detail.Reason == "" {
|
||||
t.Fatal("expected changed card ICCID to be rejected")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user