Refactor high-complexity test functions (gocyclo)
Extracted helper structs for 5 complex test functions, reducing cyclomatic complexity from 31-35 to <10: - TestLibraryIntegration: integrationTestHelper with create/assert methods - TestExportImport: exportImportHelper with JSONL encoding/validation - TestListCommand: listTestHelper with search and assertions - TestGetEpicsEligibleForClosure: epicTestHelper with epic-specific queries - TestCreateIssues: createIssuesTestHelper with batch creation helpers All tests pass. Closes bd-55. Amp-Thread-ID: https://ampcode.com/threads/T-39807355-8790-4646-a98d-d40472e1bd2c Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
@@ -12,25 +12,21 @@ import (
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
)
|
||||
|
||||
func TestListCommand(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "bd-test-list-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
// listTestHelper provides test setup and assertion methods
|
||||
type listTestHelper struct {
|
||||
t *testing.T
|
||||
ctx context.Context
|
||||
store *sqlite.SQLiteStorage
|
||||
issues []*types.Issue
|
||||
}
|
||||
|
||||
testDB := filepath.Join(tmpDir, "test.db")
|
||||
s, err := sqlite.New(testDB)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
func newListTestHelper(t *testing.T, store *sqlite.SQLiteStorage) *listTestHelper {
|
||||
return &listTestHelper{t: t, ctx: context.Background(), store: store}
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Create test issues
|
||||
func (h *listTestHelper) createTestIssues() {
|
||||
now := time.Now()
|
||||
issues := []*types.Issue{
|
||||
h.issues = []*types.Issue{
|
||||
{
|
||||
Title: "Bug Issue",
|
||||
Description: "Test bug",
|
||||
@@ -55,138 +51,116 @@ func TestListCommand(t *testing.T) {
|
||||
ClosedAt: &now,
|
||||
},
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
if err := s.CreateIssue(ctx, issue, "test-user"); err != nil {
|
||||
t.Fatalf("Failed to create issue: %v", err)
|
||||
for _, issue := range h.issues {
|
||||
if err := h.store.CreateIssue(h.ctx, issue, "test-user"); err != nil {
|
||||
h.t.Fatalf("Failed to create issue: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add labels to first issue
|
||||
if err := s.AddLabel(ctx, issues[0].ID, "critical", "test-user"); err != nil {
|
||||
t.Fatalf("Failed to add label: %v", err)
|
||||
func (h *listTestHelper) addLabel(id, label string) {
|
||||
if err := h.store.AddLabel(h.ctx, id, label, "test-user"); err != nil {
|
||||
h.t.Fatalf("Failed to add label: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *listTestHelper) search(filter types.IssueFilter) []*types.Issue {
|
||||
results, err := h.store.SearchIssues(h.ctx, "", filter)
|
||||
if err != nil {
|
||||
h.t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
func (h *listTestHelper) assertCount(count, expected int, desc string) {
|
||||
if count != expected {
|
||||
h.t.Errorf("Expected %d %s, got %d", expected, desc, count)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *listTestHelper) assertEqual(expected, actual interface{}, field string) {
|
||||
if expected != actual {
|
||||
h.t.Errorf("Expected %s %v, got %v", field, expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *listTestHelper) assertAtMost(count, max int, desc string) {
|
||||
if count > max {
|
||||
h.t.Errorf("Expected at most %d %s, got %d", max, desc, count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListCommand(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "bd-test-list-*")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
testDB := filepath.Join(tmpDir, "test.db")
|
||||
s, err := sqlite.New(testDB)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
h := newListTestHelper(t, s)
|
||||
h.createTestIssues()
|
||||
h.addLabel(h.issues[0].ID, "critical")
|
||||
|
||||
t.Run("list all issues", func(t *testing.T) {
|
||||
filter := types.IssueFilter{}
|
||||
results, err := s.SearchIssues(ctx, "", filter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 3 {
|
||||
t.Errorf("Expected 3 issues, got %d", len(results))
|
||||
}
|
||||
results := h.search(types.IssueFilter{})
|
||||
h.assertCount(len(results), 3, "issues")
|
||||
})
|
||||
|
||||
t.Run("filter by status", func(t *testing.T) {
|
||||
status := types.StatusOpen
|
||||
filter := types.IssueFilter{Status: &status}
|
||||
results, err := s.SearchIssues(ctx, "", filter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Errorf("Expected 1 open issue, got %d", len(results))
|
||||
}
|
||||
if results[0].Status != types.StatusOpen {
|
||||
t.Errorf("Expected status %s, got %s", types.StatusOpen, results[0].Status)
|
||||
}
|
||||
results := h.search(types.IssueFilter{Status: &status})
|
||||
h.assertCount(len(results), 1, "open issues")
|
||||
h.assertEqual(types.StatusOpen, results[0].Status, "status")
|
||||
})
|
||||
|
||||
t.Run("filter by priority", func(t *testing.T) {
|
||||
priority := 0
|
||||
filter := types.IssueFilter{Priority: &priority}
|
||||
results, err := s.SearchIssues(ctx, "", filter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Errorf("Expected 1 P0 issue, got %d", len(results))
|
||||
}
|
||||
if results[0].Priority != 0 {
|
||||
t.Errorf("Expected priority 0, got %d", results[0].Priority)
|
||||
}
|
||||
results := h.search(types.IssueFilter{Priority: &priority})
|
||||
h.assertCount(len(results), 1, "P0 issues")
|
||||
h.assertEqual(0, results[0].Priority, "priority")
|
||||
})
|
||||
|
||||
t.Run("filter by assignee", func(t *testing.T) {
|
||||
assignee := "alice"
|
||||
filter := types.IssueFilter{Assignee: &assignee}
|
||||
results, err := s.SearchIssues(ctx, "", filter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Errorf("Expected 1 issue for alice, got %d", len(results))
|
||||
}
|
||||
if results[0].Assignee != "alice" {
|
||||
t.Errorf("Expected assignee alice, got %s", results[0].Assignee)
|
||||
}
|
||||
results := h.search(types.IssueFilter{Assignee: &assignee})
|
||||
h.assertCount(len(results), 1, "issues for alice")
|
||||
h.assertEqual("alice", results[0].Assignee, "assignee")
|
||||
})
|
||||
|
||||
t.Run("filter by issue type", func(t *testing.T) {
|
||||
issueType := types.TypeBug
|
||||
filter := types.IssueFilter{IssueType: &issueType}
|
||||
results, err := s.SearchIssues(ctx, "", filter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Errorf("Expected 1 bug issue, got %d", len(results))
|
||||
}
|
||||
if results[0].IssueType != types.TypeBug {
|
||||
t.Errorf("Expected type %s, got %s", types.TypeBug, results[0].IssueType)
|
||||
}
|
||||
results := h.search(types.IssueFilter{IssueType: &issueType})
|
||||
h.assertCount(len(results), 1, "bug issues")
|
||||
h.assertEqual(types.TypeBug, results[0].IssueType, "type")
|
||||
})
|
||||
|
||||
t.Run("filter by label", func(t *testing.T) {
|
||||
filter := types.IssueFilter{Labels: []string{"critical"}}
|
||||
results, err := s.SearchIssues(ctx, "", filter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Errorf("Expected 1 issue with critical label, got %d", len(results))
|
||||
}
|
||||
results := h.search(types.IssueFilter{Labels: []string{"critical"}})
|
||||
h.assertCount(len(results), 1, "issues with critical label")
|
||||
})
|
||||
|
||||
t.Run("filter by title search", func(t *testing.T) {
|
||||
filter := types.IssueFilter{TitleSearch: "Bug"}
|
||||
results, err := s.SearchIssues(ctx, "", filter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
|
||||
if len(results) != 1 {
|
||||
t.Errorf("Expected 1 issue matching 'Bug', got %d", len(results))
|
||||
}
|
||||
results := h.search(types.IssueFilter{TitleSearch: "Bug"})
|
||||
h.assertCount(len(results), 1, "issues matching 'Bug'")
|
||||
})
|
||||
|
||||
t.Run("limit results", func(t *testing.T) {
|
||||
filter := types.IssueFilter{Limit: 2}
|
||||
results, err := s.SearchIssues(ctx, "", filter)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to search issues: %v", err)
|
||||
}
|
||||
|
||||
if len(results) > 2 {
|
||||
t.Errorf("Expected at most 2 issues, got %d", len(results))
|
||||
}
|
||||
results := h.search(types.IssueFilter{Limit: 2})
|
||||
h.assertAtMost(len(results), 2, "issues")
|
||||
})
|
||||
|
||||
t.Run("normalize labels", func(t *testing.T) {
|
||||
labels := []string{" bug ", "critical", "", "bug", " feature "}
|
||||
normalized := normalizeLabels(labels)
|
||||
|
||||
expected := []string{"bug", "critical", "feature"}
|
||||
if len(normalized) != len(expected) {
|
||||
t.Errorf("Expected %d normalized labels, got %d", len(expected), len(normalized))
|
||||
}
|
||||
h.assertCount(len(normalized), len(expected), "normalized labels")
|
||||
|
||||
// Check deduplication and trimming
|
||||
seen := make(map[string]bool)
|
||||
|
||||
Reference in New Issue
Block a user