test: Refactor compact_test.go to use shared DB pattern (bd-1rh)
Part of Phase 2/3 test suite optimization. BEFORE: - 10 separate test functions - Each creating its own database - 6 tests with DB setup overhead - Total: 10 test executions AFTER: - 1 TestCompactSuite with 6 shared-DB subtests - 4 standalone tests (no DB needed) - Single DB setup for suite - Total: 5 test functions IMPACT: - Lines: -135 (-22.4%) - DB setups: 6 → 1 (6x reduction) - Tests passing: 10/10 ✓ - Runtime: ~0.33s The suite consolidates all DB-dependent tests: - DryRun: eligibility check on closed issue - Stats: mix of eligible/ineligible issues - RunCompactStats: tests both normal and JSON output - CompactStatsJSON: JSON formatting path - RunCompactSingleDryRun: single issue eligibility - RunCompactAllDryRun: multiple issue eligibility Standalone tests (no DB): - TestCompactValidation: flag validation logic - TestCompactProgressBar: progress bar formatting - TestFormatUptime: uptime display formatting - TestCompactInitCommand: command initialization 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -154,7 +154,7 @@ Tests that already use good patterns:
|
||||
**Recommendation**: Mix - some can share, some need isolation
|
||||
|
||||
#### Misc Tests:
|
||||
- **compact_test.go** (10 tests)
|
||||
- ✅ **compact_test.go** (10 tests → 1 suite + 4 standalone = Phase 2 DONE)
|
||||
- **duplicates_test.go** (5 tests)
|
||||
- **epic_test.go** (3 tests)
|
||||
- **hooks_test.go** (6 tests)
|
||||
|
||||
@@ -3,74 +3,239 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||
"github.com/steveyegge/beads/internal/types"
|
||||
)
|
||||
|
||||
func TestCompactDryRun(t *testing.T) {
|
||||
func TestCompactSuite(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dbPath), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sqliteStore, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sqliteStore.Close()
|
||||
|
||||
testDB := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
s := newTestStore(t, testDB)
|
||||
ctx := context.Background()
|
||||
|
||||
// Set issue_prefix to prevent "database not initialized" errors
|
||||
if err := sqliteStore.SetConfig(ctx, "issue_prefix", "test"); err != nil {
|
||||
t.Fatalf("Failed to set issue_prefix: %v", err)
|
||||
}
|
||||
|
||||
// Create a closed issue
|
||||
issue := &types.Issue{
|
||||
ID: "test-1",
|
||||
Title: "Test Issue",
|
||||
Description: "This is a long description that should be compacted. " + string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Run("DryRun", func(t *testing.T) {
|
||||
// Create a closed issue
|
||||
issue := &types.Issue{
|
||||
ID: "test-dryrun-1",
|
||||
Title: "Test Issue",
|
||||
Description: "This is a long description that should be compacted. " + string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
|
||||
// Test dry run - should not error even without API key
|
||||
compactDryRun = true
|
||||
compactTier = 1
|
||||
compactID = "test-1"
|
||||
compactForce = false
|
||||
jsonOutput = false
|
||||
|
||||
store = sqliteStore
|
||||
daemonClient = nil
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Should check eligibility without error
|
||||
eligible, reason, err := sqliteStore.CheckEligibility(ctx, "test-1", 1)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckEligibility failed: %v", err)
|
||||
}
|
||||
|
||||
if !eligible {
|
||||
t.Fatalf("Issue should be eligible for compaction: %s", reason)
|
||||
}
|
||||
// Test dry run - should check eligibility without error even without API key
|
||||
eligible, reason, err := s.CheckEligibility(ctx, "test-dryrun-1", 1)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckEligibility failed: %v", err)
|
||||
}
|
||||
|
||||
compactDryRun = false
|
||||
compactID = ""
|
||||
if !eligible {
|
||||
t.Fatalf("Issue should be eligible for compaction: %s", reason)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Stats", func(t *testing.T) {
|
||||
// Create mix of issues - some eligible, some not
|
||||
issues := []*types.Issue{
|
||||
{
|
||||
ID: "test-stats-1",
|
||||
Title: "Old closed",
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
},
|
||||
{
|
||||
ID: "test-stats-2",
|
||||
Title: "Recent closed",
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-10 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-5 * 24 * time.Hour)),
|
||||
},
|
||||
{
|
||||
ID: "test-stats-3",
|
||||
Title: "Still open",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-40 * 24 * time.Hour),
|
||||
},
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify issues were created
|
||||
allIssues, err := s.SearchIssues(ctx, "", types.IssueFilter{})
|
||||
if err != nil {
|
||||
t.Fatalf("SearchIssues failed: %v", err)
|
||||
}
|
||||
|
||||
// Count issues with stats prefix
|
||||
statCount := 0
|
||||
for _, issue := range allIssues {
|
||||
if len(issue.ID) >= 11 && issue.ID[:11] == "test-stats-" {
|
||||
statCount++
|
||||
}
|
||||
}
|
||||
|
||||
if statCount != 3 {
|
||||
t.Errorf("Expected 3 stats issues, got %d", statCount)
|
||||
}
|
||||
|
||||
// Test eligibility check for old closed issue
|
||||
eligible, _, err := s.CheckEligibility(ctx, "test-stats-1", 1)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckEligibility failed: %v", err)
|
||||
}
|
||||
if !eligible {
|
||||
t.Error("Old closed issue should be eligible for Tier 1")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RunCompactStats", func(t *testing.T) {
|
||||
// Create some closed issues
|
||||
for i := 1; i <= 3; i++ {
|
||||
id := fmt.Sprintf("test-runstats-%d", i)
|
||||
issue := &types.Issue{
|
||||
ID: id,
|
||||
Title: "Test Issue",
|
||||
Description: string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test stats - should work without API key
|
||||
savedJSONOutput := jsonOutput
|
||||
jsonOutput = false
|
||||
defer func() { jsonOutput = savedJSONOutput }()
|
||||
|
||||
// Actually call runCompactStats to increase coverage
|
||||
runCompactStats(ctx, s)
|
||||
|
||||
// Also test with JSON output
|
||||
jsonOutput = true
|
||||
runCompactStats(ctx, s)
|
||||
})
|
||||
|
||||
t.Run("CompactStatsJSON", func(t *testing.T) {
|
||||
// Create a closed issue eligible for Tier 1
|
||||
issue := &types.Issue{
|
||||
ID: "test-json-1",
|
||||
Title: "Test Issue",
|
||||
Description: string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test with JSON output
|
||||
savedJSONOutput := jsonOutput
|
||||
jsonOutput = true
|
||||
defer func() { jsonOutput = savedJSONOutput }()
|
||||
|
||||
// Should not panic and should execute JSON path
|
||||
runCompactStats(ctx, s)
|
||||
})
|
||||
|
||||
t.Run("RunCompactSingleDryRun", func(t *testing.T) {
|
||||
// Create a closed issue eligible for compaction
|
||||
issue := &types.Issue{
|
||||
ID: "test-single-1",
|
||||
Title: "Test Compact Issue",
|
||||
Description: string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test eligibility in dry run mode
|
||||
eligible, _, err := s.CheckEligibility(ctx, "test-single-1", 1)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckEligibility failed: %v", err)
|
||||
}
|
||||
if !eligible {
|
||||
t.Error("Issue should be eligible for Tier 1 compaction")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("RunCompactAllDryRun", func(t *testing.T) {
|
||||
// Create multiple closed issues
|
||||
for i := 1; i <= 3; i++ {
|
||||
issue := &types.Issue{
|
||||
ID: fmt.Sprintf("test-all-%d", i),
|
||||
Title: "Test Issue",
|
||||
Description: string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
if err := s.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify issues eligible for compaction
|
||||
closedStatus := types.StatusClosed
|
||||
issues, err := s.SearchIssues(ctx, "", types.IssueFilter{Status: &closedStatus})
|
||||
if err != nil {
|
||||
t.Fatalf("SearchIssues failed: %v", err)
|
||||
}
|
||||
|
||||
eligibleCount := 0
|
||||
for _, issue := range issues {
|
||||
// Only count our test-all issues
|
||||
if len(issue.ID) < 9 || issue.ID[:9] != "test-all-" {
|
||||
continue
|
||||
}
|
||||
eligible, _, err := s.CheckEligibility(ctx, issue.ID, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckEligibility failed for %s: %v", issue.ID, err)
|
||||
}
|
||||
if eligible {
|
||||
eligibleCount++
|
||||
}
|
||||
}
|
||||
|
||||
if eligibleCount != 3 {
|
||||
t.Errorf("Expected 3 eligible issues, got %d", eligibleCount)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCompactValidation(t *testing.T) {
|
||||
@@ -83,10 +248,10 @@ func TestCompactValidation(t *testing.T) {
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "both id and all",
|
||||
compactID: "test-1",
|
||||
name: "both id and all",
|
||||
compactID: "test-1",
|
||||
compactAll: true,
|
||||
wantError: true,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "force without id",
|
||||
@@ -103,9 +268,9 @@ func TestCompactValidation(t *testing.T) {
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "id only",
|
||||
compactID: "test-1",
|
||||
wantError: false,
|
||||
name: "id only",
|
||||
compactID: "test-1",
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "all only",
|
||||
@@ -122,14 +287,14 @@ func TestCompactValidation(t *testing.T) {
|
||||
t.Error("Expected error for both --id and --all")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if tt.force && tt.compactID == "" {
|
||||
// Should fail
|
||||
if !tt.wantError {
|
||||
t.Error("Expected error for --force without --id")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if tt.compactID == "" && !tt.compactAll && !tt.dryRun {
|
||||
// Should fail
|
||||
if !tt.wantError {
|
||||
@@ -140,147 +305,18 @@ func TestCompactValidation(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompactStats(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dbPath), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sqliteStore, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sqliteStore.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Set issue_prefix to prevent "database not initialized" errors
|
||||
if err := sqliteStore.SetConfig(ctx, "issue_prefix", "test"); err != nil {
|
||||
t.Fatalf("Failed to set issue_prefix: %v", err)
|
||||
}
|
||||
|
||||
// Create mix of issues - some eligible, some not
|
||||
issues := []*types.Issue{
|
||||
{
|
||||
ID: "test-1",
|
||||
Title: "Old closed",
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
},
|
||||
{
|
||||
ID: "test-2",
|
||||
Title: "Recent closed",
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-10 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-5 * 24 * time.Hour)),
|
||||
},
|
||||
{
|
||||
ID: "test-3",
|
||||
Title: "Still open",
|
||||
Status: types.StatusOpen,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-40 * 24 * time.Hour),
|
||||
},
|
||||
}
|
||||
|
||||
for _, issue := range issues {
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify issues were created
|
||||
allIssues, err := sqliteStore.SearchIssues(ctx, "", types.IssueFilter{})
|
||||
if err != nil {
|
||||
t.Fatalf("SearchIssues failed: %v", err)
|
||||
}
|
||||
|
||||
if len(allIssues) != 3 {
|
||||
t.Errorf("Expected 3 total issues, got %d", len(allIssues))
|
||||
}
|
||||
|
||||
// Test eligibility check for old closed issue
|
||||
eligible, _, err := sqliteStore.CheckEligibility(ctx, "test-1", 1)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckEligibility failed: %v", err)
|
||||
}
|
||||
if !eligible {
|
||||
t.Error("Old closed issue should be eligible for Tier 1")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCompactStats(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dbPath), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sqliteStore, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sqliteStore.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Set issue_prefix
|
||||
if err := sqliteStore.SetConfig(ctx, "issue_prefix", "test"); err != nil {
|
||||
t.Fatalf("Failed to set issue_prefix: %v", err)
|
||||
}
|
||||
|
||||
// Create some closed issues
|
||||
for i := 1; i <= 3; i++ {
|
||||
id := "test-" + string(rune('0'+i))
|
||||
issue := &types.Issue{
|
||||
ID: id,
|
||||
Title: "Test Issue",
|
||||
Description: string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test stats - should work without API key
|
||||
savedJSONOutput := jsonOutput
|
||||
jsonOutput = false
|
||||
defer func() { jsonOutput = savedJSONOutput }()
|
||||
|
||||
// Actually call runCompactStats to increase coverage
|
||||
runCompactStats(ctx, sqliteStore)
|
||||
|
||||
// Also test with JSON output
|
||||
jsonOutput = true
|
||||
runCompactStats(ctx, sqliteStore)
|
||||
}
|
||||
|
||||
func TestCompactProgressBar(t *testing.T) {
|
||||
// Test progress bar formatting
|
||||
pb := progressBar(50, 100)
|
||||
if len(pb) == 0 {
|
||||
t.Error("Progress bar should not be empty")
|
||||
}
|
||||
|
||||
|
||||
pb = progressBar(100, 100)
|
||||
if len(pb) == 0 {
|
||||
t.Error("Full progress bar should not be empty")
|
||||
}
|
||||
|
||||
|
||||
pb = progressBar(0, 100)
|
||||
if len(pb) == 0 {
|
||||
t.Error("Zero progress bar should not be empty")
|
||||
@@ -333,189 +369,18 @@ func TestCompactInitCommand(t *testing.T) {
|
||||
if compactCmd == nil {
|
||||
t.Fatal("compactCmd should be initialized")
|
||||
}
|
||||
|
||||
|
||||
if compactCmd.Use != "compact" {
|
||||
t.Errorf("Expected Use='compact', got %q", compactCmd.Use)
|
||||
}
|
||||
|
||||
|
||||
if len(compactCmd.Long) == 0 {
|
||||
t.Error("compactCmd should have Long description")
|
||||
}
|
||||
|
||||
|
||||
// Verify --json flag exists
|
||||
jsonFlag := compactCmd.Flags().Lookup("json")
|
||||
if jsonFlag == nil {
|
||||
t.Error("compact command should have --json flag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompactStatsJSON(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dbPath), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sqliteStore, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sqliteStore.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Set issue_prefix
|
||||
if err := sqliteStore.SetConfig(ctx, "issue_prefix", "test"); err != nil {
|
||||
t.Fatalf("Failed to set issue_prefix: %v", err)
|
||||
}
|
||||
|
||||
// Create a closed issue eligible for Tier 1
|
||||
issue := &types.Issue{
|
||||
ID: "test-1",
|
||||
Title: "Test Issue",
|
||||
Description: string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Test with JSON output
|
||||
savedJSONOutput := jsonOutput
|
||||
jsonOutput = true
|
||||
defer func() { jsonOutput = savedJSONOutput }()
|
||||
|
||||
// Should not panic and should execute JSON path
|
||||
runCompactStats(ctx, sqliteStore)
|
||||
}
|
||||
|
||||
func TestRunCompactSingleDryRun(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dbPath), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sqliteStore, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sqliteStore.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Set issue_prefix
|
||||
if err := sqliteStore.SetConfig(ctx, "issue_prefix", "test"); err != nil {
|
||||
t.Fatalf("Failed to set issue_prefix: %v", err)
|
||||
}
|
||||
|
||||
// Create a closed issue eligible for compaction
|
||||
issue := &types.Issue{
|
||||
ID: "test-compact-1",
|
||||
Title: "Test Compact Issue",
|
||||
Description: string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Save current state
|
||||
savedJSONOutput := jsonOutput
|
||||
savedCompactDryRun := compactDryRun
|
||||
savedCompactTier := compactTier
|
||||
savedCompactForce := compactForce
|
||||
defer func() {
|
||||
jsonOutput = savedJSONOutput
|
||||
compactDryRun = savedCompactDryRun
|
||||
compactTier = savedCompactTier
|
||||
compactForce = savedCompactForce
|
||||
}()
|
||||
|
||||
// Test dry run mode
|
||||
compactDryRun = true
|
||||
compactTier = 1
|
||||
compactForce = false
|
||||
jsonOutput = false
|
||||
|
||||
// This should succeed without API key in dry run mode
|
||||
// We can't fully test without mocking the compactor, but we can test the eligibility path
|
||||
eligible, _, err := sqliteStore.CheckEligibility(ctx, "test-compact-1", 1)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckEligibility failed: %v", err)
|
||||
}
|
||||
if !eligible {
|
||||
t.Error("Issue should be eligible for Tier 1 compaction")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCompactAllDryRun(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dbPath), 0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sqliteStore, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer sqliteStore.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// Set issue_prefix
|
||||
if err := sqliteStore.SetConfig(ctx, "issue_prefix", "test"); err != nil {
|
||||
t.Fatalf("Failed to set issue_prefix: %v", err)
|
||||
}
|
||||
|
||||
// Create multiple closed issues
|
||||
for i := 1; i <= 3; i++ {
|
||||
issue := &types.Issue{
|
||||
ID: fmt.Sprintf("test-all-%d", i),
|
||||
Title: "Test Issue",
|
||||
Description: string(make([]byte, 500)),
|
||||
Status: types.StatusClosed,
|
||||
Priority: 2,
|
||||
IssueType: types.TypeTask,
|
||||
CreatedAt: time.Now().Add(-60 * 24 * time.Hour),
|
||||
ClosedAt: ptrTime(time.Now().Add(-35 * 24 * time.Hour)),
|
||||
}
|
||||
if err := sqliteStore.CreateIssue(ctx, issue, "test"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Verify issues eligible for compaction
|
||||
closedStatus := types.StatusClosed
|
||||
issues, err := sqliteStore.SearchIssues(ctx, "", types.IssueFilter{Status: &closedStatus})
|
||||
if err != nil {
|
||||
t.Fatalf("SearchIssues failed: %v", err)
|
||||
}
|
||||
|
||||
eligibleCount := 0
|
||||
for _, issue := range issues {
|
||||
eligible, _, err := sqliteStore.CheckEligibility(ctx, issue.ID, 1)
|
||||
if err != nil {
|
||||
t.Fatalf("CheckEligibility failed for %s: %v", issue.ID, err)
|
||||
}
|
||||
if eligible {
|
||||
eligibleCount++
|
||||
}
|
||||
}
|
||||
|
||||
if eligibleCount != 3 {
|
||||
t.Errorf("Expected 3 eligible issues, got %d", eligibleCount)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user