package inbox import ( "path/filepath" "testing" "time" ) func TestListFiltersByStatus(t *testing.T) { t.Parallel() dbPath := filepath.Join(t.TempDir(), "coord.db") runInboxCommand(t, "--db", dbPath, "--json", "init") createThreadForList(t, dbPath, "leader", "worker-d", "Task pending", "Pending task") blockedThreadID := createThreadForList(t, dbPath, "leader", "worker-d", "Task blocked", "Blocked task") runInboxCommand(t, "--db", dbPath, "--json", "claim", "--agent", "worker-d", "--thread", blockedThreadID) runInboxCommand( t, "--db", dbPath, "--json", "update", "--agent", "worker-d", "--thread", blockedThreadID, "--status", "blocked", "--summary", "Need policy decision", ) listOut := runInboxCommand( t, "--db", dbPath, "--json", "list", "--agent", "worker-d", "--status", "pending,blocked", ) var listResp map[string]any mustDecodeJSON(t, listOut, &listResp) threads, ok := nestedValue(t, listResp, "data", "threads").([]any) if !ok || len(threads) < 2 { t.Fatalf("expected at least two matching threads, got %#v", nestedValue(t, listResp, "data", "threads")) } for _, raw := range threads { thread, ok := raw.(map[string]any) if !ok { t.Fatalf("expected thread object, got %#v", raw) } status, _ := thread["status"].(string) if status != "pending" && status != "blocked" { t.Fatalf("expected status pending or blocked, got %#v", status) } } } func TestListFiltersByCreatedBy(t *testing.T) { t.Parallel() dbPath := filepath.Join(t.TempDir(), "coord.db") runInboxCommand(t, "--db", dbPath, "--json", "init") createThreadForList(t, dbPath, "leader", "worker-d", "Leader task", "From leader") createThreadForList(t, dbPath, "planner", "worker-d", "Planner task", "From planner") listOut := runInboxCommand( t, "--db", dbPath, "--json", "list", "--created-by", "leader", "--assigned-to", "worker-d", ) var listResp map[string]any mustDecodeJSON(t, listOut, &listResp) threads, ok := nestedValue(t, listResp, "data", "threads").([]any) if !ok || len(threads) == 0 { t.Fatalf("expected matching leader-created threads, got %#v", nestedValue(t, listResp, "data", "threads")) } for _, raw := range threads { thread, ok := raw.(map[string]any) if !ok { t.Fatalf("expected thread object, got %#v", raw) } if got := thread["created_by"]; got != "leader" { t.Fatalf("expected created_by leader, got %#v", got) } } } func TestListFiltersByAssignedTo(t *testing.T) { t.Parallel() dbPath := filepath.Join(t.TempDir(), "coord.db") runInboxCommand(t, "--db", dbPath, "--json", "init") createThreadForList(t, dbPath, "leader", "worker-d", "Worker D task", "For worker-d") createThreadForList(t, dbPath, "leader", "worker-e", "Worker E task", "For worker-e") listOut := runInboxCommand( t, "--db", dbPath, "--json", "list", "--assigned-to", "worker-d", "--status", "pending", ) var listResp map[string]any mustDecodeJSON(t, listOut, &listResp) threads, ok := nestedValue(t, listResp, "data", "threads").([]any) if !ok || len(threads) == 0 { t.Fatalf("expected assigned-to match, got %#v", nestedValue(t, listResp, "data", "threads")) } for _, raw := range threads { thread, ok := raw.(map[string]any) if !ok { t.Fatalf("expected thread object, got %#v", raw) } if got := thread["assigned_to"]; got != "worker-d" { t.Fatalf("expected assigned_to worker-d, got %#v", got) } if got := thread["status"]; got != "pending" { t.Fatalf("expected pending status, got %#v", got) } } } func TestListRespectsLimit(t *testing.T) { t.Parallel() dbPath := filepath.Join(t.TempDir(), "coord.db") runInboxCommand(t, "--db", dbPath, "--json", "init") createThreadForList(t, dbPath, "leader", "worker-d", "Task 1", "Earlier task") time.Sleep(20 * time.Millisecond) createThreadForList(t, dbPath, "leader", "worker-d", "Task 2", "Latest task") listOut := runInboxCommand( t, "--db", dbPath, "--json", "list", "--assigned-to", "worker-d", "--limit", "1", ) var listResp map[string]any mustDecodeJSON(t, listOut, &listResp) threads, ok := nestedValue(t, listResp, "data", "threads").([]any) if !ok { t.Fatalf("expected threads array, got %#v", nestedValue(t, listResp, "data", "threads")) } if len(threads) != 1 { t.Fatalf("expected exactly one row for limit=1, got %d", len(threads)) } thread, ok := threads[0].(map[string]any) if !ok { t.Fatalf("expected thread object, got %#v", threads[0]) } if got := thread["subject"]; got != "Task 2" { t.Fatalf("expected latest thread subject Task 2, got %#v", got) } } func createThreadForList(t *testing.T, dbPath, from, to, subject, summary string) string { t.Helper() sendOut := runInboxCommand( t, "--db", dbPath, "--json", "send", "--from", from, "--to", to, "--subject", subject, "--summary", summary, ) var sendResp map[string]any mustDecodeJSON(t, sendOut, &sendResp) return nestedString(t, sendResp, "data", "thread", "thread_id") }