fix(runtime): 运行稳定性 - 修复技能种子、任务续租与回归检查

保留社区技能作者与时间戳修复、Agent 参数约束、资源删除 worker 生命周期、PostgreSQL/Redis 测试门禁及相关回归。

Original-Commit: 0d05ff88bbe6c875d01964041788023a662ff7e8
This commit is contained in:
ddCat
2026-09-21 09:48:43 +08:00
parent 6a3e454141
commit 7f44332914
21 changed files with 644 additions and 354 deletions

View File

@@ -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)
}
}
})
}
}