Files
beads/internal/storage/sqlite/events_test.go

361 lines
8.2 KiB
Go

package sqlite
import (
"context"
"strings"
"testing"
"github.com/steveyegge/beads/internal/types"
)
const testUserAlice = "alice"
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, testUserAlice, "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 != testUserAlice {
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")
}
}
func TestAddCommentNotFound(t *testing.T) {
store, cleanup := setupTestDB(t)
defer cleanup()
ctx := context.Background()
nonExistentID := "bd-999"
err := store.AddComment(ctx, nonExistentID, "alice", "This should fail cleanly")
if err == nil {
t.Fatal("Expected error, got nil")
}
expectedError := "issue bd-999 not found"
if !strings.Contains(err.Error(), expectedError) {
t.Errorf("Expected error to contain %q, got %q", expectedError, err.Error())
}
}