package inbox import "testing" func TestFetchReturnsPendingThreadForTargetAgent(t *testing.T) { t.Parallel() dbPath := initCommandTestDB(t) sendPendingThread(t, dbPath, "leader", "worker-a", "Implement task", "Create API endpoint") fetchOut := runInboxCommand( t, "--db", dbPath, "--json", "fetch", "--agent", "worker-a", "--status", "pending", ) var fetchResp map[string]any mustDecodeJSON(t, fetchOut, &fetchResp) threads, ok := nestedValue(t, fetchResp, "data", "threads").([]any) if !ok || len(threads) < 1 { t.Fatalf("expected at least one fetched thread, got %#v", nestedValue(t, fetchResp, "data", "threads")) } thread, ok := threads[0].(map[string]any) if !ok { t.Fatalf("expected thread object, got %#v", threads[0]) } if got, _ := thread["assigned_to"].(string); got != "worker-a" { t.Fatalf("expected assigned_to worker-a, got %#v", thread["assigned_to"]) } if got, _ := thread["status"].(string); got != "pending" { t.Fatalf("expected pending status, got %#v", thread["status"]) } } func TestFetchRespectsStatusAndLimitFilters(t *testing.T) { t.Parallel() dbPath := initCommandTestDB(t) sendPendingThread(t, dbPath, "leader", "worker-a", "Task A", "Pending task") blockedThreadID := sendPendingThread(t, dbPath, "leader", "worker-a", "Task B", "Blocked task") runInboxCommand( t, "--db", dbPath, "--json", "claim", "--agent", "worker-a", "--thread", blockedThreadID, ) runInboxCommand( t, "--db", dbPath, "--json", "update", "--agent", "worker-a", "--thread", blockedThreadID, "--status", "blocked", "--summary", "Need decision", "--payload-json", `{"question":"continue?"}`, ) fetchOut := runInboxCommand( t, "--db", dbPath, "--json", "fetch", "--agent", "worker-a", "--status", "pending,blocked", "--limit", "1", ) var fetchResp map[string]any mustDecodeJSON(t, fetchOut, &fetchResp) threads, ok := nestedValue(t, fetchResp, "data", "threads").([]any) if !ok { t.Fatalf("expected threads array, got %#v", nestedValue(t, fetchResp, "data", "threads")) } if len(threads) > 1 { t.Fatalf("expected at most one thread with limit=1, got %d", len(threads)) } if len(threads) == 0 { t.Fatalf("expected one thread with status filter, got empty result") } thread, ok := threads[0].(map[string]any) if !ok { t.Fatalf("expected thread object, got %#v", threads[0]) } status, _ := thread["status"].(string) if status != "pending" && status != "blocked" { t.Fatalf("expected pending or blocked status, got %#v", thread["status"]) } } func TestFetchUnreadUsesReadCursor(t *testing.T) { t.Parallel() dbPath := initCommandTestDB(t) threadID := sendPendingThread(t, dbPath, "leader", "worker-e", "Review navbar copy", "Check top nav wording") firstFetchOut := runInboxCommand( t, "--db", dbPath, "--json", "fetch", "--agent", "worker-e", "--status", "pending", "--unread", ) var firstFetchResp map[string]any mustDecodeJSON(t, firstFetchOut, &firstFetchResp) firstThreads, ok := nestedValue(t, firstFetchResp, "data", "threads").([]any) if !ok || len(firstThreads) != 1 { t.Fatalf("expected one unread thread before mark-read, got %#v", nestedValue(t, firstFetchResp, "data", "threads")) } runInboxCommand( t, "--db", dbPath, "--agent", "worker-e", "--json", "show", "--thread", threadID, "--mark-read", ) stdout, _, exitCode := executeInboxCommand( "--db", dbPath, "--json", "fetch", "--agent", "worker-e", "--status", "pending", "--unread", ) if exitCode != 10 { t.Fatalf("expected unread fetch to return no_matching_work after mark-read, got exit=%d stdout=%s", exitCode, stdout) } assertErrorJSON(t, stdout, "no_matching_work") runInboxCommand( t, "--db", dbPath, "--json", "send", "--from", "leader", "--to", "worker-e", "--thread", threadID, "--summary", "Use sentence case", "--body", "Keep the nav labels in sentence case.", ) thirdFetchOut := runInboxCommand( t, "--db", dbPath, "--json", "fetch", "--agent", "worker-e", "--status", "pending", "--unread", ) var thirdFetchResp map[string]any mustDecodeJSON(t, thirdFetchOut, &thirdFetchResp) thirdThreads, ok := nestedValue(t, thirdFetchResp, "data", "threads").([]any) if !ok || len(thirdThreads) != 1 { t.Fatalf("expected unread thread to reappear after new message, got %#v", nestedValue(t, thirdFetchResp, "data", "threads")) } } func TestFetchReturnsNoMatchingWorkWhenEmpty(t *testing.T) { t.Parallel() dbPath := initCommandTestDB(t) stdout, _, exitCode := executeInboxCommand( "--db", dbPath, "--json", "fetch", "--agent", "worker-z", "--status", "pending", ) if exitCode != 10 { t.Fatalf("expected exit code 10, got %d", exitCode) } assertErrorJSON(t, stdout, "no_matching_work") }