package cmd import ( "os" "path/filepath" "testing" ) func TestResolveRecipePathDirectFile(t *testing.T) { tmpDir := t.TempDir() recipePath := filepath.Join(tmpDir, "my-recipe.md") if err := os.WriteFile(recipePath, []byte("# Recipe\n"), 0644); err != nil { t.Fatal(err) } got, err := resolveRecipePath(recipePath) if err != nil { t.Fatalf("resolveRecipePath(%q) failed: %v", recipePath, err) } if got != recipePath { t.Errorf("resolveRecipePath() = %q, want %q", got, recipePath) } } func TestResolveRecipePathDirectFileMissing(t *testing.T) { _, err := resolveRecipePath("/nonexistent/recipe.md") if err == nil { t.Fatal("expected error for missing recipe file, got nil") } } func TestResolveRecipePathProjectLocal(t *testing.T) { tmpDir := t.TempDir() origDir, _ := os.Getwd() if err := os.Chdir(tmpDir); err != nil { t.Fatal(err) } defer func() { _ = os.Chdir(origDir) }() // Create .grokkit/recipes with a recipe, and a .git dir for project root detection recipeDir := filepath.Join(tmpDir, ".grokkit", "recipes") if err := os.MkdirAll(recipeDir, 0755); err != nil { t.Fatal(err) } if err := os.MkdirAll(filepath.Join(tmpDir, ".git"), 0755); err != nil { t.Fatal(err) } recipePath := filepath.Join(recipeDir, "refactor.md") if err := os.WriteFile(recipePath, []byte("# Refactor\n"), 0644); err != nil { t.Fatal(err) } got, err := resolveRecipePath("refactor") if err != nil { t.Fatalf("resolveRecipePath('refactor') failed: %v", err) } if got != recipePath { t.Errorf("resolveRecipePath() = %q, want %q", got, recipePath) } } func TestResolveRecipePathNotFound(t *testing.T) { tmpDir := t.TempDir() origDir, _ := os.Getwd() if err := os.Chdir(tmpDir); err != nil { t.Fatal(err) } defer func() { _ = os.Chdir(origDir) }() // Create .git so findProjectRoot works, but no recipes if err := os.MkdirAll(filepath.Join(tmpDir, ".git"), 0755); err != nil { t.Fatal(err) } // Override HOME to empty dir so global fallback also fails oldHome := os.Getenv("HOME") defer func() { _ = os.Setenv("HOME", oldHome) }() _ = os.Setenv("HOME", tmpDir) _, err := resolveRecipePath("nonexistent") if err == nil { t.Fatal("expected error for nonexistent recipe, got nil") } } func TestFindProjectRoot(t *testing.T) { tests := []struct { name string marker string }{ {"git repo", ".git"}, {"gitignore", ".gitignore"}, {"grokkit dir", ".grokkit"}, {"go module", "go.mod"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tmpDir := t.TempDir() origDir, _ := os.Getwd() // Create a subdirectory and chdir into it subDir := filepath.Join(tmpDir, "sub", "deep") if err := os.MkdirAll(subDir, 0755); err != nil { t.Fatal(err) } if err := os.Chdir(subDir); err != nil { t.Fatal(err) } defer func() { _ = os.Chdir(origDir) }() // Place the marker at the root markerPath := filepath.Join(tmpDir, tt.marker) if tt.marker == ".git" || tt.marker == ".grokkit" { if err := os.MkdirAll(markerPath, 0755); err != nil { t.Fatal(err) } } else { if err := os.WriteFile(markerPath, []byte(""), 0644); err != nil { t.Fatal(err) } } root, err := findProjectRoot() if err != nil { t.Fatalf("findProjectRoot() failed: %v", err) } if root != tmpDir { t.Errorf("findProjectRoot() = %q, want %q", root, tmpDir) } }) } } func TestFindProjectRootNotInProject(t *testing.T) { tmpDir := t.TempDir() origDir, _ := os.Getwd() if err := os.Chdir(tmpDir); err != nil { t.Fatal(err) } defer func() { _ = os.Chdir(origDir) }() // Empty dir — no markers at all. This will walk up to / and fail. _, err := findProjectRoot() if err == nil { // Might succeed if a parent dir has .git — that's OK in CI t.Log("findProjectRoot() succeeded (likely found .git in a parent dir)") } }