From b1d6c218bcc777c320edfd1af3398a5a5880eacd Mon Sep 17 00:00:00 2001 From: Amolith Date: Mon, 22 Dec 2025 09:29:45 -0700 Subject: [PATCH] test(integration): add workflow metadata tests Test combinations of status, eisenhower, priority, and motivation fields to determine whether lune needs to warn users about workflow restrictions. Covers: - All metadata fields together - Individual field isolation - All eisenhower quadrants - All kanban status values - Field clearing and preservation - Cross-workflow updates Assisted-by: Claude Opus 4 via Crush --- integration_test.go | 370 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 370 insertions(+) diff --git a/integration_test.go b/integration_test.go index c59d3ed0ca1ec7b14e6ca7e3b67d04a2c3018077..06b7981f231a49d78a1bc593e9e67908ca299216 100644 --- a/integration_test.go +++ b/integration_test.go @@ -168,3 +168,373 @@ func TestIntegration_NoteRoundTrip(t *testing.T) { t.Logf("Updated note: %s", updated.ID) } + +// TestIntegration_TaskWorkflowMetadata tests how the API handles various combinations +// of workflow metadata fields. This helps determine whether lune needs to warn users +// about workflow restrictions. +func TestIntegration_TaskWorkflowMetadata(t *testing.T) { + t.Run("all_metadata_fields", func(t *testing.T) { + name := testName("metadata-all") + task, err := integrationClient.NewTask(name). + InArea(testAreaID). + WithStatus(lunatask.StatusNext). + WithEisenhower(lunatask.EisenhowerDoNow). + Priority(lunatask.PriorityHigh). + WithMotivation(lunatask.MotivationMust). + WithEstimate(60). + Create(ctx()) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if task == nil { + t.Fatal("Create() returned nil (duplicate?)") + } + + t.Cleanup(func() { + if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil { + t.Errorf("DeleteTask() cleanup error = %v", err) + } + }) + + fetched, err := integrationClient.GetTask(ctx(), task.ID) + if err != nil { + t.Fatalf("GetTask() error = %v", err) + } + + logMetadata(t, "all_fields", fetched) + if fetched.Status == nil || *fetched.Status != lunatask.StatusNext { + t.Errorf("Status = %v, want %v", fetched.Status, lunatask.StatusNext) + } + if fetched.Eisenhower == nil || *fetched.Eisenhower != lunatask.EisenhowerDoNow { + t.Errorf("Eisenhower = %v, want %v", fetched.Eisenhower, lunatask.EisenhowerDoNow) + } + if fetched.Priority == nil || *fetched.Priority != lunatask.PriorityHigh { + t.Errorf("Priority = %v, want %v", fetched.Priority, lunatask.PriorityHigh) + } + if fetched.Motivation == nil || *fetched.Motivation != lunatask.MotivationMust { + t.Errorf("Motivation = %v, want %v", fetched.Motivation, lunatask.MotivationMust) + } + }) + + t.Run("eisenhower_only", func(t *testing.T) { + name := testName("metadata-eisenhower") + task, err := integrationClient.NewTask(name). + InArea(testAreaID). + WithEisenhower(lunatask.EisenhowerDoLater). + Create(ctx()) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if task == nil { + t.Fatal("Create() returned nil (duplicate?)") + } + + t.Cleanup(func() { + if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil { + t.Errorf("DeleteTask() cleanup error = %v", err) + } + }) + + fetched, err := integrationClient.GetTask(ctx(), task.ID) + if err != nil { + t.Fatalf("GetTask() error = %v", err) + } + + logMetadata(t, "eisenhower_only", fetched) + if fetched.Eisenhower == nil || *fetched.Eisenhower != lunatask.EisenhowerDoLater { + t.Errorf("Eisenhower = %v, want %v", fetched.Eisenhower, lunatask.EisenhowerDoLater) + } + }) + + t.Run("kanban_status_only", func(t *testing.T) { + name := testName("metadata-kanban") + task, err := integrationClient.NewTask(name). + InArea(testAreaID). + WithStatus(lunatask.StatusWaiting). + Create(ctx()) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if task == nil { + t.Fatal("Create() returned nil (duplicate?)") + } + + t.Cleanup(func() { + if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil { + t.Errorf("DeleteTask() cleanup error = %v", err) + } + }) + + fetched, err := integrationClient.GetTask(ctx(), task.ID) + if err != nil { + t.Fatalf("GetTask() error = %v", err) + } + + logMetadata(t, "kanban_only", fetched) + if fetched.Status == nil || *fetched.Status != lunatask.StatusWaiting { + t.Errorf("Status = %v, want %v", fetched.Status, lunatask.StatusWaiting) + } + }) + + t.Run("priority_and_motivation", func(t *testing.T) { + name := testName("metadata-pri-mot") + task, err := integrationClient.NewTask(name). + InArea(testAreaID). + Priority(lunatask.PriorityHighest). + WithMotivation(lunatask.MotivationWant). + Create(ctx()) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if task == nil { + t.Fatal("Create() returned nil (duplicate?)") + } + + t.Cleanup(func() { + if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil { + t.Errorf("DeleteTask() cleanup error = %v", err) + } + }) + + fetched, err := integrationClient.GetTask(ctx(), task.ID) + if err != nil { + t.Fatalf("GetTask() error = %v", err) + } + + logMetadata(t, "priority_motivation", fetched) + if fetched.Priority == nil || *fetched.Priority != lunatask.PriorityHighest { + t.Errorf("Priority = %v, want %v", fetched.Priority, lunatask.PriorityHighest) + } + if fetched.Motivation == nil || *fetched.Motivation != lunatask.MotivationWant { + t.Errorf("Motivation = %v, want %v", fetched.Motivation, lunatask.MotivationWant) + } + }) + + t.Run("all_eisenhower_quadrants", func(t *testing.T) { + quadrants := []struct { + name string + quadrant lunatask.Eisenhower + important bool + urgent bool + }{ + {"do_now", lunatask.EisenhowerDoNow, true, true}, + {"delegate", lunatask.EisenhowerDelegate, false, true}, + {"do_later", lunatask.EisenhowerDoLater, true, false}, + {"eliminate", lunatask.EisenhowerEliminate, false, false}, + } + + for _, q := range quadrants { + t.Run(q.name, func(t *testing.T) { + name := testName("eisenhower-" + q.name) + task, err := integrationClient.NewTask(name). + InArea(testAreaID). + WithEisenhower(q.quadrant). + Create(ctx()) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if task == nil { + t.Fatal("Create() returned nil (duplicate?)") + } + + t.Cleanup(func() { + if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil { + t.Errorf("DeleteTask() cleanup error = %v", err) + } + }) + + fetched, err := integrationClient.GetTask(ctx(), task.ID) + if err != nil { + t.Fatalf("GetTask() error = %v", err) + } + + t.Logf("Quadrant %s: eisenhower=%v", q.name, fetched.Eisenhower) + if fetched.Eisenhower == nil || *fetched.Eisenhower != q.quadrant { + t.Errorf("Eisenhower = %v, want %v", fetched.Eisenhower, q.quadrant) + } + }) + } + }) + + t.Run("all_status_values", func(t *testing.T) { + statuses := []lunatask.TaskStatus{ + lunatask.StatusLater, + lunatask.StatusNext, + lunatask.StatusInProgress, + lunatask.StatusWaiting, + } + + for _, status := range statuses { + t.Run(string(status), func(t *testing.T) { + name := testName("status-" + string(status)) + task, err := integrationClient.NewTask(name). + InArea(testAreaID). + WithStatus(status). + Create(ctx()) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if task == nil { + t.Fatal("Create() returned nil (duplicate?)") + } + + t.Cleanup(func() { + if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil { + t.Errorf("DeleteTask() cleanup error = %v", err) + } + }) + + fetched, err := integrationClient.GetTask(ctx(), task.ID) + if err != nil { + t.Fatalf("GetTask() error = %v", err) + } + + t.Logf("Status %s: status=%v", status, fetched.Status) + if fetched.Status == nil || *fetched.Status != status { + t.Errorf("Status = %v, want %v", fetched.Status, status) + } + }) + } + }) + + t.Run("update_clears_eisenhower", func(t *testing.T) { + name := testName("clear-eisenhower") + task, err := integrationClient.NewTask(name). + InArea(testAreaID). + WithEisenhower(lunatask.EisenhowerDoNow). + Create(ctx()) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if task == nil { + t.Fatal("Create() returned nil (duplicate?)") + } + + t.Cleanup(func() { + if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil { + t.Errorf("DeleteTask() cleanup error = %v", err) + } + }) + + updated, err := integrationClient.NewTaskUpdate(task.ID). + WithEisenhower(lunatask.EisenhowerUncategorized). + Update(ctx()) + if err != nil { + t.Fatalf("Update() error = %v", err) + } + + t.Logf("After clear: eisenhower=%v", updated.Eisenhower) + if updated.Eisenhower == nil || *updated.Eisenhower != lunatask.EisenhowerUncategorized { + t.Errorf("Eisenhower = %v, want %v (uncategorized)", updated.Eisenhower, lunatask.EisenhowerUncategorized) + } + }) + + t.Run("update_preserves_unmodified_fields", func(t *testing.T) { + name := testName("preserve-fields") + task, err := integrationClient.NewTask(name). + InArea(testAreaID). + WithStatus(lunatask.StatusNext). + WithEisenhower(lunatask.EisenhowerDoLater). + Priority(lunatask.PriorityHigh). + Create(ctx()) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if task == nil { + t.Fatal("Create() returned nil (duplicate?)") + } + + t.Cleanup(func() { + if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil { + t.Errorf("DeleteTask() cleanup error = %v", err) + } + }) + + updated, err := integrationClient.NewTaskUpdate(task.ID). + WithMotivation(lunatask.MotivationShould). + Update(ctx()) + if err != nil { + t.Fatalf("Update() error = %v", err) + } + + logMetadata(t, "after_update", updated) + + if updated.Status == nil || *updated.Status != lunatask.StatusNext { + t.Errorf("Status changed: %v, want %v", updated.Status, lunatask.StatusNext) + } + if updated.Eisenhower == nil || *updated.Eisenhower != lunatask.EisenhowerDoLater { + t.Errorf("Eisenhower changed: %v, want %v", updated.Eisenhower, lunatask.EisenhowerDoLater) + } + if updated.Priority == nil || *updated.Priority != lunatask.PriorityHigh { + t.Errorf("Priority changed: %v, want %v", updated.Priority, lunatask.PriorityHigh) + } + if updated.Motivation == nil || *updated.Motivation != lunatask.MotivationShould { + t.Errorf("Motivation = %v, want %v", updated.Motivation, lunatask.MotivationShould) + } + }) + + t.Run("cross_workflow_update", func(t *testing.T) { + name := testName("cross-workflow") + task, err := integrationClient.NewTask(name). + InArea(testAreaID). + WithEisenhower(lunatask.EisenhowerDoNow). + Create(ctx()) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if task == nil { + t.Fatal("Create() returned nil (duplicate?)") + } + + t.Cleanup(func() { + if _, err := integrationClient.DeleteTask(ctx(), task.ID); err != nil { + t.Errorf("DeleteTask() cleanup error = %v", err) + } + }) + + logMetadata(t, "after_create", task) + + updated, err := integrationClient.NewTaskUpdate(task.ID). + WithStatus(lunatask.StatusWaiting). + WithMotivation(lunatask.MotivationMust). + Update(ctx()) + if err != nil { + t.Fatalf("Update() error = %v", err) + } + + logMetadata(t, "after_update", updated) + + if updated.Eisenhower == nil || *updated.Eisenhower != lunatask.EisenhowerDoNow { + t.Errorf("Eisenhower lost after update: %v, want %v", updated.Eisenhower, lunatask.EisenhowerDoNow) + } + if updated.Status == nil || *updated.Status != lunatask.StatusWaiting { + t.Errorf("Status = %v, want %v", updated.Status, lunatask.StatusWaiting) + } + if updated.Motivation == nil || *updated.Motivation != lunatask.MotivationMust { + t.Errorf("Motivation = %v, want %v", updated.Motivation, lunatask.MotivationMust) + } + + fetched, err := integrationClient.GetTask(ctx(), task.ID) + if err != nil { + t.Fatalf("GetTask() error = %v", err) + } + + logMetadata(t, "after_fetch", fetched) + + if fetched.Eisenhower == nil || *fetched.Eisenhower != lunatask.EisenhowerDoNow { + t.Errorf("Eisenhower lost after fetch: %v, want %v", fetched.Eisenhower, lunatask.EisenhowerDoNow) + } + if fetched.Status == nil || *fetched.Status != lunatask.StatusWaiting { + t.Errorf("Status after fetch = %v, want %v", fetched.Status, lunatask.StatusWaiting) + } + if fetched.Motivation == nil || *fetched.Motivation != lunatask.MotivationMust { + t.Errorf("Motivation after fetch = %v, want %v", fetched.Motivation, lunatask.MotivationMust) + } + }) +} + +func logMetadata(t *testing.T, label string, task *lunatask.Task) { + t.Helper() + t.Logf("%s: status=%v eisenhower=%v priority=%v motivation=%v", + label, task.Status, task.Eisenhower, task.Priority, task.Motivation) +}