package engine import ( "encoding/json" "strings" "testing" "git.flytoex.net/yuanwei/flyto-agent/pkg/tools" ) // ========== 13.3 Prompt Cache 共享测试 ========== func TestSpawnSubAgent_SharedSystemPromptBytes_FromConfig(t *testing.T) { // 父 Engine 的 Config 有 SharedSystemPromptBytes sharedBytes := json.RawMessage(`"shared system prompt bytes"`) cfg := testConfig() cfg.Cwd = "/tmp/test" cfg.SharedSystemPromptBytes = sharedBytes parent := &Engine{ cfg: cfg, tools: tools.NewRegistry(), observer: &NoopObserver{}, } saCfg := &SubAgentConfig{ Description: "test agent with shared bytes from parent config", } sa := SpawnSubAgent(parent, saCfg) // 应使用父 Engine Config 的 SharedSystemPromptBytes if string(sa.sharedSystemPromptBytes) != string(sharedBytes) { t.Errorf("应使用 parentEngine.cfg.SharedSystemPromptBytes,got %q", sa.sharedSystemPromptBytes) } // systemPrompt 应为空(共享模式下不调用 buildSystemPrompt) if sa.systemPrompt != "" { t.Errorf("共享模式下 systemPrompt 应为空,got %q", sa.systemPrompt) } } func TestSpawnSubAgent_SharedSystemPromptBytes_FromSubAgentConfig(t *testing.T) { // SubAgentConfig 显式传入 SharedSystemPromptBytes(最高优先级) cfg := testConfig() cfg.Cwd = "/tmp/test" // 父 Config 也有 SharedSystemPromptBytes,但 SubAgentConfig 的优先级更高 cfg.SharedSystemPromptBytes = json.RawMessage(`"parent config bytes"`) parent := &Engine{ cfg: cfg, tools: tools.NewRegistry(), observer: &NoopObserver{}, } cfgBytes := json.RawMessage(`"explicit subagent config bytes"`) saCfg := &SubAgentConfig{ Description: "test with explicit shared bytes", SharedSystemPromptBytes: cfgBytes, } sa := SpawnSubAgent(parent, saCfg) // SubAgentConfig 优先级 > parentEngine.cfg if string(sa.sharedSystemPromptBytes) != string(cfgBytes) { t.Errorf("SubAgentConfig.SharedSystemPromptBytes 应优先于父 Config,got %q", sa.sharedSystemPromptBytes) } } func TestSpawnSubAgent_SharedSystemPromptBytes_NilFallback(t *testing.T) { // 两者都为 nil → 退化为 buildSystemPrompt()(向后兼容) cfg := testConfig() cfg.Cwd = "/tmp/test" // SharedSystemPromptBytes 为 nil parent := &Engine{ cfg: cfg, tools: tools.NewRegistry(), observer: &NoopObserver{}, } saCfg := &SubAgentConfig{ Description: "fallback to independent render", // SharedSystemPromptBytes 为 nil } sa := SpawnSubAgent(parent, saCfg) // 向后兼容:sharedSystemPromptBytes 应为 nil,systemPrompt 由 buildSystemPrompt() 生成 if len(sa.sharedSystemPromptBytes) != 0 { t.Errorf("nil 共享字节时 sharedSystemPromptBytes 应为空,got %v", sa.sharedSystemPromptBytes) } // systemPrompt 可能是空字符串(buildSystemPrompt 在无注册 bundle 时),不 panic 就够 _ = sa.systemPrompt } // ========== 18.1 协调器提示词测试 ========== func TestBuildAppendPromptWithOrchestrator_NotOrchestrator(t *testing.T) { // isOrchestrator=false 时,原样返回 userAppend result := buildAppendPromptWithOrchestrator(false, "user text") if result != "user text" { t.Errorf("非协调器模式应原样返回 userAppend,got %q", result) } } func TestBuildAppendPromptWithOrchestrator_NotOrchestrator_Empty(t *testing.T) { result := buildAppendPromptWithOrchestrator(false, "") if result != "" { t.Errorf("非协调器模式且 userAppend 为空时应返回空,got %q", result) } } func TestBuildAppendPromptWithOrchestrator_IsOrchestrator_NoUserAppend(t *testing.T) { result := buildAppendPromptWithOrchestrator(true, "") if result != orchestratorGuidance { t.Errorf("协调器模式且 userAppend 为空时应返回 orchestratorGuidance,got %q", result) } } func TestBuildAppendPromptWithOrchestrator_IsOrchestrator_WithUserAppend(t *testing.T) { result := buildAppendPromptWithOrchestrator(true, "custom task") // 应该包含 orchestratorGuidance 和 "custom task" if !strings.Contains(result, orchestratorGuidance) { t.Error("协调器模式输出应包含 orchestratorGuidance") } if !strings.Contains(result, "custom task") { t.Error("协调器模式输出应包含用户追加文本") } // orchestratorGuidance 应在 userAppend 之前 guidanceIdx := strings.Index(result, orchestratorGuidance) userIdx := strings.Index(result, "custom task") if guidanceIdx > userIdx { t.Error("orchestratorGuidance 应在 userAppend 之前出现") } } func TestOrchestratorGuidance_ContainsKeyPhrases(t *testing.T) { // 验证协调器指导文本包含核心概念 keyPhrases := []string{ "协调器", "子代理", "子任务", } for _, phrase := range keyPhrases { if !strings.Contains(orchestratorGuidance, phrase) { t.Errorf("协调器指导应包含 %q", phrase) } } } func TestConfig_IsOrchestrator_DefaultFalse(t *testing.T) { cfg := &Config{} if cfg.IsOrchestrator { t.Error("IsOrchestrator 默认值应为 false") } } // ========== Engine scratchpad 初始化测试 ========== func TestEngine_Scratchpad_InitializedInNew(t *testing.T) { // 通过 Engine struct 直接测试(不需要完整 New()) sp := NewScratchpad() eng := &Engine{ cfg: &Config{}, scratchpad: sp, observer: &NoopObserver{}, } if eng.scratchpad == nil { t.Error("Engine 的 scratchpad 字段不应为 nil") } if len(eng.scratchpad.Keys()) != 0 { t.Error("初始 scratchpad 应为空") } } func TestEngine_Scratchpad_SharedWithTools(t *testing.T) { // 验证 Scratchpad 实例与注入到工具的是同一个 sp := NewScratchpad() sp.Set("test_key", "test_value", 0) // 模拟 ScratchpadStore 接口--sp 已实现 ScratchpadStore val, found := sp.Get("test_key") if !found || val != "test_value" { t.Errorf("Scratchpad 实例应能正常读写,found=%v val=%q", found, val) } }