Files
beads/cmd/bd/rename_prefix_repair_test.go
Ryan Snodgrass fa566a9700 fix(rename-prefix): use hash IDs instead of sequential in --repair mode
The --repair flag was generating sequential IDs (sageox-9895, sageox-9896)
instead of hash-based IDs (sageox-jwnv, sageox-urtm). This fix uses the
proper GenerateIssueID function from sqlite package to generate consistent
hash-based IDs during prefix repair operations.

Changes:
- Import sqlite package for hash ID generation
- Add generateRepairHashID helper that uses sqlite.GenerateIssueID
- Track used IDs within batch to avoid collisions
- Update test to verify hash IDs instead of sequential
2025-12-15 23:59:21 -08:00

121 lines
3.2 KiB
Go

package main
import (
"context"
"testing"
"time"
"github.com/steveyegge/beads/internal/storage/sqlite"
"github.com/steveyegge/beads/internal/types"
)
func TestRepairMultiplePrefixes(t *testing.T) {
// Create a temporary database
dbPath := t.TempDir() + "/test.db"
store, err := sqlite.New(context.Background(), dbPath)
if err != nil {
t.Fatalf("failed to create store: %v", err)
}
defer store.Close()
ctx := context.Background()
// Set initial prefix
if err := store.SetConfig(ctx, "issue_prefix", "test"); err != nil {
t.Fatalf("failed to set prefix: %v", err)
}
// Create issues with multiple prefixes (simulating corruption)
// We need to directly insert into the database to bypass prefix validation
db := store.UnderlyingDB()
now := time.Now()
issues := []struct {
ID string
Title string
}{
{"test-1", "Test issue 1"},
{"test-2", "Test issue 2"},
{"old-1", "Old issue 1"},
{"old-2", "Old issue 2"},
{"another-1", "Another issue 1"},
}
for _, issue := range issues {
_, err := db.ExecContext(ctx, `
INSERT INTO issues (id, title, status, priority, issue_type, created_at, updated_at)
VALUES (?, ?, 'open', 2, 'task', ?, ?)
`, issue.ID, issue.Title, now, now)
if err != nil {
t.Fatalf("failed to create issue %s: %v", issue.ID, err)
}
}
// Verify we have multiple prefixes
allIssues, err := store.SearchIssues(ctx, "", types.IssueFilter{})
if err != nil {
t.Fatalf("failed to search issues: %v", err)
}
prefixes := detectPrefixes(allIssues)
if len(prefixes) != 3 {
t.Fatalf("expected 3 prefixes, got %d: %v", len(prefixes), prefixes)
}
// Test repair
if err := repairPrefixes(ctx, store, "test", "test", allIssues, prefixes, false); err != nil {
t.Fatalf("repair failed: %v", err)
}
// Verify all issues now have correct prefix
allIssues, err = store.SearchIssues(ctx, "", types.IssueFilter{})
if err != nil {
t.Fatalf("failed to search issues after repair: %v", err)
}
prefixes = detectPrefixes(allIssues)
if len(prefixes) != 1 {
t.Fatalf("expected 1 prefix after repair, got %d: %v", len(prefixes), prefixes)
}
if _, ok := prefixes["test"]; !ok {
t.Fatalf("expected prefix 'test', got %v", prefixes)
}
// Verify the original test-1 and test-2 are unchanged
for _, id := range []string{"test-1", "test-2"} {
issue, err := store.GetIssue(ctx, id)
if err != nil {
t.Fatalf("expected issue %s to exist unchanged: %v", id, err)
}
if issue == nil {
t.Fatalf("expected issue %s to exist", id)
}
}
// Verify the others were renamed with hash IDs (not sequential)
// We have 5 total issues, 2 original (test-1, test-2), 3 renamed
if len(allIssues) != 5 {
t.Fatalf("expected 5 issues total, got %d", len(allIssues))
}
// Count issues with correct prefix and verify old IDs no longer exist
testPrefixCount := 0
for _, issue := range allIssues {
if len(issue.ID) > 5 && issue.ID[:5] == "test-" {
testPrefixCount++
}
}
if testPrefixCount != 5 {
t.Fatalf("expected all 5 issues to have 'test-' prefix, got %d", testPrefixCount)
}
// Verify old IDs no longer exist
for _, oldID := range []string{"old-1", "old-2", "another-1"} {
issue, err := store.GetIssue(ctx, oldID)
if err == nil && issue != nil {
t.Fatalf("expected old ID %s to no longer exist", oldID)
}
}
}