Files
ai-workflow-skill/packages/inbox-runtime/internal/cli/inbox/cancel_integration_test.go
T

151 lines
4.0 KiB
Go

package inbox
import (
"os"
"path/filepath"
"testing"
)
// TestCancelMarksThreadCancelled verifies cancel marks thread cancelled.
func TestCancelMarksThreadCancelled(t *testing.T) {
t.Parallel()
dbPath := filepath.Join(t.TempDir(), "coord.db")
runInboxCommand(t, "--db", dbPath, "--json", "init")
sendOut := runInboxCommand(
t,
"--db", dbPath,
"--json",
"send",
"--from", "leader",
"--to", "worker-a",
"--subject", "Implement cancellation",
"--summary", "Initial request",
)
var sendResp map[string]any
mustDecodeJSON(t, sendOut, &sendResp)
threadID := nestedString(t, sendResp, "data", "thread", "thread_id")
cancelOut := runInboxCommand(
t,
"--db", dbPath,
"--json",
"cancel",
"--agent", "leader",
"--thread", threadID,
"--reason", "Task superseded by a larger refactor",
)
var cancelResp map[string]any
mustDecodeJSON(t, cancelOut, &cancelResp)
if status := nestedString(t, cancelResp, "data", "thread", "status"); status != "cancelled" {
t.Fatalf("expected cancelled thread, got %q", status)
}
if kind := nestedString(t, cancelResp, "data", "message", "kind"); kind != "control" {
t.Fatalf("expected control message, got %q", kind)
}
}
// TestCancelPersistsReasonAndArtifact verifies cancel persists reason and artifact.
func TestCancelPersistsReasonAndArtifact(t *testing.T) {
t.Parallel()
tempDir := t.TempDir()
dbPath := filepath.Join(tempDir, "coord.db")
cancelPath := filepath.Join(tempDir, "cancel.md")
if err := os.WriteFile(cancelPath, []byte("Cancelled by product decision"), 0o644); err != nil {
t.Fatalf("write cancel artifact: %v", err)
}
runInboxCommand(t, "--db", dbPath, "--json", "init")
sendOut := runInboxCommand(
t,
"--db", dbPath,
"--json",
"send",
"--from", "leader",
"--to", "worker-a",
"--subject", "Implement cancellation",
"--summary", "Initial request",
)
var sendResp map[string]any
mustDecodeJSON(t, sendOut, &sendResp)
threadID := nestedString(t, sendResp, "data", "thread", "thread_id")
runInboxCommand(
t,
"--db", dbPath,
"--json",
"cancel",
"--agent", "leader",
"--thread", threadID,
"--reason", "Task superseded by a larger refactor",
"--artifact", cancelPath,
"--artifact-kind", "brief",
)
showOut := runInboxCommand(
t,
"--db", dbPath,
"--json",
"show",
"--thread", threadID,
)
var showResp map[string]any
mustDecodeJSON(t, showOut, &showResp)
messages, ok := nestedValue(t, showResp, "data", "messages").([]any)
if !ok || len(messages) == 0 {
t.Fatalf("expected non-empty message history, got %#v", nestedValue(t, showResp, "data", "messages"))
}
lastMessage, ok := messages[len(messages)-1].(map[string]any)
if !ok {
t.Fatalf("expected message object, got %#v", messages[len(messages)-1])
}
if got := lastMessage["summary"]; got != "Task superseded by a larger refactor" {
t.Fatalf("expected cancel summary, got %#v", got)
}
if got := lastMessage["body"]; got != "Task superseded by a larger refactor" {
t.Fatalf("expected cancel body, got %#v", got)
}
artifacts, ok := lastMessage["artifacts"].([]any)
if !ok || len(artifacts) != 1 {
t.Fatalf("expected one cancel artifact, got %#v", lastMessage["artifacts"])
}
artifact, ok := artifacts[0].(map[string]any)
if !ok {
t.Fatalf("expected artifact object, got %#v", artifacts[0])
}
if got := artifact["path"]; got != cancelPath {
t.Fatalf("expected artifact path %q, got %#v", cancelPath, got)
}
if got := artifact["kind"]; got != "brief" {
t.Fatalf("expected artifact kind brief, got %#v", got)
}
}
// TestCancelRejectsWhenThreadMissing verifies cancel rejects when thread missing.
func TestCancelRejectsWhenThreadMissing(t *testing.T) {
t.Parallel()
dbPath := filepath.Join(t.TempDir(), "coord.db")
runInboxCommand(t, "--db", dbPath, "--json", "init")
stdout, _, exitCode := executeInboxCommand(
"--db", dbPath,
"--agent", "leader",
"--json",
"cancel",
"--thread", "thr_missing",
)
if exitCode != 40 {
t.Fatalf("expected not-found exit code 40, got %d with %s", exitCode, stdout)
}
assertErrorJSON(t, stdout, "not_found")
}