实现七月迭代公共技术基础
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s

This commit is contained in:
2026-07-23 17:52:48 +09:00
parent f7c42252c0
commit 17782d5f8e
76 changed files with 5246 additions and 158 deletions

View File

@@ -0,0 +1,105 @@
package asynctask_test
import (
"testing"
"time"
"github.com/break/junhong_cmp_fiber/pkg/asynctask"
)
func TestCompletedTaskUsesCountsForPartialAndAllItemFailures(t *testing.T) {
t.Parallel()
now := time.Date(2026, 7, 23, 10, 0, 0, 0, time.UTC)
cases := []struct {
name string
success int
failed int
}{
{name: "部分成功", success: 7, failed: 3},
{name: "业务项全部失败", success: 0, failed: 10},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
projection, err := asynctask.NewTerminalProjection(asynctask.TerminalResult{
TaskID: "task-1", Status: asynctask.StatusCompleted,
TotalCount: 10, SuccessCount: tc.success, FailedCount: tc.failed,
StartedAt: &now, CompletedAt: &now, UpdatedAt: now,
})
if err != nil {
t.Fatalf("构造任务投影失败:%v", err)
}
if projection.Status != asynctask.StatusCompleted || projection.StatusName != "已完成" {
t.Fatalf("业务项处理到终点必须为已完成:%+v", projection)
}
if projection.Progress != 100 {
t.Fatalf("终态进度必须为 100得到%d", projection.Progress)
}
})
}
}
func TestTerminalProjectionRejectsBrokenCountInvariant(t *testing.T) {
t.Parallel()
_, err := asynctask.NewTerminalProjection(asynctask.TerminalResult{
TaskID: "task-2", Status: asynctask.StatusCompleted,
TotalCount: 10, SuccessCount: 5, FailedCount: 4,
UpdatedAt: time.Now(),
})
if err == nil {
t.Fatal("终态计数不守恒时应拒绝构造公开投影")
}
}
func TestStructuredPayloadRejectsBytes(t *testing.T) {
t.Parallel()
if err := asynctask.ValidatePayload([]byte(`{"task_id":1}`)); err == nil {
t.Fatal("预序列化 []byte 载荷必须被拒绝")
}
if err := asynctask.ValidatePayload(struct {
TaskID uint `json:"task_id"`
}{TaskID: 1}); err != nil {
t.Fatalf("结构化载荷应通过校验:%v", err)
}
}
func TestClaimRecoversOnlyExpiredProcessingTask(t *testing.T) {
t.Parallel()
now := time.Date(2026, 7, 23, 10, 0, 0, 0, time.UTC)
activeExpiry := now.Add(time.Minute)
active, err := asynctask.Claim(asynctask.LeaseState{
Status: asynctask.StatusProcessing, LeaseOwner: "worker-a", LeaseExpiresAt: &activeExpiry,
}, "worker-b", now, time.Minute)
if err != nil {
t.Fatalf("检查有效租约失败:%v", err)
}
if active.Claimed {
t.Fatal("其他 Worker 不得抢占有效租约")
}
expiredAt := now.Add(-time.Second)
recovered, err := asynctask.Claim(asynctask.LeaseState{
Status: asynctask.StatusProcessing, LeaseOwner: "worker-a", LeaseExpiresAt: &expiredAt,
}, "worker-b", now, time.Minute)
if err != nil {
t.Fatalf("恢复过期任务失败:%v", err)
}
if !recovered.Claimed || recovered.State.LeaseOwner != "worker-b" {
t.Fatalf("过期任务应由新 Worker 恢复:%+v", recovered)
}
}
func TestTerminalTaskCannotBeClaimedOrCancelledAgain(t *testing.T) {
t.Parallel()
result, err := asynctask.Claim(asynctask.LeaseState{Status: asynctask.StatusCompleted}, "worker-a", time.Now(), time.Minute)
if err != nil {
t.Fatalf("检查终态任务失败:%v", err)
}
if result.Claimed || asynctask.CanCancel(asynctask.StatusCompleted) {
t.Fatal("终态任务不得被重复消费或取消")
}
}