Files
kandev/apps/backend/internal/office/repository/sqlite/labels_test.go
T
2026-05-16 20:07:57 +01:00

176 lines
4.5 KiB
Go

package sqlite_test
import (
"context"
"testing"
)
func TestGetOrCreateLabel_CreatesNew(t *testing.T) {
repo := newTestRepo(t)
ctx := context.Background()
lbl, err := repo.GetOrCreateLabel(ctx, "ws-1", "bug")
if err != nil {
t.Fatalf("GetOrCreateLabel: %v", err)
}
if lbl.ID == "" {
t.Error("expected non-empty label ID")
}
if lbl.Name != "bug" {
t.Errorf("name = %q, want %q", lbl.Name, "bug")
}
if lbl.WorkspaceID != "ws-1" {
t.Errorf("workspace_id = %q, want %q", lbl.WorkspaceID, "ws-1")
}
if lbl.Color == "" {
t.Error("expected non-empty color")
}
}
func TestGetOrCreateLabel_ReturnsExisting(t *testing.T) {
repo := newTestRepo(t)
ctx := context.Background()
first, err := repo.GetOrCreateLabel(ctx, "ws-1", "bug")
if err != nil {
t.Fatalf("first GetOrCreateLabel: %v", err)
}
second, err := repo.GetOrCreateLabel(ctx, "ws-1", "bug")
if err != nil {
t.Fatalf("second GetOrCreateLabel: %v", err)
}
if first.ID != second.ID {
t.Errorf("expected same label ID; got %q and %q", first.ID, second.ID)
}
if first.Color != second.Color {
t.Errorf("color changed on re-fetch: %q vs %q", first.Color, second.Color)
}
}
func TestAddRemoveLabelToTask(t *testing.T) {
repo := newTestRepo(t)
ctx := context.Background()
lbl, err := repo.GetOrCreateLabel(ctx, "ws-1", "urgent")
if err != nil {
t.Fatalf("GetOrCreateLabel: %v", err)
}
if err := repo.AddLabelToTask(ctx, "task-1", lbl.ID); err != nil {
t.Fatalf("AddLabelToTask: %v", err)
}
// Idempotent — adding again should not error.
if err := repo.AddLabelToTask(ctx, "task-1", lbl.ID); err != nil {
t.Fatalf("AddLabelToTask (idempotent): %v", err)
}
labels, err := repo.ListLabelsForTask(ctx, "task-1")
if err != nil {
t.Fatalf("ListLabelsForTask: %v", err)
}
if len(labels) != 1 {
t.Fatalf("count = %d, want 1", len(labels))
}
if labels[0].Name != "urgent" {
t.Errorf("label name = %q, want %q", labels[0].Name, "urgent")
}
if err := repo.RemoveLabelFromTask(ctx, "task-1", lbl.ID); err != nil {
t.Fatalf("RemoveLabelFromTask: %v", err)
}
labels, err = repo.ListLabelsForTask(ctx, "task-1")
if err != nil {
t.Fatalf("ListLabelsForTask after remove: %v", err)
}
if len(labels) != 0 {
t.Errorf("count after remove = %d, want 0", len(labels))
}
}
func TestListLabelsForTask(t *testing.T) {
repo := newTestRepo(t)
ctx := context.Background()
names := []string{"alpha", "beta", "gamma"}
for _, n := range names {
lbl, err := repo.GetOrCreateLabel(ctx, "ws-2", n)
if err != nil {
t.Fatalf("GetOrCreateLabel %q: %v", n, err)
}
if err := repo.AddLabelToTask(ctx, "task-2", lbl.ID); err != nil {
t.Fatalf("AddLabelToTask %q: %v", n, err)
}
}
labels, err := repo.ListLabelsForTask(ctx, "task-2")
if err != nil {
t.Fatalf("ListLabelsForTask: %v", err)
}
if len(labels) != 3 {
t.Fatalf("count = %d, want 3", len(labels))
}
// Results are ordered by name.
for i, want := range names {
if labels[i].Name != want {
t.Errorf("label[%d].Name = %q, want %q", i, labels[i].Name, want)
}
}
}
func TestListLabelsForTasks_Batch(t *testing.T) {
repo := newTestRepo(t)
ctx := context.Background()
lbl1, _ := repo.GetOrCreateLabel(ctx, "ws-3", "backend")
lbl2, _ := repo.GetOrCreateLabel(ctx, "ws-3", "frontend")
_ = repo.AddLabelToTask(ctx, "t-a", lbl1.ID)
_ = repo.AddLabelToTask(ctx, "t-b", lbl2.ID)
_ = repo.AddLabelToTask(ctx, "t-b", lbl1.ID)
result, err := repo.ListLabelsForTasks(ctx, []string{"t-a", "t-b", "t-c"})
if err != nil {
t.Fatalf("ListLabelsForTasks: %v", err)
}
if len(result["t-a"]) != 1 {
t.Errorf("t-a label count = %d, want 1", len(result["t-a"]))
}
if len(result["t-b"]) != 2 {
t.Errorf("t-b label count = %d, want 2", len(result["t-b"]))
}
if len(result["t-c"]) != 0 {
t.Errorf("t-c label count = %d, want 0", len(result["t-c"]))
}
}
func TestDeleteLabel_CascadesJunction(t *testing.T) {
repo := newTestRepo(t)
ctx := context.Background()
lbl, err := repo.GetOrCreateLabel(ctx, "ws-4", "to-delete")
if err != nil {
t.Fatalf("GetOrCreateLabel: %v", err)
}
// Attach to two tasks.
_ = repo.AddLabelToTask(ctx, "td-1", lbl.ID)
_ = repo.AddLabelToTask(ctx, "td-2", lbl.ID)
if err := repo.DeleteLabel(ctx, lbl.ID); err != nil {
t.Fatalf("DeleteLabel: %v", err)
}
// Junction rows should be gone via ON DELETE CASCADE.
lbls1, _ := repo.ListLabelsForTask(ctx, "td-1")
if len(lbls1) != 0 {
t.Errorf("td-1 label count after cascade delete = %d, want 0", len(lbls1))
}
lbls2, _ := repo.ListLabelsForTask(ctx, "td-2")
if len(lbls2) != 0 {
t.Errorf("td-2 label count after cascade delete = %d, want 0", len(lbls2))
}
}