Files
beads/internal/storage/sqlite/dirty_test.go
Steve Yegge 587e1d9d0f test: Add comprehensive test coverage for storage layer
Added test files for core SQLite storage functionality:
- beads_test.go: Database path detection tests
- dirty_test.go: Dirty tracking for auto-flush
- events_test.go: Event logging tests
- labels_test.go: Label management tests
- sqlite_test.go: Added metadata tests (SetMetadata, GetMetadata)

Merged with upstream TestParallelIssueCreation (bd-89 regression test).

All tests passing. Ready to push.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 01:30:28 -07:00

314 lines
7.3 KiB
Go

package sqlite
import (
"context"
"testing"
"time"
"github.com/steveyegge/beads/internal/types"
)
func TestMarkIssueDirty(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
// Create an issue first
issue := &types.Issue{
Title: "Test issue",
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeTask,
}
err := store.CreateIssue(ctx, issue, "test-user")
if err != nil {
t.Fatalf("CreateIssue failed: %v", err)
}
// Issue should already be marked dirty by CreateIssue
dirtyIssues, err := store.GetDirtyIssues(ctx)
if err != nil {
t.Fatalf("GetDirtyIssues failed: %v", err)
}
if len(dirtyIssues) != 1 {
t.Fatalf("Expected 1 dirty issue, got %d", len(dirtyIssues))
}
if dirtyIssues[0] != issue.ID {
t.Errorf("Expected dirty issue %s, got %s", issue.ID, dirtyIssues[0])
}
// Clear dirty issues
err = store.ClearDirtyIssues(ctx)
if err != nil {
t.Fatalf("ClearDirtyIssues failed: %v", err)
}
// Verify cleared
dirtyIssues, err = store.GetDirtyIssues(ctx)
if err != nil {
t.Fatalf("GetDirtyIssues failed: %v", err)
}
if len(dirtyIssues) != 0 {
t.Errorf("Expected 0 dirty issues after clear, got %d", len(dirtyIssues))
}
// Mark it dirty again manually
err = store.MarkIssueDirty(ctx, issue.ID)
if err != nil {
t.Fatalf("MarkIssueDirty failed: %v", err)
}
// Verify it's dirty again
dirtyIssues, err = store.GetDirtyIssues(ctx)
if err != nil {
t.Fatalf("GetDirtyIssues failed: %v", err)
}
if len(dirtyIssues) != 1 {
t.Errorf("Expected 1 dirty issue after marking, got %d", len(dirtyIssues))
}
}
func TestMarkIssuesDirty(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
// Create multiple issues
var issueIDs []string
for i := 0; i < 3; i++ {
issue := &types.Issue{
Title: "Test issue",
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeTask,
}
err := store.CreateIssue(ctx, issue, "test-user")
if err != nil {
t.Fatalf("CreateIssue failed: %v", err)
}
issueIDs = append(issueIDs, issue.ID)
}
// Clear all dirty issues
err := store.ClearDirtyIssues(ctx)
if err != nil {
t.Fatalf("ClearDirtyIssues failed: %v", err)
}
// Mark multiple issues dirty at once
err = store.MarkIssuesDirty(ctx, issueIDs)
if err != nil {
t.Fatalf("MarkIssuesDirty failed: %v", err)
}
// Verify all are dirty
dirtyIssues, err := store.GetDirtyIssues(ctx)
if err != nil {
t.Fatalf("GetDirtyIssues failed: %v", err)
}
if len(dirtyIssues) != 3 {
t.Errorf("Expected 3 dirty issues, got %d", len(dirtyIssues))
}
}
func TestMarkIssuesDirtyEmpty(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
// Mark empty slice - should not error
err := store.MarkIssuesDirty(ctx, []string{})
if err != nil {
t.Errorf("MarkIssuesDirty with empty slice should not error: %v", err)
}
}
func TestGetDirtyIssueCount(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
// Count should be 0 initially
count, err := store.GetDirtyIssueCount(ctx)
if err != nil {
t.Fatalf("GetDirtyIssueCount failed: %v", err)
}
if count != 0 {
t.Errorf("Expected 0 dirty issues, got %d", count)
}
// Create an issue
issue := &types.Issue{
Title: "Test issue",
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeTask,
}
err = store.CreateIssue(ctx, issue, "test-user")
if err != nil {
t.Fatalf("CreateIssue failed: %v", err)
}
// Count should be 1 now
count, err = store.GetDirtyIssueCount(ctx)
if err != nil {
t.Fatalf("GetDirtyIssueCount failed: %v", err)
}
if count != 1 {
t.Errorf("Expected 1 dirty issue, got %d", count)
}
}
func TestClearDirtyIssuesByID(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
// Create multiple issues
var issueIDs []string
for i := 0; i < 5; i++ {
issue := &types.Issue{
Title: "Test issue",
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeTask,
}
err := store.CreateIssue(ctx, issue, "test-user")
if err != nil {
t.Fatalf("CreateIssue failed: %v", err)
}
issueIDs = append(issueIDs, issue.ID)
}
// Verify all are dirty
count, err := store.GetDirtyIssueCount(ctx)
if err != nil {
t.Fatalf("GetDirtyIssueCount failed: %v", err)
}
if count != 5 {
t.Errorf("Expected 5 dirty issues, got %d", count)
}
// Clear only the first 3
err = store.ClearDirtyIssuesByID(ctx, issueIDs[:3])
if err != nil {
t.Fatalf("ClearDirtyIssuesByID failed: %v", err)
}
// Should have 2 remaining
count, err = store.GetDirtyIssueCount(ctx)
if err != nil {
t.Fatalf("GetDirtyIssueCount failed: %v", err)
}
if count != 2 {
t.Errorf("Expected 2 dirty issues remaining, got %d", count)
}
// Verify the correct ones remain
dirtyIssues, err := store.GetDirtyIssues(ctx)
if err != nil {
t.Fatalf("GetDirtyIssues failed: %v", err)
}
for _, id := range dirtyIssues {
if id == issueIDs[0] || id == issueIDs[1] || id == issueIDs[2] {
t.Errorf("Issue %s should have been cleared", id)
}
}
}
func TestClearDirtyIssuesByIDEmpty(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
// Clear empty slice - should not error
err := store.ClearDirtyIssuesByID(ctx, []string{})
if err != nil {
t.Errorf("ClearDirtyIssuesByID with empty slice should not error: %v", err)
}
}
func TestDirtyIssuesOrdering(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
// Create issues with slight time delays to ensure ordering
var issueIDs []string
for i := 0; i < 3; i++ {
issue := &types.Issue{
Title: "Test issue",
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeTask,
}
err := store.CreateIssue(ctx, issue, "test-user")
if err != nil {
t.Fatalf("CreateIssue failed: %v", err)
}
issueIDs = append(issueIDs, issue.ID)
time.Sleep(10 * time.Millisecond) // Ensure different timestamps
}
// Get dirty issues - should be in order by marked_at (oldest first)
dirtyIssues, err := store.GetDirtyIssues(ctx)
if err != nil {
t.Fatalf("GetDirtyIssues failed: %v", err)
}
if len(dirtyIssues) != 3 {
t.Fatalf("Expected 3 dirty issues, got %d", len(dirtyIssues))
}
// Verify order matches creation order
for i, id := range issueIDs {
if dirtyIssues[i] != id {
t.Errorf("Expected issue %d to be %s, got %s", i, id, dirtyIssues[i])
}
}
}
func TestDirtyIssuesUpdateTimestamp(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
// Create an issue
issue := &types.Issue{
Title: "Test issue",
Status: types.StatusOpen,
Priority: 1,
IssueType: types.TypeTask,
}
err := store.CreateIssue(ctx, issue, "test-user")
if err != nil {
t.Fatalf("CreateIssue failed: %v", err)
}
// Wait a bit
time.Sleep(50 * time.Millisecond)
// Mark dirty again - should update timestamp
err = store.MarkIssueDirty(ctx, issue.ID)
if err != nil {
t.Fatalf("MarkIssueDirty failed: %v", err)
}
// Should still have only 1 dirty issue (ON CONFLICT DO UPDATE)
count, err := store.GetDirtyIssueCount(ctx)
if err != nil {
t.Fatalf("GetDirtyIssueCount failed: %v", err)
}
if count != 1 {
t.Errorf("Expected 1 dirty issue after re-marking, got %d", count)
}
}