From 0575ca9bd977368a6d4e9189e0b8ff34e2f90927 Mon Sep 17 00:00:00 2001 From: Eugene Sukhodolin Date: Fri, 26 Dec 2025 22:23:58 -0800 Subject: [PATCH] fix(nodb): set storeActive when initializing no-db mode The `bd comment --no-db` command was failing because initializeNoDbMode() set the global `store` but never set `storeActive = true`. When comments.go called ensureStoreActive(), the guard check failed and it tried to find a SQLite database, returning an ironic error telling the user to use --no-db. Why only `comment` was affected: - Commands like `create`, `update`, `close` use `store` directly - The `comment` command calls `ensureStoreActive()` first as a safety check - That function guards on `storeActive && store != nil` - Since `storeActive` was never set, the guard failed and it looked for SQLite The fix aligns no-db mode with what ensureStoreActive() expects. --- cmd/bd/nodb.go | 5 ++- cmd/bd/nodb_test.go | 84 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/cmd/bd/nodb.go b/cmd/bd/nodb.go index 5ba47368..012ac5d4 100644 --- a/cmd/bd/nodb.go +++ b/cmd/bd/nodb.go @@ -72,8 +72,11 @@ func initializeNoDbMode() error { debug.Logf("using prefix '%s'", prefix) - // Set global store + // Set global store and mark as active (fixes bd comment --no-db) + storeMutex.Lock() store = memStore + storeActive = true + storeMutex.Unlock() return nil } diff --git a/cmd/bd/nodb_test.go b/cmd/bd/nodb_test.go index 56be64e9..0063c58e 100644 --- a/cmd/bd/nodb_test.go +++ b/cmd/bd/nodb_test.go @@ -158,6 +158,90 @@ func TestDetectPrefix(t *testing.T) { }) } +func TestInitializeNoDbMode_SetsStoreActive(t *testing.T) { + // This test verifies the fix for bd comment --no-db not working. + // The bug was that initializeNoDbMode() set `store` but not `storeActive`, + // so ensureStoreActive() would try to find a SQLite database. + + tempDir := t.TempDir() + beadsDir := filepath.Join(tempDir, ".beads") + if err := os.MkdirAll(beadsDir, 0o755); err != nil { + t.Fatalf("Failed to create .beads dir: %v", err) + } + + // Create a minimal JSONL file with one issue + jsonlPath := filepath.Join(beadsDir, "issues.jsonl") + content := `{"id":"bd-1","title":"Test Issue","status":"open"} +` + if err := os.WriteFile(jsonlPath, []byte(content), 0o600); err != nil { + t.Fatalf("Failed to write JSONL: %v", err) + } + + // Save and restore global state + oldStore := store + oldStoreActive := storeActive + oldCwd, _ := os.Getwd() + defer func() { + storeMutex.Lock() + store = oldStore + storeActive = oldStoreActive + storeMutex.Unlock() + _ = os.Chdir(oldCwd) + }() + + // Change to temp dir so initializeNoDbMode finds .beads + if err := os.Chdir(tempDir); err != nil { + t.Fatalf("Failed to chdir: %v", err) + } + + // Reset global state + storeMutex.Lock() + store = nil + storeActive = false + storeMutex.Unlock() + + // Initialize no-db mode + if err := initializeNoDbMode(); err != nil { + t.Fatalf("initializeNoDbMode failed: %v", err) + } + + // Verify storeActive is now true + storeMutex.Lock() + active := storeActive + s := store + storeMutex.Unlock() + + if !active { + t.Error("storeActive should be true after initializeNoDbMode") + } + if s == nil { + t.Fatal("store should not be nil after initializeNoDbMode") + } + + // ensureStoreActive should now return immediately without error + if err := ensureStoreActive(); err != nil { + t.Errorf("ensureStoreActive should succeed after initializeNoDbMode: %v", err) + } + + // Verify comments work (this was the failing case) + ctx := rootCtx + comment, err := s.AddIssueComment(ctx, "bd-1", "testuser", "Test comment") + if err != nil { + t.Fatalf("AddIssueComment failed: %v", err) + } + if comment.Text != "Test comment" { + t.Errorf("Expected 'Test comment', got %s", comment.Text) + } + + comments, err := s.GetIssueComments(ctx, "bd-1") + if err != nil { + t.Fatalf("GetIssueComments failed: %v", err) + } + if len(comments) != 1 { + t.Errorf("Expected 1 comment, got %d", len(comments)) + } +} + func TestWriteIssuesToJSONL(t *testing.T) { tempDir := t.TempDir() beadsDir := filepath.Join(tempDir, ".beads")