package cc_test import ( "context" "fmt" "strings" "testing" "superwork-tui/internal/cc" ) func TestRunReview_CallsOnThreadID(t *testing.T) { ndjson := strings.Join([]string{ `{"type":"thread.started","thread_id":"tid-abc-123"}`, `{"type":"message","content":"hello"}`, }, "\n") + "\n" fakeRunner := func(_ context.Context, _ string, _ []string) ([]byte, []byte, error) { return []byte(ndjson), nil, nil } var gotID string opts := cc.ReviewOpts{ WorkspaceRoot: "/tmp", Prompt: "review this", OnThreadID: func(id string) { gotID = id }, } oldRunner := cc.RunCodex cc.RunCodex = fakeRunner defer func() { cc.RunCodex = oldRunner }() if err := cc.RunReview(context.Background(), opts); err != nil { t.Fatalf("unexpected error: %v", err) } if gotID != "tid-abc-123" { t.Errorf("want tid-abc-123, got %q", gotID) } } func TestRunReview_NoThreadStarted_NotCalled(t *testing.T) { ndjson := `{"type":"message","content":"hello"}` + "\n" fakeRunner := func(_ context.Context, _ string, _ []string) ([]byte, []byte, error) { return []byte(ndjson), nil, nil } called := false opts := cc.ReviewOpts{ WorkspaceRoot: "/tmp", Prompt: "review this", OnThreadID: func(id string) { called = true }, } oldRunner := cc.RunCodex cc.RunCodex = fakeRunner defer func() { cc.RunCodex = oldRunner }() if err := cc.RunReview(context.Background(), opts); err != nil { t.Fatalf("unexpected error: %v", err) } if called { t.Error("OnThreadID should not be called when no thread.started event") } } func TestRunReview_SessionIDFallback(t *testing.T) { // older codex uses session_id ndjson := `{"type":"session.created","session_id":"sess-xyz"}` + "\n" fakeRunner := func(_ context.Context, _ string, _ []string) ([]byte, []byte, error) { return []byte(ndjson), nil, nil } var gotID string opts := cc.ReviewOpts{ WorkspaceRoot: "/tmp", Prompt: "review", OnThreadID: func(id string) { gotID = id }, } oldRunner := cc.RunCodex cc.RunCodex = fakeRunner defer func() { cc.RunCodex = oldRunner }() cc.RunReview(context.Background(), opts) //nolint if gotID != "sess-xyz" { t.Errorf("want sess-xyz, got %q", gotID) } } func TestRunReview_RunnerError_NoError(t *testing.T) { // fire-and-forget: runner error should NOT propagate fakeRunner := func(_ context.Context, _ string, _ []string) ([]byte, []byte, error) { return nil, []byte("exec error"), fmt.Errorf("exec failed") } opts := cc.ReviewOpts{WorkspaceRoot: "/tmp", Prompt: "x"} oldRunner := cc.RunCodex cc.RunCodex = fakeRunner defer func() { cc.RunCodex = oldRunner }() // Should not panic or return error — fire-and-forget cc.RunReview(context.Background(), opts) }