实现七月迭代公共技术基础
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 9m20s
This commit is contained in:
130
internal/infrastructure/asynctask/store_integration_test.go
Normal file
130
internal/infrastructure/asynctask/store_integration_test.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package asynctask_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
storepkg "github.com/break/junhong_cmp_fiber/internal/infrastructure/asynctask"
|
||||
"github.com/break/junhong_cmp_fiber/internal/testutil"
|
||||
contract "github.com/break/junhong_cmp_fiber/pkg/asynctask"
|
||||
)
|
||||
|
||||
func TestPostgresTaskTransitionsAreConditionalAndRecoverExpiredLease(t *testing.T) {
|
||||
db := testutil.NewPostgresTransaction(t)
|
||||
createTaskContractTable(t, db)
|
||||
store, err := storepkg.NewStore(db, taskDefinition())
|
||||
if err != nil {
|
||||
t.Fatalf("创建任务契约 Store 失败:%v", err)
|
||||
}
|
||||
now := time.Date(2026, 7, 23, 10, 0, 0, 0, time.UTC)
|
||||
if err := db.Exec("INSERT INTO test_async_contract_task (id, status, updated_at) VALUES (1, 1, ?)", now).Error; err != nil {
|
||||
t.Fatalf("准备待处理任务失败:%v", err)
|
||||
}
|
||||
claimed, err := store.Claim(context.Background(), 1, "worker-a", now, time.Minute)
|
||||
if err != nil || !claimed {
|
||||
t.Fatalf("领取待处理任务失败:%v,领取:%v", err, claimed)
|
||||
}
|
||||
claimed, err = store.Claim(context.Background(), 1, "worker-b", now.Add(30*time.Second), time.Minute)
|
||||
if err != nil || claimed {
|
||||
t.Fatalf("不得抢占有效租约:%v,领取:%v", err, claimed)
|
||||
}
|
||||
claimed, err = store.Claim(context.Background(), 1, "worker-b", now.Add(2*time.Minute), time.Minute)
|
||||
if err != nil || !claimed {
|
||||
t.Fatalf("过期任务应由新 Worker 恢复:%v,领取:%v", err, claimed)
|
||||
}
|
||||
renewed, err := store.Renew(context.Background(), 1, "worker-a", now.Add(150*time.Second), 2*time.Minute)
|
||||
if err != nil || renewed {
|
||||
t.Fatalf("旧租约所有者不得续租:%v,续租:%v", err, renewed)
|
||||
}
|
||||
renewed, err = store.Renew(context.Background(), 1, "worker-b", now.Add(150*time.Second), 2*time.Minute)
|
||||
if err != nil || !renewed {
|
||||
t.Fatalf("有效租约所有者续租失败:%v,续租:%v", err, renewed)
|
||||
}
|
||||
claimed, err = store.Claim(context.Background(), 1, "worker-c", now.Add(3*time.Minute), time.Minute)
|
||||
if err != nil || claimed {
|
||||
t.Fatalf("续租后不得被其他 Worker 领取:%v,领取:%v", err, claimed)
|
||||
}
|
||||
finished, err := store.Finish(context.Background(), 1, "worker-a", contract.TerminalResult{
|
||||
TaskID: "1", Status: contract.StatusCompleted, TotalCount: 10, SuccessCount: 7, FailedCount: 3, UpdatedAt: now,
|
||||
}, now.Add(3*time.Minute))
|
||||
if err != nil || finished {
|
||||
t.Fatalf("旧租约所有者不得完成任务:%v,完成:%v", err, finished)
|
||||
}
|
||||
finished, err = store.Finish(context.Background(), 1, "worker-b", contract.TerminalResult{
|
||||
TaskID: "1", Status: contract.StatusCompleted, TotalCount: 10, SuccessCount: 7, FailedCount: 3, UpdatedAt: now,
|
||||
}, now.Add(3*time.Minute))
|
||||
if err != nil || !finished {
|
||||
t.Fatalf("当前租约所有者完成任务失败:%v,完成:%v", err, finished)
|
||||
}
|
||||
claimed, err = store.Claim(context.Background(), 1, "worker-c", now.Add(4*time.Minute), time.Minute)
|
||||
if err != nil || claimed {
|
||||
t.Fatalf("终态重复消费必须无副作用:%v,领取:%v", err, claimed)
|
||||
}
|
||||
cancelled, err := store.Cancel(context.Background(), 1, now.Add(4*time.Minute))
|
||||
if err != nil || cancelled {
|
||||
t.Fatalf("终态任务不得再次取消:%v,取消:%v", err, cancelled)
|
||||
}
|
||||
|
||||
var row struct {
|
||||
Status int
|
||||
Total int
|
||||
Success int
|
||||
Failed int
|
||||
}
|
||||
if err := db.Table("test_async_contract_task").Select("status, total_count AS total, success_count AS success, failed_count AS failed").Where("id = 1").Scan(&row).Error; err != nil {
|
||||
t.Fatalf("读取任务终态失败:%v", err)
|
||||
}
|
||||
if row.Status != contract.StatusCompleted || row.Total != 10 || row.Success != 7 || row.Failed != 3 {
|
||||
t.Fatalf("任务终态计数错误:%+v", row)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPostgresTaskContractSupportsWholeFailureAndCancellation(t *testing.T) {
|
||||
db := testutil.NewPostgresTransaction(t)
|
||||
createTaskContractTable(t, db)
|
||||
store, _ := storepkg.NewStore(db, taskDefinition())
|
||||
now := time.Now().UTC()
|
||||
if err := db.Exec("INSERT INTO test_async_contract_task (id, status, updated_at) VALUES (2, 1, ?), (3, 1, ?)", now, now).Error; err != nil {
|
||||
t.Fatalf("准备任务失败:%v", err)
|
||||
}
|
||||
claimed, _ := store.Claim(context.Background(), 2, "worker", now, time.Minute)
|
||||
if !claimed {
|
||||
t.Fatal("整体失败任务领取失败")
|
||||
}
|
||||
finished, err := store.Finish(context.Background(), 2, "worker", contract.TerminalResult{
|
||||
TaskID: "2", Status: contract.StatusFailed, ErrorCode: "FILE_PARSE_FAILED", ErrorSummary: "文件无法解析", UpdatedAt: now,
|
||||
}, now)
|
||||
if err != nil || !finished {
|
||||
t.Fatalf("整体失败终态更新失败:%v", err)
|
||||
}
|
||||
cancelled, err := store.Cancel(context.Background(), 3, now)
|
||||
if err != nil || !cancelled {
|
||||
t.Fatalf("待处理任务取消失败:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func taskDefinition() storepkg.Definition {
|
||||
return storepkg.Definition{
|
||||
Table: "test_async_contract_task", IDColumn: "id", StatusColumn: "status",
|
||||
LeaseOwnerColumn: "lease_owner", LeaseExpiresColumn: "lease_expires_at",
|
||||
TotalColumn: "total_count", SuccessColumn: "success_count", FailedColumn: "failed_count",
|
||||
ProgressColumn: "progress", ErrorCodeColumn: "error_code", ErrorSummaryColumn: "error_summary",
|
||||
StartedAtColumn: "started_at", CompletedAtColumn: "completed_at", UpdatedAtColumn: "updated_at",
|
||||
}
|
||||
}
|
||||
|
||||
func createTaskContractTable(t *testing.T, db *gorm.DB) {
|
||||
t.Helper()
|
||||
if err := db.Exec(`CREATE TEMP TABLE test_async_contract_task (
|
||||
id bigint PRIMARY KEY, status integer NOT NULL, total_count integer NOT NULL DEFAULT 0,
|
||||
success_count integer NOT NULL DEFAULT 0, failed_count integer NOT NULL DEFAULT 0,
|
||||
progress integer NOT NULL DEFAULT 0, error_code varchar(100) NOT NULL DEFAULT '',
|
||||
error_summary varchar(500) NOT NULL DEFAULT '', lease_owner varchar(100), lease_expires_at timestamptz,
|
||||
started_at timestamptz, completed_at timestamptz, updated_at timestamptz NOT NULL
|
||||
) ON COMMIT DROP`).Error; err != nil {
|
||||
t.Fatalf("创建任务契约测试表失败:%v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user