package polecat import ( "os" "path/filepath" "testing" "github.com/steveyegge/gastown/internal/git" "github.com/steveyegge/gastown/internal/rig" ) func TestStateIsActive(t *testing.T) { tests := []struct { state State active bool }{ {StateWorking, true}, {StateDone, false}, {StateStuck, false}, // Legacy states are treated as active {StateIdle, true}, {StateActive, true}, } for _, tt := range tests { if got := tt.state.IsActive(); got != tt.active { t.Errorf("%s.IsActive() = %v, want %v", tt.state, got, tt.active) } } } func TestStateIsWorking(t *testing.T) { tests := []struct { state State working bool }{ {StateIdle, false}, {StateActive, false}, {StateWorking, true}, {StateDone, false}, {StateStuck, false}, } for _, tt := range tests { if got := tt.state.IsWorking(); got != tt.working { t.Errorf("%s.IsWorking() = %v, want %v", tt.state, got, tt.working) } } } func TestPolecatSummary(t *testing.T) { p := &Polecat{ Name: "Toast", State: StateWorking, Issue: "gt-abc", } summary := p.Summary() if summary.Name != "Toast" { t.Errorf("Name = %q, want Toast", summary.Name) } if summary.State != StateWorking { t.Errorf("State = %v, want StateWorking", summary.State) } if summary.Issue != "gt-abc" { t.Errorf("Issue = %q, want gt-abc", summary.Issue) } } func TestListEmpty(t *testing.T) { root := t.TempDir() r := &rig.Rig{ Name: "test-rig", Path: root, } m := NewManager(r, git.NewGit(root)) polecats, err := m.List() if err != nil { t.Fatalf("List: %v", err) } if len(polecats) != 0 { t.Errorf("polecats count = %d, want 0", len(polecats)) } } func TestGetNotFound(t *testing.T) { root := t.TempDir() r := &rig.Rig{ Name: "test-rig", Path: root, } m := NewManager(r, git.NewGit(root)) _, err := m.Get("nonexistent") if err != ErrPolecatNotFound { t.Errorf("Get = %v, want ErrPolecatNotFound", err) } } func TestRemoveNotFound(t *testing.T) { root := t.TempDir() r := &rig.Rig{ Name: "test-rig", Path: root, } m := NewManager(r, git.NewGit(root)) err := m.Remove("nonexistent", false) if err != ErrPolecatNotFound { t.Errorf("Remove = %v, want ErrPolecatNotFound", err) } } func TestPolecatDir(t *testing.T) { r := &rig.Rig{ Name: "test-rig", Path: "/home/user/ai/test-rig", } m := NewManager(r, git.NewGit(r.Path)) dir := m.polecatDir("Toast") expected := "/home/user/ai/test-rig/polecats/Toast" if dir != expected { t.Errorf("polecatDir = %q, want %q", dir, expected) } } func TestAssigneeID(t *testing.T) { r := &rig.Rig{ Name: "test-rig", Path: "/home/user/ai/test-rig", } m := NewManager(r, git.NewGit(r.Path)) id := m.assigneeID("Toast") expected := "test-rig/Toast" if id != expected { t.Errorf("assigneeID = %q, want %q", id, expected) } } // Note: State persistence tests removed - state is now derived from beads assignee field. // Integration tests should verify beads-based state management. func TestGetReturnsIdleWithoutBeads(t *testing.T) { // When beads is not available, Get should return StateIdle root := t.TempDir() polecatDir := filepath.Join(root, "polecats", "Test") if err := os.MkdirAll(polecatDir, 0755); err != nil { t.Fatalf("mkdir: %v", err) } // Create mayor/rig directory for beads (but no actual beads) mayorRigDir := filepath.Join(root, "mayor", "rig") if err := os.MkdirAll(mayorRigDir, 0755); err != nil { t.Fatalf("mkdir mayor/rig: %v", err) } r := &rig.Rig{ Name: "test-rig", Path: root, } m := NewManager(r, git.NewGit(root)) // Get should return polecat with StateIdle (no beads = no assignment) polecat, err := m.Get("Test") if err != nil { t.Fatalf("Get: %v", err) } if polecat.Name != "Test" { t.Errorf("Name = %q, want Test", polecat.Name) } if polecat.State != StateIdle { t.Errorf("State = %v, want StateIdle (beads not available)", polecat.State) } } func TestListWithPolecats(t *testing.T) { root := t.TempDir() // Create some polecat directories (state is now derived from beads, not state files) for _, name := range []string{"Toast", "Cheedo"} { polecatDir := filepath.Join(root, "polecats", name) if err := os.MkdirAll(polecatDir, 0755); err != nil { t.Fatalf("mkdir: %v", err) } } // Create mayor/rig for beads path mayorRig := filepath.Join(root, "mayor", "rig") if err := os.MkdirAll(mayorRig, 0755); err != nil { t.Fatalf("mkdir mayor/rig: %v", err) } r := &rig.Rig{ Name: "test-rig", Path: root, } m := NewManager(r, git.NewGit(root)) polecats, err := m.List() if err != nil { t.Fatalf("List: %v", err) } if len(polecats) != 2 { t.Errorf("polecats count = %d, want 2", len(polecats)) } } // Note: TestSetState, TestAssignIssue, and TestClearIssue were removed. // These operations now require a running beads instance and are tested // via integration tests. The unit tests here focus on testing the basic // polecat lifecycle operations that don't require beads. func TestSetStateWithoutBeads(t *testing.T) { // SetState should not error when beads is not available root := t.TempDir() polecatDir := filepath.Join(root, "polecats", "Test") if err := os.MkdirAll(polecatDir, 0755); err != nil { t.Fatalf("mkdir: %v", err) } // Create mayor/rig for beads path mayorRig := filepath.Join(root, "mayor", "rig") if err := os.MkdirAll(mayorRig, 0755); err != nil { t.Fatalf("mkdir mayor/rig: %v", err) } r := &rig.Rig{ Name: "test-rig", Path: root, } m := NewManager(r, git.NewGit(root)) // SetState should succeed (no-op when no issue assigned) err := m.SetState("Test", StateActive) if err != nil { t.Errorf("SetState: %v (expected no error when no beads/issue)", err) } } func TestClearIssueWithoutAssignment(t *testing.T) { // ClearIssue should not error when no issue is assigned root := t.TempDir() polecatDir := filepath.Join(root, "polecats", "Test") if err := os.MkdirAll(polecatDir, 0755); err != nil { t.Fatalf("mkdir: %v", err) } // Create mayor/rig for beads path mayorRig := filepath.Join(root, "mayor", "rig") if err := os.MkdirAll(mayorRig, 0755); err != nil { t.Fatalf("mkdir mayor/rig: %v", err) } r := &rig.Rig{ Name: "test-rig", Path: root, } m := NewManager(r, git.NewGit(root)) // ClearIssue should succeed even when no issue assigned err := m.ClearIssue("Test") if err != nil { t.Errorf("ClearIssue: %v (expected no error when no assignment)", err) } }