192 lines
5.0 KiB
Go
192 lines
5.0 KiB
Go
package inbox
|
|
|
|
import "testing"
|
|
|
|
// TestFetchReturnsPendingThreadForTargetAgent verifies fetch returns pending thread for target agent.
|
|
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"])
|
|
}
|
|
}
|
|
|
|
// TestFetchRespectsStatusAndLimitFilters verifies fetch respects status and limit filters.
|
|
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"])
|
|
}
|
|
}
|
|
|
|
// TestFetchUnreadUsesReadCursor verifies fetch unread uses read cursor.
|
|
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"))
|
|
}
|
|
}
|
|
|
|
// TestFetchReturnsNoMatchingWorkWhenEmpty verifies fetch returns no matching work when empty.
|
|
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")
|
|
}
|