package beads import ( "os" "path/filepath" "testing" ) // TestNew verifies the constructor. func TestNew(t *testing.T) { b := New("/some/path") if b == nil { t.Fatal("New returned nil") } if b.workDir != "/some/path" { t.Errorf("workDir = %q, want /some/path", b.workDir) } } // TestListOptions verifies ListOptions defaults. func TestListOptions(t *testing.T) { opts := ListOptions{ Status: "open", Type: "task", Priority: 1, } if opts.Status != "open" { t.Errorf("Status = %q, want open", opts.Status) } } // TestCreateOptions verifies CreateOptions fields. func TestCreateOptions(t *testing.T) { opts := CreateOptions{ Title: "Test issue", Type: "task", Priority: 2, Description: "A test description", Parent: "gt-abc", } if opts.Title != "Test issue" { t.Errorf("Title = %q, want 'Test issue'", opts.Title) } if opts.Parent != "gt-abc" { t.Errorf("Parent = %q, want gt-abc", opts.Parent) } } // TestUpdateOptions verifies UpdateOptions pointer fields. func TestUpdateOptions(t *testing.T) { status := "in_progress" priority := 1 opts := UpdateOptions{ Status: &status, Priority: &priority, } if *opts.Status != "in_progress" { t.Errorf("Status = %q, want in_progress", *opts.Status) } if *opts.Priority != 1 { t.Errorf("Priority = %d, want 1", *opts.Priority) } } // TestIsBeadsRepo tests repository detection. func TestIsBeadsRepo(t *testing.T) { // Test with a non-beads directory tmpDir, err := os.MkdirTemp("", "beads-test-*") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpDir) b := New(tmpDir) // This should return false since there's no .beads directory // and bd list will fail if b.IsBeadsRepo() { // This might pass if bd handles missing .beads gracefully t.Log("IsBeadsRepo returned true for non-beads directory (bd might initialize)") } } // TestWrapError tests error wrapping. func TestWrapError(t *testing.T) { b := New("/test") tests := []struct { stderr string wantErr error wantNil bool }{ {"not a beads repository", ErrNotARepo, false}, {"No .beads directory found", ErrNotARepo, false}, {".beads directory not found", ErrNotARepo, false}, {"sync conflict detected", ErrSyncConflict, false}, {"CONFLICT in file.md", ErrSyncConflict, false}, {"Issue not found: gt-xyz", ErrNotFound, false}, {"gt-xyz not found", ErrNotFound, false}, } for _, tt := range tests { err := b.wrapError(nil, tt.stderr, []string{"test"}) if tt.wantNil { if err != nil { t.Errorf("wrapError(%q) = %v, want nil", tt.stderr, err) } } else { if err != tt.wantErr { t.Errorf("wrapError(%q) = %v, want %v", tt.stderr, err, tt.wantErr) } } } } // Integration test that runs against real bd if available func TestIntegration(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } // Find a beads repo (use current directory if it has .beads) cwd, err := os.Getwd() if err != nil { t.Fatal(err) } // Walk up to find .beads dir := cwd for { if _, err := os.Stat(filepath.Join(dir, ".beads")); err == nil { break } parent := filepath.Dir(dir) if parent == dir { t.Skip("no .beads directory found in path") } dir = parent } b := New(dir) // Test List t.Run("List", func(t *testing.T) { issues, err := b.List(ListOptions{Status: "open"}) if err != nil { t.Fatalf("List failed: %v", err) } t.Logf("Found %d open issues", len(issues)) }) // Test Ready t.Run("Ready", func(t *testing.T) { issues, err := b.Ready() if err != nil { t.Fatalf("Ready failed: %v", err) } t.Logf("Found %d ready issues", len(issues)) }) // Test Blocked t.Run("Blocked", func(t *testing.T) { issues, err := b.Blocked() if err != nil { t.Fatalf("Blocked failed: %v", err) } t.Logf("Found %d blocked issues", len(issues)) }) // Test Show (if we have issues) t.Run("Show", func(t *testing.T) { issues, err := b.List(ListOptions{}) if err != nil { t.Fatalf("List failed: %v", err) } if len(issues) == 0 { t.Skip("no issues to show") } issue, err := b.Show(issues[0].ID) if err != nil { t.Fatalf("Show(%s) failed: %v", issues[0].ID, err) } t.Logf("Showed issue: %s - %s", issue.ID, issue.Title) }) }