test: refactor factory setup for coverage (#790)

* test: add git helper and guard annotations

* chore: align release metadata with 0.40.0

* test: refactor factory setup for coverage
This commit is contained in:
Jordan Hubbard
2025-12-29 18:15:56 -04:00
committed by GitHub
parent 96c7cceefc
commit 8f8a075943
14 changed files with 316 additions and 490 deletions

View File

@@ -1,7 +1,9 @@
package setup
import (
"errors"
"fmt"
"io"
"os"
"strings"
)
@@ -100,127 +102,152 @@ For more details, see README.md and docs/QUICKSTART.md.
<!-- END BEADS INTEGRATION -->
`
var (
errAgentsFileMissing = errors.New("agents file not found")
errBeadsSectionMissing = errors.New("beads section missing")
)
type factoryEnv struct {
agentsPath string
stdout io.Writer
stderr io.Writer
}
var factoryEnvProvider = defaultFactoryEnv
func defaultFactoryEnv() factoryEnv {
return factoryEnv{
agentsPath: "AGENTS.md",
stdout: os.Stdout,
stderr: os.Stderr,
}
}
// InstallFactory installs Factory.ai/Droid integration
func InstallFactory() {
agentsPath := "AGENTS.md"
env := factoryEnvProvider()
if err := installFactory(env); err != nil {
setupExit(1)
}
}
fmt.Println("Installing Factory.ai (Droid) integration...")
func installFactory(env factoryEnv) error {
fmt.Fprintln(env.stdout, "Installing Factory.ai (Droid) integration...")
// Check if AGENTS.md exists
var currentContent string
data, err := os.ReadFile(agentsPath)
data, err := os.ReadFile(env.agentsPath)
if err == nil {
currentContent = string(data)
} else if !os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "Error: failed to read AGENTS.md: %v\n", err)
os.Exit(1)
fmt.Fprintf(env.stderr, "Error: failed to read %s: %v\n", env.agentsPath, err)
return err
}
// If file exists, check if we already have beads section
if currentContent != "" {
if strings.Contains(currentContent, factoryBeginMarker) {
// Update existing section
newContent := updateBeadsSection(currentContent)
if err := atomicWriteFile(agentsPath, []byte(newContent)); err != nil {
fmt.Fprintf(os.Stderr, "Error: write AGENTS.md: %v\n", err)
os.Exit(1)
if err := atomicWriteFile(env.agentsPath, []byte(newContent)); err != nil {
fmt.Fprintf(env.stderr, "Error: write %s: %v\n", env.agentsPath, err)
return err
}
fmt.Println("✓ Updated existing beads section in AGENTS.md")
fmt.Fprintln(env.stdout, "✓ Updated existing beads section in AGENTS.md")
} else {
// Append to existing file
newContent := currentContent + "\n\n" + factoryBeadsSection
if err := atomicWriteFile(agentsPath, []byte(newContent)); err != nil {
fmt.Fprintf(os.Stderr, "Error: write AGENTS.md: %v\n", err)
os.Exit(1)
if err := atomicWriteFile(env.agentsPath, []byte(newContent)); err != nil {
fmt.Fprintf(env.stderr, "Error: write %s: %v\n", env.agentsPath, err)
return err
}
fmt.Println("✓ Added beads section to existing AGENTS.md")
fmt.Fprintln(env.stdout, "✓ Added beads section to existing AGENTS.md")
}
} else {
// Create new AGENTS.md with template
newContent := createNewAgentsFile()
if err := atomicWriteFile(agentsPath, []byte(newContent)); err != nil {
fmt.Fprintf(os.Stderr, "Error: write AGENTS.md: %v\n", err)
os.Exit(1)
if err := atomicWriteFile(env.agentsPath, []byte(newContent)); err != nil {
fmt.Fprintf(env.stderr, "Error: write %s: %v\n", env.agentsPath, err)
return err
}
fmt.Println("✓ Created new AGENTS.md with beads integration")
fmt.Fprintln(env.stdout, "✓ Created new AGENTS.md with beads integration")
}
fmt.Printf("\n✓ Factory.ai (Droid) integration installed\n")
fmt.Printf(" File: %s\n", agentsPath)
fmt.Println("\nFactory Droid will automatically read AGENTS.md on session start.")
fmt.Println("No additional configuration needed!")
fmt.Fprintln(env.stdout, "\n✓ Factory.ai (Droid) integration installed")
fmt.Fprintf(env.stdout, " File: %s\n", env.agentsPath)
fmt.Fprintln(env.stdout, "\nFactory Droid will automatically read AGENTS.md on session start.")
fmt.Fprintln(env.stdout, "No additional configuration needed!")
return nil
}
// CheckFactory checks if Factory.ai integration is installed
func CheckFactory() {
agentsPath := "AGENTS.md"
env := factoryEnvProvider()
if err := checkFactory(env); err != nil {
setupExit(1)
}
}
// Check if AGENTS.md exists
data, err := os.ReadFile(agentsPath)
func checkFactory(env factoryEnv) error {
data, err := os.ReadFile(env.agentsPath)
if os.IsNotExist(err) {
fmt.Println("✗ AGENTS.md not found")
fmt.Println(" Run: bd setup factory")
os.Exit(1)
fmt.Fprintln(env.stdout, "✗ AGENTS.md not found")
fmt.Fprintln(env.stdout, " Run: bd setup factory")
return errAgentsFileMissing
} else if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to read AGENTS.md: %v\n", err)
os.Exit(1)
fmt.Fprintf(env.stderr, "Error: failed to read %s: %v\n", env.agentsPath, err)
return err
}
// Check if it contains beads section
content := string(data)
if strings.Contains(content, factoryBeginMarker) {
fmt.Println("✓ Factory.ai integration installed:", agentsPath)
fmt.Println(" Beads section found in AGENTS.md")
} else {
fmt.Println("⚠ AGENTS.md exists but no beads section found")
fmt.Println(" Run: bd setup factory (to add beads section)")
os.Exit(1)
fmt.Fprintf(env.stdout, "✓ Factory.ai integration installed: %s\n", env.agentsPath)
fmt.Fprintln(env.stdout, " Beads section found in AGENTS.md")
return nil
}
fmt.Fprintln(env.stdout, "⚠ AGENTS.md exists but no beads section found")
fmt.Fprintln(env.stdout, " Run: bd setup factory (to add beads section)")
return errBeadsSectionMissing
}
// RemoveFactory removes Factory.ai integration
func RemoveFactory() {
agentsPath := "AGENTS.md"
env := factoryEnvProvider()
if err := removeFactory(env); err != nil {
setupExit(1)
}
}
fmt.Println("Removing Factory.ai (Droid) integration...")
// Read current content
data, err := os.ReadFile(agentsPath)
func removeFactory(env factoryEnv) error {
fmt.Fprintln(env.stdout, "Removing Factory.ai (Droid) integration...")
data, err := os.ReadFile(env.agentsPath)
if os.IsNotExist(err) {
fmt.Println("No AGENTS.md file found")
return
fmt.Fprintln(env.stdout, "No AGENTS.md file found")
return nil
} else if err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to read AGENTS.md: %v\n", err)
os.Exit(1)
fmt.Fprintf(env.stderr, "Error: failed to read %s: %v\n", env.agentsPath, err)
return err
}
content := string(data)
// Check if beads section exists
if !strings.Contains(content, factoryBeginMarker) {
fmt.Println("No beads section found in AGENTS.md")
return
fmt.Fprintln(env.stdout, "No beads section found in AGENTS.md")
return nil
}
// Remove beads section
newContent := removeBeadsSection(content)
// If file would be empty after removal, delete it
trimmed := strings.TrimSpace(newContent)
if trimmed == "" {
if err := os.Remove(agentsPath); err != nil {
fmt.Fprintf(os.Stderr, "Error: failed to remove AGENTS.md: %v\n", err)
os.Exit(1)
if err := os.Remove(env.agentsPath); err != nil {
fmt.Fprintf(env.stderr, "Error: failed to remove %s: %v\n", env.agentsPath, err)
return err
}
fmt.Println("✓ Removed AGENTS.md (file was empty after removing beads section)")
} else {
// Write back modified content
if err := atomicWriteFile(agentsPath, []byte(newContent)); err != nil {
fmt.Fprintf(os.Stderr, "Error: write AGENTS.md: %v\n", err)
os.Exit(1)
}
fmt.Println("✓ Removed beads section from AGENTS.md")
fmt.Fprintf(env.stdout, "✓ Removed %s (file was empty after removing beads section)\n", env.agentsPath)
return nil
}
if err := atomicWriteFile(env.agentsPath, []byte(newContent)); err != nil {
fmt.Fprintf(env.stderr, "Error: write %s: %v\n", env.agentsPath, err)
return err
}
fmt.Fprintln(env.stdout, "✓ Removed beads section from AGENTS.md")
return nil
}
// updateBeadsSection replaces the beads section in existing content