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>
395 lines
8.9 KiB
Go
395 lines
8.9 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/steveyegge/beads/internal/types"
|
|
)
|
|
|
|
func TestAddLabel(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 label
|
|
err = store.AddLabel(ctx, issue.ID, "bug", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel failed: %v", err)
|
|
}
|
|
|
|
// Get labels
|
|
labels, err := store.GetLabels(ctx, issue.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetLabels failed: %v", err)
|
|
}
|
|
|
|
if len(labels) != 1 {
|
|
t.Fatalf("Expected 1 label, got %d", len(labels))
|
|
}
|
|
|
|
if labels[0] != "bug" {
|
|
t.Errorf("Expected label 'bug', got '%s'", labels[0])
|
|
}
|
|
}
|
|
|
|
func TestAddMultipleLabels(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 labels
|
|
labelsToAdd := []string{"bug", "critical", "ui"}
|
|
for _, label := range labelsToAdd {
|
|
err = store.AddLabel(ctx, issue.ID, label, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel failed for %s: %v", label, err)
|
|
}
|
|
}
|
|
|
|
// Get labels
|
|
labels, err := store.GetLabels(ctx, issue.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetLabels failed: %v", err)
|
|
}
|
|
|
|
if len(labels) != 3 {
|
|
t.Fatalf("Expected 3 labels, got %d", len(labels))
|
|
}
|
|
|
|
// Verify labels are sorted alphabetically
|
|
expectedOrder := []string{"bug", "critical", "ui"}
|
|
for i, expected := range expectedOrder {
|
|
if labels[i] != expected {
|
|
t.Errorf("Expected label %d to be '%s', got '%s'", i, expected, labels[i])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAddDuplicateLabel(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 label
|
|
err = store.AddLabel(ctx, issue.ID, "bug", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel failed: %v", err)
|
|
}
|
|
|
|
// Add the same label again - should not error (INSERT OR IGNORE)
|
|
err = store.AddLabel(ctx, issue.ID, "bug", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel duplicate should not error: %v", err)
|
|
}
|
|
|
|
// Should still have only 1 label
|
|
labels, err := store.GetLabels(ctx, issue.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetLabels failed: %v", err)
|
|
}
|
|
|
|
if len(labels) != 1 {
|
|
t.Errorf("Expected 1 label after duplicate add, got %d", len(labels))
|
|
}
|
|
}
|
|
|
|
func TestRemoveLabel(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 labels
|
|
err = store.AddLabel(ctx, issue.ID, "bug", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel failed: %v", err)
|
|
}
|
|
err = store.AddLabel(ctx, issue.ID, "critical", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel failed: %v", err)
|
|
}
|
|
|
|
// Remove one label
|
|
err = store.RemoveLabel(ctx, issue.ID, "bug", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("RemoveLabel failed: %v", err)
|
|
}
|
|
|
|
// Get labels
|
|
labels, err := store.GetLabels(ctx, issue.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetLabels failed: %v", err)
|
|
}
|
|
|
|
if len(labels) != 1 {
|
|
t.Fatalf("Expected 1 label after removal, got %d", len(labels))
|
|
}
|
|
|
|
if labels[0] != "critical" {
|
|
t.Errorf("Expected remaining label 'critical', got '%s'", labels[0])
|
|
}
|
|
}
|
|
|
|
func TestRemoveNonexistentLabel(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)
|
|
}
|
|
|
|
// Remove a label that doesn't exist - should not error
|
|
err = store.RemoveLabel(ctx, issue.ID, "nonexistent", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("RemoveLabel for nonexistent label should not error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestGetLabelsEmpty(t *testing.T) {
|
|
store, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create an issue without labels
|
|
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)
|
|
}
|
|
|
|
// Get labels - should return nil or empty slice (both valid in Go)
|
|
labels, err := store.GetLabels(ctx, issue.ID)
|
|
if err != nil {
|
|
t.Fatalf("GetLabels failed: %v", err)
|
|
}
|
|
|
|
if len(labels) != 0 {
|
|
t.Errorf("Expected 0 labels, got %d", len(labels))
|
|
}
|
|
}
|
|
|
|
func TestGetIssuesByLabel(t *testing.T) {
|
|
store, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create issues with different labels
|
|
issue1 := &types.Issue{
|
|
Title: "Bug 1",
|
|
Status: types.StatusOpen,
|
|
Priority: 0,
|
|
IssueType: types.TypeBug,
|
|
}
|
|
err := store.CreateIssue(ctx, issue1, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("CreateIssue failed: %v", err)
|
|
}
|
|
err = store.AddLabel(ctx, issue1.ID, "critical", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel failed: %v", err)
|
|
}
|
|
|
|
issue2 := &types.Issue{
|
|
Title: "Bug 2",
|
|
Status: types.StatusOpen,
|
|
Priority: 1,
|
|
IssueType: types.TypeBug,
|
|
}
|
|
err = store.CreateIssue(ctx, issue2, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("CreateIssue failed: %v", err)
|
|
}
|
|
err = store.AddLabel(ctx, issue2.ID, "critical", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel failed: %v", err)
|
|
}
|
|
|
|
issue3 := &types.Issue{
|
|
Title: "Feature",
|
|
Status: types.StatusOpen,
|
|
Priority: 2,
|
|
IssueType: types.TypeFeature,
|
|
}
|
|
err = store.CreateIssue(ctx, issue3, "test-user")
|
|
if err != nil {
|
|
t.Fatalf("CreateIssue failed: %v", err)
|
|
}
|
|
err = store.AddLabel(ctx, issue3.ID, "enhancement", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel failed: %v", err)
|
|
}
|
|
|
|
// Get issues by label "critical"
|
|
issues, err := store.GetIssuesByLabel(ctx, "critical")
|
|
if err != nil {
|
|
t.Fatalf("GetIssuesByLabel failed: %v", err)
|
|
}
|
|
|
|
if len(issues) != 2 {
|
|
t.Fatalf("Expected 2 issues with 'critical' label, got %d", len(issues))
|
|
}
|
|
|
|
// Verify both critical issues are returned
|
|
foundIssue1 := false
|
|
foundIssue2 := false
|
|
for _, issue := range issues {
|
|
if issue.ID == issue1.ID {
|
|
foundIssue1 = true
|
|
}
|
|
if issue.ID == issue2.ID {
|
|
foundIssue2 = true
|
|
}
|
|
}
|
|
|
|
if !foundIssue1 || !foundIssue2 {
|
|
t.Error("Expected both critical issues to be returned")
|
|
}
|
|
}
|
|
|
|
func TestGetIssuesByLabelEmpty(t *testing.T) {
|
|
store, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
|
|
// Get issues by nonexistent label
|
|
issues, err := store.GetIssuesByLabel(ctx, "nonexistent")
|
|
if err != nil {
|
|
t.Fatalf("GetIssuesByLabel failed: %v", err)
|
|
}
|
|
|
|
if len(issues) != 0 {
|
|
t.Errorf("Expected 0 issues for nonexistent label, got %d", len(issues))
|
|
}
|
|
}
|
|
|
|
func TestLabelMarksDirty(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 label - should mark issue dirty
|
|
err = store.AddLabel(ctx, issue.ID, "bug", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("AddLabel 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 label")
|
|
}
|
|
|
|
// Clear dirty again
|
|
err = store.ClearDirtyIssues(ctx)
|
|
if err != nil {
|
|
t.Fatalf("ClearDirtyIssues failed: %v", err)
|
|
}
|
|
|
|
// Remove label - should mark issue dirty
|
|
err = store.RemoveLabel(ctx, issue.ID, "bug", "test-user")
|
|
if err != nil {
|
|
t.Fatalf("RemoveLabel failed: %v", err)
|
|
}
|
|
|
|
// Verify issue is dirty again
|
|
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 removing label")
|
|
}
|
|
}
|