The canonical beads database name is issues.jsonl. Tens of thousands of users have issues.jsonl, and beads.jsonl was only used by the Beads project itself due to git history pollution. Changes: - Updated bd doctor to warn about beads.jsonl instead of issues.jsonl - Changed default config from beads.jsonl to issues.jsonl - Reversed precedence in checkGitForIssues to prefer issues.jsonl - Updated git merge driver config to use issues.jsonl - Updated all tests to expect issues.jsonl as the default issues.jsonl is now the canonical default; beads.jsonl is legacy 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
221 lines
5.5 KiB
Go
221 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
|
"github.com/steveyegge/beads/internal/types"
|
|
)
|
|
|
|
// setupTestStore creates a test storage with issue_prefix configured
|
|
func setupTestStore(t *testing.T, dbPath string) *sqlite.SQLiteStorage {
|
|
t.Helper()
|
|
|
|
store, err := sqlite.New(context.Background(), dbPath)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create store: %v", err)
|
|
}
|
|
|
|
ctx := context.Background()
|
|
if err := store.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
|
|
store.Close()
|
|
t.Fatalf("Failed to set issue_prefix: %v", err)
|
|
}
|
|
|
|
return store
|
|
}
|
|
|
|
// TestDBNeedsExport_InSync verifies dbNeedsExport returns false when DB and JSONL are in sync
|
|
func TestDBNeedsExport_InSync(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dbPath := filepath.Join(tmpDir, "beads.db")
|
|
jsonlPath := filepath.Join(tmpDir, "issues.jsonl")
|
|
|
|
store := setupTestStore(t, dbPath)
|
|
defer store.Close()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create an issue in DB
|
|
issue := &types.Issue{
|
|
Title: "Test Issue",
|
|
Status: types.StatusOpen,
|
|
Priority: 1,
|
|
IssueType: types.TypeBug,
|
|
}
|
|
err := store.CreateIssue(ctx, issue, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create issue: %v", err)
|
|
}
|
|
|
|
// Export to JSONL
|
|
if err := exportToJSONLWithStore(ctx, store, jsonlPath); err != nil {
|
|
t.Fatalf("Failed to export: %v", err)
|
|
}
|
|
|
|
// Wait a moment to ensure DB mtime isn't newer
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
// Touch JSONL to make it newer than DB
|
|
now := time.Now()
|
|
if err := os.Chtimes(jsonlPath, now, now); err != nil {
|
|
t.Fatalf("Failed to touch JSONL: %v", err)
|
|
}
|
|
|
|
// DB and JSONL should be in sync
|
|
needsExport, err := dbNeedsExport(ctx, store, jsonlPath)
|
|
if err != nil {
|
|
t.Fatalf("dbNeedsExport failed: %v", err)
|
|
}
|
|
|
|
if needsExport {
|
|
t.Errorf("Expected needsExport=false (DB and JSONL in sync), got true")
|
|
}
|
|
}
|
|
|
|
// TestDBNeedsExport_DBNewer verifies dbNeedsExport returns true when DB is modified
|
|
func TestDBNeedsExport_DBNewer(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dbPath := filepath.Join(tmpDir, "beads.db")
|
|
jsonlPath := filepath.Join(tmpDir, "issues.jsonl")
|
|
|
|
store := setupTestStore(t, dbPath)
|
|
defer store.Close()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create and export issue
|
|
issue1 := &types.Issue{
|
|
Title: "Test Issue",
|
|
Status: types.StatusOpen,
|
|
Priority: 1,
|
|
IssueType: types.TypeBug,
|
|
}
|
|
err := store.CreateIssue(ctx, issue1, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create issue: %v", err)
|
|
}
|
|
|
|
if err := exportToJSONLWithStore(ctx, store, jsonlPath); err != nil {
|
|
t.Fatalf("Failed to export: %v", err)
|
|
}
|
|
|
|
// Wait and modify DB
|
|
time.Sleep(10 * time.Millisecond)
|
|
issue2 := &types.Issue{
|
|
Title: "Another Issue",
|
|
Status: types.StatusOpen,
|
|
Priority: 2,
|
|
IssueType: types.TypeTask,
|
|
}
|
|
err = store.CreateIssue(ctx, issue2, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create second issue: %v", err)
|
|
}
|
|
|
|
// DB is newer, should need export
|
|
needsExport, err := dbNeedsExport(ctx, store, jsonlPath)
|
|
if err != nil {
|
|
t.Fatalf("dbNeedsExport failed: %v", err)
|
|
}
|
|
|
|
if !needsExport {
|
|
t.Errorf("Expected needsExport=true (DB modified), got false")
|
|
}
|
|
}
|
|
|
|
// TestDBNeedsExport_CountMismatch verifies dbNeedsExport returns true when counts differ
|
|
func TestDBNeedsExport_CountMismatch(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dbPath := filepath.Join(tmpDir, "beads.db")
|
|
jsonlPath := filepath.Join(tmpDir, "issues.jsonl")
|
|
|
|
store := setupTestStore(t, dbPath)
|
|
defer store.Close()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create and export issue
|
|
issue1 := &types.Issue{
|
|
Title: "Test Issue",
|
|
Status: types.StatusOpen,
|
|
Priority: 1,
|
|
IssueType: types.TypeBug,
|
|
}
|
|
err := store.CreateIssue(ctx, issue1, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create issue: %v", err)
|
|
}
|
|
|
|
if err := exportToJSONLWithStore(ctx, store, jsonlPath); err != nil {
|
|
t.Fatalf("Failed to export: %v", err)
|
|
}
|
|
|
|
// Add another issue to DB but don't export
|
|
issue2 := &types.Issue{
|
|
Title: "Another Issue",
|
|
Status: types.StatusOpen,
|
|
Priority: 2,
|
|
IssueType: types.TypeTask,
|
|
}
|
|
err = store.CreateIssue(ctx, issue2, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create second issue: %v", err)
|
|
}
|
|
|
|
// Make JSONL appear newer (but counts differ)
|
|
time.Sleep(10 * time.Millisecond)
|
|
now := time.Now().Add(1 * time.Hour) // Way in the future
|
|
if err := os.Chtimes(jsonlPath, now, now); err != nil {
|
|
t.Fatalf("Failed to touch JSONL: %v", err)
|
|
}
|
|
|
|
// Counts mismatch, should need export
|
|
needsExport, err := dbNeedsExport(ctx, store, jsonlPath)
|
|
if err != nil {
|
|
t.Fatalf("dbNeedsExport failed: %v", err)
|
|
}
|
|
|
|
if !needsExport {
|
|
t.Errorf("Expected needsExport=true (count mismatch), got false")
|
|
}
|
|
}
|
|
|
|
// TestDBNeedsExport_NoJSONL verifies dbNeedsExport returns true when JSONL doesn't exist
|
|
func TestDBNeedsExport_NoJSONL(t *testing.T) {
|
|
tmpDir := t.TempDir()
|
|
dbPath := filepath.Join(tmpDir, "beads.db")
|
|
jsonlPath := filepath.Join(tmpDir, "issues.jsonl")
|
|
|
|
store := setupTestStore(t, dbPath)
|
|
defer store.Close()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create issue but don't export
|
|
issue := &types.Issue{
|
|
Title: "Test Issue",
|
|
Status: types.StatusOpen,
|
|
Priority: 1,
|
|
IssueType: types.TypeBug,
|
|
}
|
|
err := store.CreateIssue(ctx, issue, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("Failed to create issue: %v", err)
|
|
}
|
|
|
|
// JSONL doesn't exist, should need export
|
|
needsExport, err := dbNeedsExport(ctx, store, jsonlPath)
|
|
if err != nil {
|
|
t.Fatalf("dbNeedsExport failed: %v", err)
|
|
}
|
|
|
|
if !needsExport {
|
|
t.Fatalf("Expected needsExport=true (JSONL missing), got false")
|
|
}
|
|
}
|