Files
tutti/packages/agent/runtimeprep/stable_skill_bundle_test.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

221 lines
7.1 KiB
Go

package runtimeprep
import (
"context"
"encoding/json"
"os"
"path/filepath"
"strings"
"sync"
"testing"
)
func TestTuttiAgentStableSkillsReuseRootAcrossSessions(t *testing.T) {
stateDir := t.TempDir()
storeRoot := filepath.Join(stateDir, "agent", "skill-bundles")
preparer := TuttiAgentPreparer{
ResolveAuthSource: func(context.Context, PrepareInput) (string, error) {
return "", nil
},
StableSkillBundleRoot: storeRoot,
StableSystemSkillBundleRoot: filepath.Join(stateDir, "agent", "system-skill-bundles"),
}
wantRoot := ""
for _, sessionID := range []string{"session-a", "session-b"} {
runtimeRoot := filepath.Join(stateDir, "agent", "runs", sessionID)
result, err := preparer.Prepare(t.Context(), ProviderPrepareInput{
PrepareInput: testResolvedInput(t, PrepareInput{
AgentSessionID: sessionID,
AgentTargetID: "local:tutti-agent",
Provider: "tutti-agent",
CLICommand: "tutti",
}),
RuntimeRoot: runtimeRoot,
Store: LocalStore{StateDir: stateDir},
})
if err != nil {
t.Fatalf("Prepare(%s) error = %v", sessionID, err)
}
var roots []string
if err := json.Unmarshal(
[]byte(envValue(result.Env, "TUTTI_AGENT_EXTRA_SKILL_ROOTS_JSON")),
&roots,
); err != nil {
t.Fatalf("Prepare(%s) extra roots = %#v: %v", sessionID, result.Env, err)
}
if len(roots) != 1 || !filepath.IsAbs(roots[0]) {
t.Fatalf("Prepare(%s) roots = %#v, want one absolute root", sessionID, roots)
}
if root := envValue(result.Env, "TUTTI_AGENT_STABLE_SYSTEM_SKILLS_ROOT"); root != filepath.Join(stateDir, "agent", "system-skill-bundles") {
t.Fatalf("Prepare(%s) stable system root = %q", sessionID, root)
}
if wantRoot == "" {
wantRoot = roots[0]
} else if roots[0] != wantRoot {
t.Fatalf("stable roots differ: %q != %q", roots[0], wantRoot)
}
home := filepath.Join(runtimeRoot, "tutti-agent-home")
if _, err := os.Stat(filepath.Join(home, "skills")); !os.IsNotExist(err) {
t.Fatalf("Prepare(%s) created legacy skills root, err = %v", sessionID, err)
}
instructions, err := os.ReadFile(filepath.Join(home, "AGENTS.md"))
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(instructions), sessionID) {
t.Fatalf("Prepare(%s) embedded session id in AGENTS.md", sessionID)
}
}
if _, err := os.Stat(filepath.Join(wantRoot, tuttiSkillName, "SKILL.md")); err != nil {
t.Fatalf("stable tutti-cli skill missing: %v", err)
}
if !strings.Contains(filepath.ToSlash(wantRoot), "/agent/skill-bundles/v1/") {
t.Fatalf("stable root = %q, want versioned content-addressed path", wantRoot)
}
}
func TestTuttiAgentAddsVerifiedConnectorSkillRoots(t *testing.T) {
stateDir := t.TempDir()
connectorRoot := filepath.Join(stateDir, "connectors", "lark-cli", "skills")
if err := os.MkdirAll(connectorRoot, 0o700); err != nil {
t.Fatal(err)
}
preparer := TuttiAgentPreparer{
ResolveAuthSource: func(context.Context, PrepareInput) (string, error) { return "", nil },
StableSkillBundleRoot: filepath.Join(stateDir, "agent", "skill-bundles"),
}
result, err := preparer.Prepare(t.Context(), ProviderPrepareInput{
PrepareInput: testResolvedInput(t, PrepareInput{
AgentSessionID: "session-a",
AgentTargetID: "local:tutti-agent",
Provider: "tutti-agent",
CLICommand: "tutti",
ConnectorRoutingHints: []ConnectorRoutingHint{
{ConnectorKey: "lark-cli", SkillRoot: connectorRoot},
{ConnectorKey: "lark-cli-copy", SkillRoot: connectorRoot},
},
}),
RuntimeRoot: filepath.Join(stateDir, "agent", "runs", "session-a"),
Store: LocalStore{StateDir: stateDir},
})
if err != nil {
t.Fatal(err)
}
var roots []string
if err := json.Unmarshal([]byte(envValue(result.Env, "TUTTI_AGENT_EXTRA_SKILL_ROOTS_JSON")), &roots); err != nil {
t.Fatal(err)
}
if len(roots) != 2 || roots[1] != connectorRoot {
t.Fatalf("extra roots = %#v, want stable provider root followed by deduplicated connector root", roots)
}
}
func TestConnectorSkillRootsRejectsRelativeOrSymlinkRoots(t *testing.T) {
if _, err := connectorSkillRoots([]ConnectorRoutingHint{{ConnectorKey: "demo", SkillRoot: "relative/skills"}}); err == nil {
t.Fatal("relative connector Skill root was accepted")
}
realRoot := t.TempDir()
link := filepath.Join(t.TempDir(), "skills")
if err := os.Symlink(realRoot, link); err != nil {
t.Fatal(err)
}
if _, err := connectorSkillRoots([]ConnectorRoutingHint{{ConnectorKey: "demo", SkillRoot: link}}); err == nil {
t.Fatal("symlink connector Skill root was accepted")
}
}
func TestStableSkillBundleDigestChangesWithContentNotMapOrder(t *testing.T) {
specA := providerSkillSpec{
baseName: "sample",
skillID: "example/sample",
files: map[string]string{
"SKILL.md": "sample",
"references/guide.md": "guide",
},
}
specB := providerSkillSpec{
baseName: "sample",
skillID: "example/sample",
files: map[string]string{
"references/guide.md": "guide",
"SKILL.md": "sample",
},
}
first, _, err := canonicalizeStableProviderSkills("tutti-agent", []providerSkillSpec{specA})
if err != nil {
t.Fatal(err)
}
second, _, err := canonicalizeStableProviderSkills("tutti-agent", []providerSkillSpec{specB})
if err != nil {
t.Fatal(err)
}
firstJSON, _ := json.Marshal(first)
secondJSON, _ := json.Marshal(second)
if string(firstJSON) != string(secondJSON) {
t.Fatalf("canonical bundle depends on map order:\n%s\n%s", firstJSON, secondJSON)
}
specB.files["SKILL.md"] = "changed"
changed, _, err := canonicalizeStableProviderSkills("tutti-agent", []providerSkillSpec{specB})
if err != nil {
t.Fatal(err)
}
changedJSON, _ := json.Marshal(changed)
if string(firstJSON) == string(changedJSON) {
t.Fatal("canonical bundle did not change with skill content")
}
}
func TestStableSkillBundleConcurrentMaterialization(t *testing.T) {
input := testResolvedInput(t, PrepareInput{
AgentSessionID: "session-a",
AgentTargetID: "local:tutti-agent",
Provider: "tutti-agent",
CLICommand: "tutti",
})
storeRoot := filepath.Join(t.TempDir(), "skill-bundles")
const workers = 8
roots := make(chan string, workers)
errors := make(chan error, workers)
var group sync.WaitGroup
for index := 0; index < workers; index++ {
group.Add(1)
go func() {
defer group.Done()
root, err := materializeStableProviderSkills(storeRoot, input)
if err != nil {
errors <- err
return
}
roots <- root
}()
}
group.Wait()
close(roots)
close(errors)
for err := range errors {
t.Fatalf("materializeStableProviderSkills() error = %v", err)
}
want := ""
for root := range roots {
if want == "" {
want = root
} else if root != want {
t.Fatalf("concurrent roots differ: %q != %q", root, want)
}
}
if _, err := os.Stat(filepath.Join(want, tuttiSkillName, "SKILL.md")); err != nil {
t.Fatalf("concurrent bundle is incomplete: %v", err)
}
}
func TestStableSkillBundleRejectsEscapingSkillName(t *testing.T) {
_, _, err := canonicalizeStableProviderSkills("tutti-agent", []providerSkillSpec{{
baseName: "../escape",
skillID: "example/escape",
files: map[string]string{"SKILL.md": "escape"},
}})
if err == nil {
t.Fatal("canonicalizeStableProviderSkills() error = nil, want escaping name rejection")
}
}