fix(runtime): 运行稳定性 - 修复技能种子、任务续租与回归检查
保留社区技能作者与时间戳修复、Agent 参数约束、资源删除 worker 生命周期、PostgreSQL/Redis 测试门禁及相关回归。 Original-Commit: 0d05ff88bbe6c875d01964041788023a662ff7e8
This commit is contained in:
@@ -2,8 +2,12 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"infinite-canvas/backend/internal/model"
|
||||
"infinite-canvas/backend/internal/repository"
|
||||
)
|
||||
|
||||
// 续租必须比"这条任务的执行时限"活得更久:否则父 context 到点时,续租请求会被自己的
|
||||
@@ -39,3 +43,66 @@ func TestTaskLeaseRenewContextSurvivesExecutionDeadline(t *testing.T) {
|
||||
t.Fatalf("父 context 取消不该连带取消续租 context:%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskDeadlineRenewalAndTerminalFencing(t *testing.T) {
|
||||
for _, lost := range []bool{false, true} {
|
||||
name := "deadline_with_valid_lease"
|
||||
if lost {
|
||||
name = "deadline_with_reclaimed_lease"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
db := newSQLiteTestDB(t)
|
||||
if err := db.AutoMigrate(&model.Task{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
repo := repository.New(db)
|
||||
if err := db.Create(&model.Task{ID: "deadline-task", UserID: "user", Type: "canvas_text", Status: model.TaskStatusQueued}).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
task, err := repo.ClaimNextTask("worker-a", time.Minute)
|
||||
if err != nil || task == nil {
|
||||
t.Fatalf("claim: %v %v", task, err)
|
||||
}
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-time.Second))
|
||||
defer cancel()
|
||||
renew, cancelRenew := taskLeaseRenewContext(ctx)
|
||||
defer cancelRenew()
|
||||
if lost {
|
||||
if err := db.Model(&model.Task{}).Where("id = ?", task.ID).Update("lease_expires_at", time.Now().Add(-time.Second)).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
reclaimed, err := repo.ClaimNextTask("worker-b", time.Minute)
|
||||
if err != nil || reclaimed == nil {
|
||||
t.Fatalf("reclaim: %v %v", reclaimed, err)
|
||||
}
|
||||
}
|
||||
err = repo.WithContext(renew).RenewTaskLease(task.ID, task.LeaseOwner, time.Minute)
|
||||
if (err != nil) != lost {
|
||||
t.Fatalf("renew lost=%v: %v", lost, err)
|
||||
}
|
||||
billing, replay := &taskTerminalBillingStub{}, &taskTerminalReplayStub{}
|
||||
terminal := newTaskTerminalCoordinatorForTest(repo, billing, replay, &taskTerminalLoggerStub{}, &taskTerminalOutputStub{})
|
||||
err = terminal.handleExecutionFailure(task, ctx.Err(), false, false)
|
||||
stored, readErr := repo.Task(task.ID)
|
||||
if readErr != nil {
|
||||
t.Fatal(readErr)
|
||||
}
|
||||
if lost {
|
||||
if !errors.Is(err, repository.ErrTaskStateConflict) || stored.Status != model.TaskStatusRunning || stored.LeaseOwner != "worker-b" {
|
||||
t.Fatalf("stale terminal write: %v %#v", err, stored)
|
||||
}
|
||||
if len(billing.refund)+len(billing.uncertain)+len(replay.statuses) != 0 {
|
||||
t.Fatal("stale worker emitted terminal side effects")
|
||||
}
|
||||
} else {
|
||||
if !errors.Is(err, context.DeadlineExceeded) || stored.Status != model.TaskStatusFailed {
|
||||
t.Fatalf("deadline not finalized: %v %#v", err, stored)
|
||||
}
|
||||
next, err := repo.ClaimNextTask("worker-b", time.Minute)
|
||||
if err != nil || next != nil {
|
||||
t.Fatalf("terminal task reclaimed: %v %v", next, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user