feat(tasks): add templates findings workflows and batch controls
This commit is contained in:
1 parent
b7e0b93023
commit
d3183dbbd2
65 files changed
+11603
-2198
No files matched your search
@@ -42,7 +42,7 @@ func TestEngineDeleteBarrierRejectsNewTaskOperations(t *testing.T) {
|
||||
if e.BeginDelete(taskID) {
|
||||
t.Fatal("duplicate delete must not acquire the barrier")
|
||||
}
|
||||
e.AbortDelete(taskID)
|
||||
e.AbortDelete(taskID, false)
|
||||
if e.IsPaused(taskID) {
|
||||
t.Fatal("aborted delete should restore a previously running task")
|
||||
}
|
||||
@@ -58,12 +58,130 @@ func TestAbortDeletePreservesExistingPause(t *testing.T) {
|
||||
if !e.BeginDelete("42") {
|
||||
t.Fatal("delete barrier should be installed")
|
||||
}
|
||||
e.AbortDelete("42")
|
||||
e.AbortDelete("42", true)
|
||||
if !e.IsPaused("42") {
|
||||
t.Fatal("aborted delete must preserve a pre-existing user pause")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBeginTaskDeleteSerializesWithLifecycleLock(t *testing.T) {
|
||||
s := &Server{engine: NewEngine(nil)}
|
||||
s.concMu.Lock()
|
||||
done := make(chan bool, 1)
|
||||
go func() { done <- s.beginTaskDelete("42") }()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
t.Fatal("delete barrier bypassed the lifecycle lock")
|
||||
case <-time.After(20 * time.Millisecond):
|
||||
}
|
||||
if s.engine.IsDeleting("42") {
|
||||
t.Fatal("delete barrier was installed before concMu was released")
|
||||
}
|
||||
s.concMu.Unlock()
|
||||
select {
|
||||
case ok := <-done:
|
||||
if !ok {
|
||||
t.Fatal("delete barrier was not installed after lifecycle lock release")
|
||||
}
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("timed out waiting for delete barrier")
|
||||
}
|
||||
s.engine.AbortDelete("42", false)
|
||||
}
|
||||
|
||||
func TestDeletingRunningTaskKeepsConcurrencySlotUntilCommit(t *testing.T) {
|
||||
task := &Task{ID: "42", Status: "running"}
|
||||
s := &Server{engine: NewEngine(nil)}
|
||||
if !s.engine.BeginDelete(task.ID) {
|
||||
t.Fatal("install delete barrier")
|
||||
}
|
||||
if !s.occupiesConcurrencySlot(task) {
|
||||
t.Fatal("temporary delete pause released the running task's slot before commit")
|
||||
}
|
||||
s.engine.AbortDelete(task.ID, false)
|
||||
}
|
||||
|
||||
func TestTaskLifecycleRechecksDeleteBarrierAfterConcLock(t *testing.T) {
|
||||
task := &Task{ID: "42", notify: make(chan struct{}, 1)}
|
||||
m := &Manager{tasks: map[string]*Task{task.ID: task}}
|
||||
s := &Server{m: m, engine: NewEngine(m)}
|
||||
|
||||
s.concMu.Lock()
|
||||
started := make(chan struct{})
|
||||
pauseDone := make(chan error, 1)
|
||||
go func() {
|
||||
close(started)
|
||||
_, err := s.applyTaskControl(task, "pause")
|
||||
pauseDone <- err
|
||||
}()
|
||||
<-started
|
||||
if !s.engine.BeginDelete(task.ID) {
|
||||
t.Fatal("install delete barrier")
|
||||
}
|
||||
s.concMu.Unlock()
|
||||
if err := <-pauseDone; err == nil || !strings.Contains(err.Error(), "正在删除") {
|
||||
t.Fatalf("pause passed a delete barrier installed while waiting for concMu: %v", err)
|
||||
}
|
||||
s.engine.AbortDelete(task.ID, false)
|
||||
|
||||
s.concMu.Lock()
|
||||
started = make(chan struct{})
|
||||
admitDone := make(chan error, 1)
|
||||
go func() {
|
||||
close(started)
|
||||
_, err := s.admitTask(task, "resume")
|
||||
admitDone <- err
|
||||
}()
|
||||
<-started
|
||||
if !s.engine.BeginDelete(task.ID) {
|
||||
t.Fatal("install second delete barrier")
|
||||
}
|
||||
s.concMu.Unlock()
|
||||
if err := <-admitDone; err == nil || !strings.Contains(err.Error(), "being deleted") {
|
||||
t.Fatalf("admission passed a delete barrier installed while waiting for concMu: %v", err)
|
||||
}
|
||||
s.engine.AbortDelete(task.ID, false)
|
||||
}
|
||||
|
||||
func TestAbortTaskDeleteUsesPersistedPauseAndQueueState(t *testing.T) {
|
||||
m, err := NewManager(t.TempDir(), "")
|
||||
if err != nil {
|
||||
t.Skipf("postgres unavailable (%v) - skipping", err)
|
||||
}
|
||||
defer m.Close()
|
||||
task, err := m.CreateTask("delete abort state", "restore persisted barrier", nil, 0, 0)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _, _ = m.DeleteTask(task.ID, DeleteTaskOptions{}) }()
|
||||
s := &Server{m: m, engine: NewEngine(m)}
|
||||
|
||||
// An engine-only pause is not authoritative. The committed task is running,
|
||||
// so aborting deletion must clear this stale in-memory barrier.
|
||||
s.engine.Pause(task.ID, agent.AbortPausedByUser)
|
||||
if !s.beginTaskDelete(task.ID) {
|
||||
t.Fatal("install first delete barrier")
|
||||
}
|
||||
s.abortTaskDelete(task.ID)
|
||||
if s.engine.IsPaused(task.ID) {
|
||||
t.Fatal("stale engine pause survived although the persisted task is running")
|
||||
}
|
||||
|
||||
// A queued row must retain its execution barrier even when BeginDelete saw the
|
||||
// engine as running. This specifically guards against restoring a stale snapshot.
|
||||
if err := m.EnqueueTask(task.ID, "resume"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !s.beginTaskDelete(task.ID) {
|
||||
t.Fatal("install second delete barrier")
|
||||
}
|
||||
s.abortTaskDelete(task.ID)
|
||||
if !s.engine.IsPaused(task.ID) {
|
||||
t.Fatal("persisted queued task lost its execution barrier after abort")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStopTaskCancelsRuntimeAndClearsLifecycleState(t *testing.T) {
|
||||
e := NewEngine(nil)
|
||||
const taskID = "42"
|
||||
@@ -182,6 +300,32 @@ func TestWaitTaskQuiescentHonorsContext(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskChatStatusUsesAuthoritativeBusyState(t *testing.T) {
|
||||
m := &Manager{tasks: map[string]*Task{"7": {ID: "7"}}}
|
||||
s := &Server{
|
||||
m: m,
|
||||
chatBusy: map[string]bool{"7": true},
|
||||
chatCancel: map[string]context.CancelCauseFunc{},
|
||||
}
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/tasks/7/chat/status", nil)
|
||||
req.SetPathValue("id", "7")
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
s.taskChatStatus(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("status=%d body=%s, want 200", rec.Code, rec.Body.String())
|
||||
}
|
||||
var got struct {
|
||||
Running bool `json:"running"`
|
||||
}
|
||||
if err := json.NewDecoder(rec.Body).Decode(&got); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !got.Running {
|
||||
t.Fatal("busy main-agent turn was reported idle")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskChatUploadRejectsDeleteBarrierBeforeCreatingDirectory(t *testing.T) {
|
||||
dataDir := t.TempDir()
|
||||
m := &Manager{dir: dataDir, tasks: map[string]*Task{"7": {ID: "7"}}}
|
||||
|
||||
Reference in new issue
Block a user