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>
314 lines
7.3 KiB
Go
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)
|
|
}
|
|
}
|