Context propagation with graceful cancellation (bd-rtp, bd-yb8, bd-2o2)
Complete implementation of signal-aware context propagation for graceful cancellation across all commands and storage operations. Key changes: 1. Signal-aware contexts (bd-rtp): - Added rootCtx/rootCancel in main.go using signal.NotifyContext() - Set up in PersistentPreRun, cancelled in PersistentPostRun - Daemon uses same pattern in runDaemonLoop() - Handles SIGINT/SIGTERM for graceful shutdown 2. Context propagation (bd-yb8): - All commands now use rootCtx instead of context.Background() - sqlite.New() receives context for cancellable operations - Database operations respect context cancellation - Storage layer propagates context through all queries 3. Cancellation tests (bd-2o2): - Added import_cancellation_test.go with comprehensive tests - Added export cancellation test in export_test.go - Tests verify database integrity after cancellation - All cancellation tests passing Fixes applied during review: - Fixed rootCtx lifecycle (removed premature defer from PersistentPreRun) - Fixed test context contamination (reset rootCtx in test cleanup) - Fixed export tests missing context setup Impact: - Pressing Ctrl+C during import/export now cancels gracefully - No database corruption or hanging transactions - Clean shutdown of all operations Tested: - go build ./cmd/bd ✓ - go test ./cmd/bd -run TestImportCancellation ✓ - go test ./cmd/bd -run TestExportCommand ✓ - Manual Ctrl+C testing verified 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -34,7 +34,7 @@ func TestSyncBranchCommitAndPush_NotConfigured(t *testing.T) {
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
store, err := sqlite.New(dbPath)
|
||||
store, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
@@ -105,7 +105,7 @@ func TestSyncBranchCommitAndPush_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
store, err := sqlite.New(dbPath)
|
||||
store, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
@@ -216,7 +216,7 @@ func TestSyncBranchCommitAndPush_NoChanges(t *testing.T) {
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
store, err := sqlite.New(dbPath)
|
||||
store, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
@@ -301,7 +301,7 @@ func TestSyncBranchCommitAndPush_WorktreeHealthCheck(t *testing.T) {
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
store, err := sqlite.New(dbPath)
|
||||
store, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
@@ -405,7 +405,7 @@ func TestSyncBranchPull_NotConfigured(t *testing.T) {
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
store, err := sqlite.New(dbPath)
|
||||
store, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
@@ -464,7 +464,7 @@ func TestSyncBranchPull_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
clone1DBPath := filepath.Join(clone1BeadsDir, "test.db")
|
||||
store1, err := sqlite.New(clone1DBPath)
|
||||
store1, err := sqlite.New(context.Background(), clone1DBPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store1: %v", err)
|
||||
}
|
||||
@@ -531,7 +531,7 @@ func TestSyncBranchPull_Success(t *testing.T) {
|
||||
|
||||
clone2BeadsDir := filepath.Join(clone2Dir, ".beads")
|
||||
clone2DBPath := filepath.Join(clone2BeadsDir, "test.db")
|
||||
store2, err := sqlite.New(clone2DBPath)
|
||||
store2, err := sqlite.New(context.Background(), clone2DBPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store2: %v", err)
|
||||
}
|
||||
@@ -613,7 +613,7 @@ func TestSyncBranchIntegration_EndToEnd(t *testing.T) {
|
||||
clone1BeadsDir := filepath.Join(clone1Dir, ".beads")
|
||||
os.MkdirAll(clone1BeadsDir, 0755)
|
||||
clone1DBPath := filepath.Join(clone1BeadsDir, "test.db")
|
||||
store1, _ := sqlite.New(clone1DBPath)
|
||||
store1, _ := sqlite.New(context.Background(), clone1DBPath)
|
||||
defer store1.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -664,7 +664,7 @@ func TestSyncBranchIntegration_EndToEnd(t *testing.T) {
|
||||
|
||||
clone2BeadsDir := filepath.Join(clone2Dir, ".beads")
|
||||
clone2DBPath := filepath.Join(clone2BeadsDir, "test.db")
|
||||
store2, _ := sqlite.New(clone2DBPath)
|
||||
store2, _ := sqlite.New(context.Background(), clone2DBPath)
|
||||
defer store2.Close()
|
||||
|
||||
store2.SetConfig(ctx, "issue_prefix", "test")
|
||||
@@ -763,7 +763,7 @@ func TestSyncBranchConfigChange(t *testing.T) {
|
||||
}
|
||||
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
store, err := sqlite.New(dbPath)
|
||||
store, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
@@ -903,7 +903,7 @@ func TestSyncBranchMultipleConcurrentClones(t *testing.T) {
|
||||
beadsDir := filepath.Join(cloneDir, ".beads")
|
||||
os.MkdirAll(beadsDir, 0755)
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
store, _ := sqlite.New(dbPath)
|
||||
store, _ := sqlite.New(context.Background(), dbPath)
|
||||
|
||||
ctx := context.Background()
|
||||
store.SetConfig(ctx, "issue_prefix", "test")
|
||||
@@ -1046,7 +1046,7 @@ func TestSyncBranchPerformance(t *testing.T) {
|
||||
os.MkdirAll(beadsDir, 0755)
|
||||
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
store, err := sqlite.New(dbPath)
|
||||
store, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
@@ -1137,7 +1137,7 @@ func TestSyncBranchNetworkFailure(t *testing.T) {
|
||||
os.MkdirAll(beadsDir, 0755)
|
||||
|
||||
dbPath := filepath.Join(beadsDir, "test.db")
|
||||
store, err := sqlite.New(dbPath)
|
||||
store, err := sqlite.New(context.Background(), dbPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create store: %v", err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user