mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-14 01:27:28 +08:00
166 lines
4.2 KiB
Go
166 lines
4.2 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"clawgo/pkg/bus"
|
|
)
|
|
|
|
func TestSubagentSpawnEnforcesTaskQuota(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
workspace := t.TempDir()
|
|
manager := NewSubagentManager(nil, workspace, nil, nil)
|
|
manager.SetRunFunc(func(ctx context.Context, task *SubagentTask) (string, error) {
|
|
return "ok", nil
|
|
})
|
|
store := manager.ProfileStore()
|
|
if store == nil {
|
|
t.Fatalf("expected profile store")
|
|
}
|
|
if _, err := store.Upsert(SubagentProfile{
|
|
AgentID: "coder",
|
|
MaxTaskChars: 8,
|
|
}); err != nil {
|
|
t.Fatalf("failed to create profile: %v", err)
|
|
}
|
|
|
|
_, err := manager.Spawn(context.Background(), SubagentSpawnOptions{
|
|
Task: "this task is too long",
|
|
AgentID: "coder",
|
|
OriginChannel: "cli",
|
|
OriginChatID: "direct",
|
|
})
|
|
if err == nil {
|
|
t.Fatalf("expected max_task_chars quota to reject spawn")
|
|
}
|
|
}
|
|
|
|
func TestSubagentRunWithRetryEventuallySucceeds(t *testing.T) {
|
|
workspace := t.TempDir()
|
|
manager := NewSubagentManager(nil, workspace, nil, nil)
|
|
attempts := 0
|
|
manager.SetRunFunc(func(ctx context.Context, task *SubagentTask) (string, error) {
|
|
attempts++
|
|
if attempts == 1 {
|
|
return "", errors.New("temporary failure")
|
|
}
|
|
return "retry success", nil
|
|
})
|
|
|
|
_, err := manager.Spawn(context.Background(), SubagentSpawnOptions{
|
|
Task: "retry task",
|
|
AgentID: "coder",
|
|
OriginChannel: "cli",
|
|
OriginChatID: "direct",
|
|
MaxRetries: 1,
|
|
RetryBackoff: 1,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("spawn failed: %v", err)
|
|
}
|
|
|
|
task := waitSubagentDone(t, manager, 4*time.Second)
|
|
if task.Status != "completed" {
|
|
t.Fatalf("expected completed task, got %s (%s)", task.Status, task.Result)
|
|
}
|
|
if task.RetryCount != 1 {
|
|
t.Fatalf("expected retry_count=1, got %d", task.RetryCount)
|
|
}
|
|
if attempts < 2 {
|
|
t.Fatalf("expected at least 2 attempts, got %d", attempts)
|
|
}
|
|
}
|
|
|
|
func TestSubagentRunWithTimeoutFails(t *testing.T) {
|
|
workspace := t.TempDir()
|
|
manager := NewSubagentManager(nil, workspace, nil, nil)
|
|
manager.SetRunFunc(func(ctx context.Context, task *SubagentTask) (string, error) {
|
|
select {
|
|
case <-ctx.Done():
|
|
return "", ctx.Err()
|
|
case <-time.After(2 * time.Second):
|
|
return "unexpected", nil
|
|
}
|
|
})
|
|
|
|
_, err := manager.Spawn(context.Background(), SubagentSpawnOptions{
|
|
Task: "timeout task",
|
|
AgentID: "coder",
|
|
OriginChannel: "cli",
|
|
OriginChatID: "direct",
|
|
TimeoutSec: 1,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("spawn failed: %v", err)
|
|
}
|
|
|
|
task := waitSubagentDone(t, manager, 4*time.Second)
|
|
if task.Status != "failed" {
|
|
t.Fatalf("expected failed task on timeout, got %s", task.Status)
|
|
}
|
|
if task.RetryCount != 0 {
|
|
t.Fatalf("expected retry_count=0, got %d", task.RetryCount)
|
|
}
|
|
}
|
|
|
|
func TestSubagentBroadcastIncludesFailureStatus(t *testing.T) {
|
|
workspace := t.TempDir()
|
|
msgBus := bus.NewMessageBus()
|
|
defer msgBus.Close()
|
|
|
|
manager := NewSubagentManager(nil, workspace, msgBus, nil)
|
|
manager.SetRunFunc(func(ctx context.Context, task *SubagentTask) (string, error) {
|
|
return "", errors.New("boom")
|
|
})
|
|
|
|
_, err := manager.Spawn(context.Background(), SubagentSpawnOptions{
|
|
Task: "failing task",
|
|
AgentID: "coder",
|
|
OriginChannel: "cli",
|
|
OriginChatID: "direct",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("spawn failed: %v", err)
|
|
}
|
|
|
|
task := waitSubagentDone(t, manager, 4*time.Second)
|
|
if task.Status != "failed" {
|
|
t.Fatalf("expected failed task, got %s", task.Status)
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
defer cancel()
|
|
msg, ok := msgBus.ConsumeInbound(ctx)
|
|
if !ok {
|
|
t.Fatalf("expected subagent completion message")
|
|
}
|
|
if got := strings.TrimSpace(msg.Metadata["status"]); got != "failed" {
|
|
t.Fatalf("expected metadata status=failed, got %q", got)
|
|
}
|
|
if !strings.Contains(strings.ToLower(msg.Content), "failed") {
|
|
t.Fatalf("expected failure wording in content, got %q", msg.Content)
|
|
}
|
|
}
|
|
|
|
func waitSubagentDone(t *testing.T, manager *SubagentManager, timeout time.Duration) *SubagentTask {
|
|
t.Helper()
|
|
deadline := time.Now().Add(timeout)
|
|
for time.Now().Before(deadline) {
|
|
tasks := manager.ListTasks()
|
|
if len(tasks) > 0 {
|
|
task := tasks[0]
|
|
if task.Status != "running" {
|
|
return task
|
|
}
|
|
}
|
|
time.Sleep(30 * time.Millisecond)
|
|
}
|
|
t.Fatalf("timeout waiting for subagent completion")
|
|
return nil
|
|
}
|