/ internal / agents / embed_test.go
embed_test.go
  1  package agents
  2  
  3  import (
  4  	"io/fs"
  5  	"os"
  6  	"path/filepath"
  7  	"sort"
  8  	"testing"
  9  )
 10  
 11  func TestEnsureBuiltins_CreatesOnFirstRun(t *testing.T) {
 12  	dir := t.TempDir()
 13  	agentsDir := filepath.Join(dir, "agents")
 14  	os.MkdirAll(agentsDir, 0700)
 15  
 16  	err := EnsureBuiltins(agentsDir, "0.0.99-test")
 17  	if err != nil {
 18  		t.Fatalf("EnsureBuiltins: %v", err)
 19  	}
 20  
 21  	// Verify explorer
 22  	data, err := os.ReadFile(filepath.Join(agentsDir, "_builtin", "explorer", "AGENT.md"))
 23  	if err != nil {
 24  		t.Fatalf("explorer AGENT.md missing: %v", err)
 25  	}
 26  	if len(data) == 0 {
 27  		t.Fatal("explorer AGENT.md is empty")
 28  	}
 29  
 30  	// Verify reviewer
 31  	data, err = os.ReadFile(filepath.Join(agentsDir, "_builtin", "reviewer", "AGENT.md"))
 32  	if err != nil {
 33  		t.Fatalf("reviewer AGENT.md missing: %v", err)
 34  	}
 35  	if len(data) == 0 {
 36  		t.Fatal("reviewer AGENT.md is empty")
 37  	}
 38  
 39  	// Verify version file
 40  	ver, err := os.ReadFile(filepath.Join(agentsDir, "_builtin", ".version"))
 41  	if err != nil {
 42  		t.Fatalf(".version missing: %v", err)
 43  	}
 44  	if string(ver) != "0.0.99-test" {
 45  		t.Fatalf("expected version 0.0.99-test, got %s", string(ver))
 46  	}
 47  }
 48  
 49  func TestEnsureBuiltins_SkipsWhenVersionMatches(t *testing.T) {
 50  	dir := t.TempDir()
 51  	agentsDir := filepath.Join(dir, "agents")
 52  	os.MkdirAll(agentsDir, 0700)
 53  
 54  	// First run
 55  	EnsureBuiltins(agentsDir, "0.0.99")
 56  
 57  	// Modify a file to detect if it gets overwritten
 58  	marker := filepath.Join(agentsDir, "_builtin", "explorer", "AGENT.md")
 59  	os.WriteFile(marker, []byte("modified"), 0600)
 60  
 61  	// Second run with same version
 62  	EnsureBuiltins(agentsDir, "0.0.99")
 63  
 64  	data, _ := os.ReadFile(marker)
 65  	if string(data) != "modified" {
 66  		t.Fatal("EnsureBuiltins overwrote files when version matched")
 67  	}
 68  }
 69  
 70  func TestEnsureBuiltins_OverwritesOnVersionChange(t *testing.T) {
 71  	dir := t.TempDir()
 72  	agentsDir := filepath.Join(dir, "agents")
 73  	os.MkdirAll(agentsDir, 0700)
 74  
 75  	EnsureBuiltins(agentsDir, "0.0.98")
 76  
 77  	marker := filepath.Join(agentsDir, "_builtin", "explorer", "AGENT.md")
 78  	os.WriteFile(marker, []byte("modified"), 0600)
 79  
 80  	// Upgrade
 81  	EnsureBuiltins(agentsDir, "0.0.99")
 82  
 83  	data, _ := os.ReadFile(marker)
 84  	if string(data) == "modified" {
 85  		t.Fatal("EnsureBuiltins did not overwrite on version change")
 86  	}
 87  }
 88  
 89  func TestMaterializeBuiltin(t *testing.T) {
 90  	dir := t.TempDir()
 91  	agentsDir := filepath.Join(dir, "agents")
 92  
 93  	builtinDir := filepath.Join(agentsDir, "_builtin", "explorer")
 94  	os.MkdirAll(builtinDir, 0700)
 95  	os.WriteFile(filepath.Join(builtinDir, "AGENT.md"), []byte("builtin prompt"), 0600)
 96  	os.WriteFile(filepath.Join(builtinDir, "config.yaml"), []byte("tools:\n  allow: [bash]"), 0600)
 97  
 98  	userDir := filepath.Join(agentsDir, "explorer")
 99  	if _, err := os.Stat(filepath.Join(userDir, "AGENT.md")); err == nil {
100  		t.Fatal("user dir should not exist before materialization")
101  	}
102  
103  	err := MaterializeBuiltin(agentsDir, "explorer")
104  	if err != nil {
105  		t.Fatalf("MaterializeBuiltin: %v", err)
106  	}
107  
108  	data, err := os.ReadFile(filepath.Join(userDir, "AGENT.md"))
109  	if err != nil || string(data) != "builtin prompt" {
110  		t.Fatalf("AGENT.md not materialized correctly")
111  	}
112  	data, err = os.ReadFile(filepath.Join(userDir, "config.yaml"))
113  	if err != nil || string(data) != "tools:\n  allow: [bash]" {
114  		t.Fatalf("config.yaml not materialized correctly")
115  	}
116  }
117  
118  func TestBuiltinNames_MatchEmbeddedDirs(t *testing.T) {
119  	entries, err := fs.ReadDir(builtinFS, "builtin")
120  	if err != nil {
121  		t.Fatalf("reading embedded FS: %v", err)
122  	}
123  	var embedded []string
124  	for _, e := range entries {
125  		if e.IsDir() {
126  			embedded = append(embedded, e.Name())
127  		}
128  	}
129  	sort.Strings(embedded)
130  
131  	names := make([]string, len(BuiltinNames))
132  	copy(names, BuiltinNames)
133  	sort.Strings(names)
134  
135  	if len(embedded) != len(names) {
136  		t.Fatalf("BuiltinNames has %d entries but embedded FS has %d dirs: embedded=%v names=%v",
137  			len(names), len(embedded), embedded, names)
138  	}
139  	for i := range embedded {
140  		if embedded[i] != names[i] {
141  			t.Fatalf("mismatch at index %d: embedded=%q names=%q", i, embedded[i], names[i])
142  		}
143  	}
144  }
145  
146  func TestMaterializeBuiltin_SkipsMemory(t *testing.T) {
147  	dir := t.TempDir()
148  	agentsDir := filepath.Join(dir, "agents")
149  	builtinDir := filepath.Join(agentsDir, "_builtin", "explorer")
150  	os.MkdirAll(builtinDir, 0700)
151  	os.WriteFile(filepath.Join(builtinDir, "AGENT.md"), []byte("prompt"), 0600)
152  	os.WriteFile(filepath.Join(builtinDir, "MEMORY.md"), []byte("should not copy"), 0600)
153  
154  	MaterializeBuiltin(agentsDir, "explorer")
155  
156  	userDir := filepath.Join(agentsDir, "explorer")
157  	if _, err := os.Stat(filepath.Join(userDir, "MEMORY.md")); err == nil {
158  		t.Fatal("MEMORY.md should not be materialized")
159  	}
160  }