Files
beads/cmd/bd/label_test.go
Steve Yegge 4ea347e08a Fix all test failures from bd-166 (missing issue_prefix)
Completed bd-167: Fixed all tests that failed with 'database not initialized: issue_prefix config is missing' error.

Changes:
- Created test helper functions in 3 locations:
  * cmd/bd/test_helpers_test.go (already existed)
  * internal/rpc/test_helpers.go (new)
  * internal/storage/sqlite/test_helpers.go (new)

- Updated all affected test files to use newTestStore():
  * cmd/bd: comments, export, git_sync, label, list, reopen, direct_mode
  * internal/rpc: rpc_test, version_test
  * internal/storage/sqlite: sqlite_test, underlying_db_test

- Fixed config test: updated flush-debounce default from 5s to 30s

- Removed unused sqlite imports from test files

All tests now passing 

Also:
- Closed bd-167, bd-170 (cleanup of beads-* duplicates)
- Removed corrupt backup files

Amp-Thread-ID: https://ampcode.com/threads/T-4a8c6002-9384-45b6-81f6-2907d1e4c6c2
Co-authored-by: Amp <amp@ampcode.com>
2025-10-26 22:31:24 -07:00

222 lines
6.2 KiB
Go

package main
import (
"context"
"os"
"path/filepath"
"testing"
"github.com/steveyegge/beads/internal/storage/sqlite"
"github.com/steveyegge/beads/internal/types"
)
type labelTestHelper struct {
s *sqlite.SQLiteStorage
ctx context.Context
t *testing.T
}
func (h *labelTestHelper) createIssue(title string, issueType types.IssueType, priority int) *types.Issue {
issue := &types.Issue{
Title: title,
Priority: priority,
IssueType: issueType,
Status: types.StatusOpen,
}
if err := h.s.CreateIssue(h.ctx, issue, "test-user"); err != nil {
h.t.Fatalf("Failed to create issue: %v", err)
}
return issue
}
func (h *labelTestHelper) addLabel(issueID, label string) {
if err := h.s.AddLabel(h.ctx, issueID, label, "test-user"); err != nil {
h.t.Fatalf("Failed to add label '%s': %v", label, err)
}
}
func (h *labelTestHelper) addLabels(issueID string, labels []string) {
for _, label := range labels {
h.addLabel(issueID, label)
}
}
func (h *labelTestHelper) removeLabel(issueID, label string) {
if err := h.s.RemoveLabel(h.ctx, issueID, label, "test-user"); err != nil {
h.t.Fatalf("Failed to remove label '%s': %v", label, err)
}
}
func (h *labelTestHelper) getLabels(issueID string) []string {
labels, err := h.s.GetLabels(h.ctx, issueID)
if err != nil {
h.t.Fatalf("Failed to get labels: %v", err)
}
return labels
}
func (h *labelTestHelper) assertLabelCount(issueID string, expected int) {
labels := h.getLabels(issueID)
if len(labels) != expected {
h.t.Errorf("Expected %d labels, got %d", expected, len(labels))
}
}
func (h *labelTestHelper) assertHasLabel(issueID, expected string) {
labels := h.getLabels(issueID)
for _, l := range labels {
if l == expected {
return
}
}
h.t.Errorf("Expected label '%s' not found", expected)
}
func (h *labelTestHelper) assertHasLabels(issueID string, expected []string) {
labels := h.getLabels(issueID)
labelMap := make(map[string]bool)
for _, l := range labels {
labelMap[l] = true
}
for _, exp := range expected {
if !labelMap[exp] {
h.t.Errorf("Expected label '%s' not found", exp)
}
}
}
func (h *labelTestHelper) assertNotHasLabel(issueID, label string) {
labels := h.getLabels(issueID)
for _, l := range labels {
if l == label {
h.t.Errorf("Did not expect label '%s' but found it", label)
}
}
}
func (h *labelTestHelper) assertLabelEvent(issueID string, eventType types.EventType, labelName string) {
events, err := h.s.GetEvents(h.ctx, issueID, 100)
if err != nil {
h.t.Fatalf("Failed to get events: %v", err)
}
expectedComment := ""
if eventType == types.EventLabelAdded {
expectedComment = "Added label: " + labelName
} else if eventType == types.EventLabelRemoved {
expectedComment = "Removed label: " + labelName
}
for _, e := range events {
if e.EventType == eventType && e.Comment != nil && *e.Comment == expectedComment {
return
}
}
h.t.Errorf("Expected to find event %s for label %s", eventType, labelName)
}
func TestLabelCommands(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "bd-test-label-*")
if err != nil {
t.Fatalf("Failed to create temp dir: %v", err)
}
defer os.RemoveAll(tmpDir)
testDB := filepath.Join(tmpDir, "test.db")
s := newTestStore(t, testDB)
defer s.Close()
ctx := context.Background()
h := &labelTestHelper{s: s, ctx: ctx, t: t}
t.Run("add label to issue", func(t *testing.T) {
issue := h.createIssue("Test Issue", types.TypeBug, 1)
h.addLabel(issue.ID, "bug")
h.assertLabelCount(issue.ID, 1)
h.assertHasLabel(issue.ID, "bug")
})
t.Run("add multiple labels", func(t *testing.T) {
issue := h.createIssue("Multi Label Issue", types.TypeFeature, 1)
labels := []string{"feature", "high-priority", "needs-review"}
h.addLabels(issue.ID, labels)
h.assertLabelCount(issue.ID, 3)
h.assertHasLabels(issue.ID, labels)
})
t.Run("add duplicate label is idempotent", func(t *testing.T) {
issue := h.createIssue("Duplicate Label Test", types.TypeTask, 1)
h.addLabel(issue.ID, "duplicate")
h.addLabel(issue.ID, "duplicate")
h.assertLabelCount(issue.ID, 1)
})
t.Run("remove label from issue", func(t *testing.T) {
issue := h.createIssue("Remove Label Test", types.TypeBug, 1)
h.addLabel(issue.ID, "temporary")
h.removeLabel(issue.ID, "temporary")
h.assertLabelCount(issue.ID, 0)
})
t.Run("remove one of multiple labels", func(t *testing.T) {
issue := h.createIssue("Multi Remove Test", types.TypeTask, 1)
labels := []string{"label1", "label2", "label3"}
h.addLabels(issue.ID, labels)
h.removeLabel(issue.ID, "label2")
h.assertLabelCount(issue.ID, 2)
h.assertNotHasLabel(issue.ID, "label2")
})
t.Run("remove non-existent label is no-op", func(t *testing.T) {
issue := h.createIssue("Remove Non-Existent Test", types.TypeTask, 1)
h.addLabel(issue.ID, "exists")
h.removeLabel(issue.ID, "does-not-exist")
h.assertLabelCount(issue.ID, 1)
})
t.Run("get labels for issue with no labels", func(t *testing.T) {
issue := h.createIssue("No Labels Test", types.TypeTask, 1)
h.assertLabelCount(issue.ID, 0)
})
t.Run("label operations create events", func(t *testing.T) {
issue := h.createIssue("Event Test", types.TypeTask, 1)
h.addLabel(issue.ID, "test-label")
h.removeLabel(issue.ID, "test-label")
h.assertLabelEvent(issue.ID, types.EventLabelAdded, "test-label")
h.assertLabelEvent(issue.ID, types.EventLabelRemoved, "test-label")
})
t.Run("labels persist after issue update", func(t *testing.T) {
issue := h.createIssue("Persistence Test", types.TypeTask, 1)
h.addLabel(issue.ID, "persistent")
updates := map[string]interface{}{
"description": "Updated description",
"priority": 2,
}
if err := s.UpdateIssue(ctx, issue.ID, updates, "test-user"); err != nil {
t.Fatalf("Failed to update issue: %v", err)
}
h.assertLabelCount(issue.ID, 1)
h.assertHasLabel(issue.ID, "persistent")
})
t.Run("labels work with different issue types", func(t *testing.T) {
issueTypes := []types.IssueType{
types.TypeBug,
types.TypeFeature,
types.TypeTask,
types.TypeEpic,
types.TypeChore,
}
for _, issueType := range issueTypes {
issue := h.createIssue("Type Test: "+string(issueType), issueType, 1)
labelName := "type-" + string(issueType)
h.addLabel(issue.ID, labelName)
h.assertLabelCount(issue.ID, 1)
h.assertHasLabel(issue.ID, labelName)
}
})
}