Files
Sakurasan d5dac5a6fc feat: AutoZero 通用 Agent 框架(从 AutoGo 五阶段拆分完成)
llm(三协议 Provider)/ session(JSONL 持久化)/ tools(Registry、围栏、
builtin、computer)/ cdp(browser)/ skills / mcp / agent(循环、两级压缩、
编排、审批门控)+ 底座 jsonl/textx/execx/diag/scratch/spill/imagecache。

产品装配代码(config 桥接、schedule 工具、memory/plan/retro/prompt)
留在 AutoGo,宿主接入方式见 MIGRATION.md。
2026-10-26 00:54:46 +08:00

87 lines
2.6 KiB
Go

package skills
import (
"os"
"path/filepath"
"strings"
"testing"
)
func writeSkill(t *testing.T, root, name, content string) {
t.Helper()
dir := filepath.Join(root, name)
if err := os.MkdirAll(dir, 0o755); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(dir, "SKILL.md"), []byte(content), 0o644); err != nil {
t.Fatal(err)
}
}
// TestLoad 验证 frontmatter 解析、优先级覆盖与缺省名。
func TestLoad(t *testing.T) {
user := t.TempDir()
proj := t.TempDir()
writeSkill(t, user, "deploy", "---\nname: deploy\ndescription: \"How to deploy\"\nallowed-tools: read, grep\n---\n\nStep 1: run make deploy\n")
writeSkill(t, user, "notes", "No frontmatter here, just body.\n")
writeSkill(t, proj, "deploy", "---\ndescription: Project override\n---\nProject deploy steps.\n")
got := Load(proj, user) // 项目级优先
if len(got) != 2 {
t.Fatalf("want 2 skills, got %d: %+v", len(got), got)
}
byName := map[string]Skill{}
for _, s := range got {
byName[s.Name] = s
}
dep := byName["deploy"]
if dep.Description != "Project override" || !strings.Contains(dep.Body, "Project deploy steps") {
t.Fatalf("project skill should win: %+v", dep)
}
notes := byName["notes"]
if notes.Name != "notes" || !strings.Contains(notes.Body, "just body") {
t.Fatalf("missing frontmatter should fall back to dir name: %+v", notes)
}
if len(dep.AllowedTools) != 0 {
t.Fatalf("override should not inherit allowed-tools from user version")
}
// 用户级版本的字段
gotUser := Load(user)
var found bool
for _, s := range gotUser {
if s.Name == "deploy" {
found = true
if len(s.AllowedTools) != 2 || s.AllowedTools[0] != "read" || s.AllowedTools[1] != "grep" {
t.Fatalf("allowed-tools parse: %+v", s.AllowedTools)
}
if !strings.Contains(s.Body, "make deploy") {
t.Fatalf("body: %q", s.Body)
}
}
}
if !found {
t.Fatal("user deploy skill missing")
}
}
// TestPromptSection:列表注入、预算截断、空列表为空。
func TestPromptSection(t *testing.T) {
if PromptSection(nil) != "" {
t.Fatal("empty skills should produce empty section")
}
sk := []Skill{{Name: "deploy", Description: "How to deploy", AllowedTools: []string{"read"}}}
sec := PromptSection(sk)
if !strings.Contains(sec, "- deploy: How to deploy") || !strings.Contains(sec, "allowed-tools: read") {
t.Fatalf("section: %q", sec)
}
many := make([]Skill, 200)
for i := range many {
many[i] = Skill{Name: strings.Repeat("s", 40) + string(rune('a'+i%26)) + string(rune(i))}
}
if got := PromptSection(many); len(got) > maxPromptChars+100 {
t.Fatalf("budget exceeded: %d", len(got))
}
}