Files
beads/internal/storage/sqlite/events_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

340 lines
7.6 KiB
Go

package sqlite
import (
"context"
"testing"
"github.com/steveyegge/beads/internal/types"
)
func TestAddComment(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)
}
// Add a comment
err = store.AddComment(ctx, issue.ID, "alice", "This is a test comment")
if err != nil {
t.Fatalf("AddComment failed: %v", err)
}
// Get events to verify comment was added
events, err := store.GetEvents(ctx, issue.ID, 0)
if err != nil {
t.Fatalf("GetEvents failed: %v", err)
}
// Should have 2 events: created and commented
if len(events) < 2 {
t.Fatalf("Expected at least 2 events, got %d", len(events))
}
// Find the comment event (most recent should be first due to DESC order)
var commentEvent *types.Event
for _, event := range events {
if event.EventType == types.EventCommented {
commentEvent = event
break
}
}
if commentEvent == nil {
t.Fatal("Comment event not found")
}
if commentEvent.Actor != "alice" {
t.Errorf("Expected actor 'alice', got '%s'", commentEvent.Actor)
}
if commentEvent.Comment == nil || *commentEvent.Comment != "This is a test comment" {
t.Errorf("Expected comment 'This is a test comment', got %v", commentEvent.Comment)
}
}
func TestAddMultipleComments(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)
}
// Add multiple comments
comments := []struct {
actor string
comment string
}{
{"alice", "First comment"},
{"bob", "Second comment"},
{"charlie", "Third comment"},
}
for _, c := range comments {
err = store.AddComment(ctx, issue.ID, c.actor, c.comment)
if err != nil {
t.Fatalf("AddComment failed: %v", err)
}
}
// Get events
events, err := store.GetEvents(ctx, issue.ID, 0)
if err != nil {
t.Fatalf("GetEvents failed: %v", err)
}
// Count comment events
commentCount := 0
var commentEvents []*types.Event
for _, event := range events {
if event.EventType == types.EventCommented {
commentCount++
commentEvents = append(commentEvents, event)
}
}
if commentCount != 3 {
t.Fatalf("Expected 3 comment events, got %d", commentCount)
}
// Verify we can find all three comments
foundComments := make(map[string]bool)
for _, event := range commentEvents {
if event.Comment != nil {
foundComments[*event.Comment] = true
}
}
expectedComments := []string{"First comment", "Second comment", "Third comment"}
for _, expected := range expectedComments {
if !foundComments[expected] {
t.Errorf("Expected to find comment '%s'", expected)
}
}
}
func TestGetEventsWithLimit(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)
}
// Add 5 comments
for i := 0; i < 5; i++ {
err = store.AddComment(ctx, issue.ID, "alice", "Comment")
if err != nil {
t.Fatalf("AddComment failed: %v", err)
}
}
// Get events with limit
events, err := store.GetEvents(ctx, issue.ID, 3)
if err != nil {
t.Fatalf("GetEvents failed: %v", err)
}
if len(events) != 3 {
t.Errorf("Expected 3 events with limit, got %d", len(events))
}
}
func TestGetEventsEmpty(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
// Get events for non-existent issue
events, err := store.GetEvents(ctx, "bd-999", 0)
if err != nil {
t.Fatalf("GetEvents failed: %v", err)
}
if len(events) != 0 {
t.Errorf("Expected 0 events for non-existent issue, got %d", len(events))
}
}
func TestAddCommentMarksDirty(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)
}
// Clear dirty issues
err = store.ClearDirtyIssues(ctx)
if err != nil {
t.Fatalf("ClearDirtyIssues failed: %v", err)
}
// Add comment - should mark issue dirty
err = store.AddComment(ctx, issue.ID, "alice", "Test comment")
if err != nil {
t.Fatalf("AddComment failed: %v", err)
}
// Verify issue is dirty
dirtyIssues, err := store.GetDirtyIssues(ctx)
if err != nil {
t.Fatalf("GetDirtyIssues failed: %v", err)
}
if len(dirtyIssues) != 1 || dirtyIssues[0] != issue.ID {
t.Error("Expected issue to be marked dirty after adding comment")
}
}
func TestAddCommentUpdatesTimestamp(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)
}
originalUpdatedAt := issue.UpdatedAt
// Add comment
err = store.AddComment(ctx, issue.ID, "alice", "Test comment")
if err != nil {
t.Fatalf("AddComment failed: %v", err)
}
// Get issue again and verify updated_at changed
updatedIssue, err := store.GetIssue(ctx, issue.ID)
if err != nil {
t.Fatalf("GetIssue failed: %v", err)
}
if !updatedIssue.UpdatedAt.After(originalUpdatedAt) {
t.Error("Expected updated_at to be updated after adding comment")
}
}
func TestEventTypesInHistory(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)
}
// Perform various operations that create events
err = store.UpdateIssue(ctx, issue.ID, map[string]interface{}{
"priority": 2,
}, "test-user")
if err != nil {
t.Fatalf("UpdateIssue failed: %v", err)
}
err = store.AddComment(ctx, issue.ID, "alice", "A comment")
if err != nil {
t.Fatalf("AddComment failed: %v", err)
}
err = store.AddLabel(ctx, issue.ID, "bug", "test-user")
if err != nil {
t.Fatalf("AddLabel failed: %v", err)
}
err = store.CloseIssue(ctx, issue.ID, "Done", "test-user")
if err != nil {
t.Fatalf("CloseIssue failed: %v", err)
}
// Get events
events, err := store.GetEvents(ctx, issue.ID, 0)
if err != nil {
t.Fatalf("GetEvents failed: %v", err)
}
// Should have multiple event types
eventTypes := make(map[types.EventType]bool)
for _, event := range events {
eventTypes[event.EventType] = true
}
// Verify we have different event types
if !eventTypes[types.EventCreated] {
t.Error("Expected EventCreated in history")
}
if !eventTypes[types.EventUpdated] {
t.Error("Expected EventUpdated in history")
}
if !eventTypes[types.EventCommented] {
t.Error("Expected EventCommented in history")
}
if !eventTypes[types.EventLabelAdded] {
t.Error("Expected EventLabelAdded in history")
}
if !eventTypes[types.EventClosed] {
t.Error("Expected EventClosed in history")
}
}