package recipe import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestLoad(t *testing.T) { t.Parallel() tmpDir := t.TempDir() recipePath := filepath.Join(tmpDir, "test.yaml") content := `--- name: Test Recipe description: A recipe for testing version: 1.0.0 parameters: target_file: type: string default: "main.go" description: File to process allowed_shell_commands: - ls -la - pwd project_languages: - go extensions: go: [".go"] search_pattern: "func main" --- ## Execution Steps ### Step 1: Discover **Objective:** Find files. **Instructions:** Use discovery. **Expected output:** List of files. ### Step 2: Refactor **Objective:** Refactor files. **Instructions:** Use refactoring. **Expected output:** JSON. This is the final summary prompt. ` err := os.WriteFile(recipePath, []byte(content), 0600) require.NoError(t, err) t.Run("successful load with defaults", func(t *testing.T) { r, err := Load(recipePath, nil) require.NoError(t, err) assert.Equal(t, "Test Recipe", r.Name) assert.Equal(t, "1.0.0", r.Version) assert.Equal(t, "main.go", r.ResolvedParams["target_file"]) assert.Len(t, r.Steps, 2) assert.Equal(t, 1, r.Steps[0].Number) assert.Equal(t, "Discover", r.Steps[0].Title) assert.Equal(t, "Find files.", r.Steps[0].Objective) assert.Equal(t, "Use discovery.", r.Steps[0].Instructions) assert.Equal(t, "List of files.", r.Steps[0].Expected) assert.Contains(t, r.FinalSummaryPrompt, "This is the final summary prompt.") }) t.Run("load with parameter override", func(t *testing.T) { params := map[string]any{"target_file": "app.go"} r, err := Load(recipePath, params) require.NoError(t, err) assert.Equal(t, "app.go", r.ResolvedParams["target_file"]) }) t.Run("load unsafe command", func(t *testing.T) { unsafeContent := `--- name: Unsafe Recipe allowed_shell_commands: - rm -rf / --- ## Execution Steps ### Step 1: Bad ` unsafePath := filepath.Join(tmpDir, "unsafe.yaml") err := os.WriteFile(unsafePath, []byte(unsafeContent), 0600) require.NoError(t, err) _, err = Load(unsafePath, nil) assert.Error(t, err) assert.Contains(t, err.Error(), "unsafe shell command") }) t.Run("missing frontmatter", func(t *testing.T) { badPath := filepath.Join(tmpDir, "bad.yaml") err := os.WriteFile(badPath, []byte("no frontmatter here"), 0600) require.NoError(t, err) _, err = Load(badPath, nil) assert.Error(t, err) assert.Contains(t, err.Error(), "missing YAML frontmatter") }) } func TestResolveWorkDir(t *testing.T) { // Not using t.Parallel() because it might depend on HOME env which we might want to mock if needed. r := &Recipe{ ResolvedParams: make(map[string]any), } runner := &Runner{Recipe: r} t.Run("default to dot", func(t *testing.T) { dir := runner.resolveWorkDir() absDot, _ := filepath.Abs(".") assert.Equal(t, absDot, dir) }) t.Run("explicit path", func(t *testing.T) { tmpDir := t.TempDir() r.ResolvedParams["package_path"] = tmpDir dir := runner.resolveWorkDir() absTmp, _ := filepath.Abs(tmpDir) assert.Equal(t, absTmp, dir) }) t.Run("tilde expansion", func(t *testing.T) { r.ResolvedParams["package_path"] = "~/projects" dir := runner.resolveWorkDir() home, _ := os.UserHomeDir() expected := filepath.Join(home, "projects") assert.Equal(t, expected, dir) }) } func TestDiscoverFiles(t *testing.T) { t.Parallel() tmpDir := t.TempDir() // Create some files require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "main.go"), []byte("func main() { if err != nil { return } }"), 0600)) require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "app.go"), []byte("package main"), 0600)) require.NoError(t, os.WriteFile(filepath.Join(tmpDir, "README.md"), []byte("if err != nil"), 0600)) r := &Recipe{ ProjectLanguages: []string{"go"}, Extensions: map[string][]string{"go": {".go"}}, SearchPattern: "if err != nil", } runner := &Runner{Recipe: r} t.Run("finds matching files", func(t *testing.T) { files := runner.discoverFiles(tmpDir) assert.Len(t, files, 1) assert.Contains(t, files[0], "main.go") }) t.Run("no matching files", func(t *testing.T) { r.SearchPattern = "nonexistent" files := runner.discoverFiles(tmpDir) assert.Len(t, files, 1) assert.Equal(t, "No files found matching the criteria.", files[0]) }) } func TestCreateUnifiedPatch(t *testing.T) { t.Parallel() tmpDir := t.TempDir() patchPath := filepath.Join(tmpDir, "test.patch") changes := []FileChange{ { File: "main.go", Content: "package main\n\nfunc main() {}\n", }, } err := createUnifiedPatch(changes, patchPath) require.NoError(t, err) content, err := os.ReadFile(patchPath) require.NoError(t, err) assert.Contains(t, string(content), "--- main.go") assert.Contains(t, string(content), "+++ main.go") assert.Contains(t, string(content), "+package main") assert.Contains(t, string(content), "+func main() {}") }