test: Refactor P1 test files to use shared DB pattern (bd-1rh)
Refactored 6 high-priority test files to reduce database initializations and improve test suite performance: - create_test.go: Combined 11 tests into TestCreateSuite (11 DBs → 1 DB) - dep_test.go: Combined into TestDependencySuite (4 DBs → 1 DB) - comments_test.go: Combined into TestCommentsSuite (2 DBs → 1 DB) - list_test.go: Split into 2 suites to avoid data pollution (2 DBs → 2 DBs) - ready_test.go: Combined into TestReadySuite (3 DBs → 1 DB) - stale_test.go: Kept as individual functions due to data isolation needs Added TEST_SUITE_AUDIT.md documenting the refactoring plan, results, and key learnings for future test development. Results: - P1 tests now run in 0.43 seconds - Estimated 10-20x speedup - All tests passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,188 +14,229 @@ import (
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
)
|
||||
|
||||
func TestDepAdd(t *testing.T) {
|
||||
func TestDependencySuite(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
sqliteStore := newTestStore(t, dbPath)
|
||||
|
||||
testDB := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
s := newTestStore(t, testDB)
|
||||
ctx := context.Background()
|
||||
|
||||
// Create test issues
|
||||
issues := []*types.Issue{
|
||||
{
|
||||
ID: "test-1",
|
||||
Title: "Task 1",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: "test-2",
|
||||
Title: "Task 2",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
t.Run("DepAdd", func(t *testing.T) {
|
||||
// Create test issues
|
||||
issues := []*types.Issue{
|
||||
{
|
||||
ID: "test-1",
|
||||
Title: "Task 1",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: "test-2",
|
||||
Title: "Task 2",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Add dependency
|
||||
dep := &types.Dependency{
|
||||
IssueID: "test-1",
|
||||
DependsOnID: "test-2",
|
||||
Type: types.DepBlocks,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := sqliteStore.AddDependency(ctx, dep, "test"); err != nil {
|
||||
t.Fatalf("AddDependency failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify dependency was added
|
||||
deps, err := sqliteStore.GetDependencies(ctx, "test-1")
|
||||
if err != nil {
|
||||
t.Fatalf("GetDependencies failed: %v", err)
|
||||
}
|
||||
|
||||
if len(deps) != 1 {
|
||||
t.Fatalf("Expected 1 dependency, got %d", len(deps))
|
||||
}
|
||||
|
||||
if deps[0].ID != "test-2" {
|
||||
t.Errorf("Expected dependency on test-2, got %s", deps[0].ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDepTypes(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
sqliteStore := newTestStore(t, dbPath)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Create test issues
|
||||
for i := 1; i <= 4; i++ {
|
||||
issue := &types.Issue{
|
||||
ID: fmt.Sprintf("test-%d", i),
|
||||
Title: fmt.Sprintf("Task %d", i),
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
for _, issue := range issues {
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test different dependency types (without creating cycles)
|
||||
depTypes := []struct {
|
||||
depType types.DependencyType
|
||||
from string
|
||||
to string
|
||||
}{
|
||||
{types.DepBlocks, "test-2", "test-1"},
|
||||
{types.DepRelated, "test-3", "test-1"},
|
||||
{types.DepParentChild, "test-4", "test-1"},
|
||||
{types.DepDiscoveredFrom, "test-3", "test-2"},
|
||||
}
|
||||
|
||||
for _, dt := range depTypes {
|
||||
// Add dependency
|
||||
dep := &types.Dependency{
|
||||
IssueID: dt.from,
|
||||
DependsOnID: dt.to,
|
||||
Type: dt.depType,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := sqliteStore.AddDependency(ctx, dep, "test"); err != nil {
|
||||
t.Fatalf("AddDependency failed for type %s: %v", dt.depType, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDepCycleDetection(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
sqliteStore := newTestStore(t, dbPath)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Create test issues
|
||||
for i := 1; i <= 3; i++ {
|
||||
issue := &types.Issue{
|
||||
ID: fmt.Sprintf("test-%d", i),
|
||||
Title: fmt.Sprintf("Task %d", i),
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a cycle: test-1 -> test-2 -> test-3 -> test-1
|
||||
// Add first two deps successfully
|
||||
deps := []struct {
|
||||
from string
|
||||
to string
|
||||
}{
|
||||
{"test-1", "test-2"},
|
||||
{"test-2", "test-3"},
|
||||
}
|
||||
|
||||
for _, d := range deps {
|
||||
dep := &types.Dependency{
|
||||
IssueID: d.from,
|
||||
DependsOnID: d.to,
|
||||
IssueID: "test-1",
|
||||
DependsOnID: "test-2",
|
||||
Type: types.DepBlocks,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := sqliteStore.AddDependency(ctx, dep, "test"); err != nil {
|
||||
|
||||
if err := s.AddDependency(ctx, dep, "test"); err != nil {
|
||||
t.Fatalf("AddDependency failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to add the third dep which would create a cycle - should fail
|
||||
cycleDep := &types.Dependency{
|
||||
IssueID: "test-3",
|
||||
DependsOnID: "test-1",
|
||||
Type: types.DepBlocks,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := sqliteStore.AddDependency(ctx, cycleDep, "test"); err == nil {
|
||||
t.Fatal("Expected AddDependency to fail when creating cycle, but it succeeded")
|
||||
}
|
||||
|
||||
// Since cycle detection prevented the cycle, DetectCycles should find no cycles
|
||||
cycles, err := sqliteStore.DetectCycles(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("DetectCycles failed: %v", err)
|
||||
}
|
||||
// Verify dependency was added
|
||||
deps, err := s.GetDependencies(ctx, "test-1")
|
||||
if err != nil {
|
||||
t.Fatalf("GetDependencies failed: %v", err)
|
||||
}
|
||||
|
||||
if len(cycles) != 0 {
|
||||
t.Error("Expected no cycles since cycle was prevented")
|
||||
}
|
||||
if len(deps) != 1 {
|
||||
t.Fatalf("Expected 1 dependency, got %d", len(deps))
|
||||
}
|
||||
|
||||
if deps[0].ID != "test-2" {
|
||||
t.Errorf("Expected dependency on test-2, got %s", deps[0].ID)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DepTypes", func(t *testing.T) {
|
||||
// Create test issues
|
||||
for i := 1; i <= 4; i++ {
|
||||
issue := &types.Issue{
|
||||
ID: fmt.Sprintf("test-types-%d", i),
|
||||
Title: fmt.Sprintf("Task %d", i),
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test different dependency types (without creating cycles)
|
||||
depTypes := []struct {
|
||||
depType types.DependencyType
|
||||
from string
|
||||
to string
|
||||
}{
|
||||
{types.DepBlocks, "test-types-2", "test-types-1"},
|
||||
{types.DepRelated, "test-types-3", "test-types-1"},
|
||||
{types.DepParentChild, "test-types-4", "test-types-1"},
|
||||
{types.DepDiscoveredFrom, "test-types-3", "test-types-2"},
|
||||
}
|
||||
|
||||
for _, dt := range depTypes {
|
||||
dep := &types.Dependency{
|
||||
IssueID: dt.from,
|
||||
DependsOnID: dt.to,
|
||||
Type: dt.depType,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := s.AddDependency(ctx, dep, "test"); err != nil {
|
||||
t.Fatalf("AddDependency failed for type %s: %v", dt.depType, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DepCycleDetection", func(t *testing.T) {
|
||||
// Create test issues
|
||||
for i := 1; i <= 3; i++ {
|
||||
issue := &types.Issue{
|
||||
ID: fmt.Sprintf("test-cycle-%d", i),
|
||||
Title: fmt.Sprintf("Task %d", i),
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create a cycle: test-cycle-1 -> test-cycle-2 -> test-cycle-3 -> test-cycle-1
|
||||
// Add first two deps successfully
|
||||
deps := []struct {
|
||||
from string
|
||||
to string
|
||||
}{
|
||||
{"test-cycle-1", "test-cycle-2"},
|
||||
{"test-cycle-2", "test-cycle-3"},
|
||||
}
|
||||
|
||||
for _, d := range deps {
|
||||
dep := &types.Dependency{
|
||||
IssueID: d.from,
|
||||
DependsOnID: d.to,
|
||||
Type: types.DepBlocks,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := s.AddDependency(ctx, dep, "test"); err != nil {
|
||||
t.Fatalf("AddDependency failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Try to add the third dep which would create a cycle - should fail
|
||||
cycleDep := &types.Dependency{
|
||||
IssueID: "test-cycle-3",
|
||||
DependsOnID: "test-cycle-1",
|
||||
Type: types.DepBlocks,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if err := s.AddDependency(ctx, cycleDep, "test"); err == nil {
|
||||
t.Fatal("Expected AddDependency to fail when creating cycle, but it succeeded")
|
||||
}
|
||||
|
||||
// Since cycle detection prevented the cycle, DetectCycles should find no cycles
|
||||
cycles, err := s.DetectCycles(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("DetectCycles failed: %v", err)
|
||||
}
|
||||
|
||||
if len(cycles) != 0 {
|
||||
t.Error("Expected no cycles since cycle was prevented")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("DepRemove", func(t *testing.T) {
|
||||
// Create test issues
|
||||
issues := []*types.Issue{
|
||||
{
|
||||
ID: "test-remove-1",
|
||||
Title: "Task 1",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: "test-remove-2",
|
||||
Title: "Task 2",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add dependency
|
||||
dep := &types.Dependency{
|
||||
IssueID: "test-remove-1",
|
||||
DependsOnID: "test-remove-2",
|
||||
Type: types.DepBlocks,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := s.AddDependency(ctx, dep, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Remove dependency
|
||||
if err := s.RemoveDependency(ctx, "test-remove-1", "test-remove-2", "test"); err != nil {
|
||||
t.Fatalf("RemoveDependency failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify dependency was removed
|
||||
deps, err := s.GetDependencies(ctx, "test-remove-1")
|
||||
if err != nil {
|
||||
t.Fatalf("GetDependencies failed: %v", err)
|
||||
}
|
||||
|
||||
if len(deps) != 0 {
|
||||
t.Errorf("Expected 0 dependencies after removal, got %d", len(deps))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestDepCommandsInit(t *testing.T) {
|
||||
if depCmd == nil {
|
||||
t.Fatal("depCmd should be initialized")
|
||||
}
|
||||
|
||||
|
||||
if depCmd.Use != "dep" {
|
||||
t.Errorf("Expected Use='dep', got %q", depCmd.Use)
|
||||
}
|
||||
@@ -209,68 +250,6 @@ func TestDepCommandsInit(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDepRemove(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
sqliteStore := newTestStore(t, dbPath)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Create test issues
|
||||
issues := []*types.Issue{
|
||||
{
|
||||
ID: "test-1",
|
||||
Title: "Task 1",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
{
|
||||
ID: "test-2",
|
||||
Title: "Task 2",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 1,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Add dependency
|
||||
dep := &types.Dependency{
|
||||
IssueID: "test-1",
|
||||
DependsOnID: "test-2",
|
||||
Type: types.DepBlocks,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := sqliteStore.AddDependency(ctx, dep, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Remove dependency
|
||||
if err := sqliteStore.RemoveDependency(ctx, "test-1", "test-2", "test"); err != nil {
|
||||
t.Fatalf("RemoveDependency failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify dependency was removed
|
||||
deps, err := sqliteStore.GetDependencies(ctx, "test-1")
|
||||
if err != nil {
|
||||
t.Fatalf("GetDependencies failed: %v", err)
|
||||
}
|
||||
|
||||
if len(deps) != 0 {
|
||||
t.Errorf("Expected 0 dependencies after removal, got %d", len(deps))
|
||||
}
|
||||
}
|
||||
|
||||
func TestDepTreeFormatFlag(t *testing.T) {
|
||||
// Test that the --format flag exists on depTreeCmd
|
||||
flag := depTreeCmd.Flags().Lookup("format")
|
||||
|
||||
Reference in New Issue
Block a user