package service import ( "context" "errors" "sync" "testing" "time" ) type channelFakeExecutor struct { fn func(context.Context, SingleRequestRequest, SingleRequestController) error } func (f *channelFakeExecutor) ExecuteSingleRequest(ctx context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { if f.fn != nil { return f.fn(ctx, req, ctrl) } return nil } func createTestBinding(t *testing.T) *SingleRequestBinding { t.Helper() binding, err := NewSingleRequestBinding( "test-model", "workspace-ref-123", SingleRequestStageBinding{Model: "gemini-3.6-flash", Options: map[string]any{"reasoning_effort": "high"}}, SingleRequestStageBinding{Model: "ornith-fast"}, SingleRequestStageBinding{Model: "gemini-3.6-flash", Options: map[string]any{"reasoning_effort": "high"}}, SingleRequestLimits{WallClockMS: 60000, StageTimeoutMS: 10000, MaxToolIterations: 10, MaxOutputBytes: 1048576}, ) if err != nil { t.Fatalf("NewSingleRequestBinding: %v", err) } return binding } func testEnvelope(requestID string, sequence uint64, stage SingleRequestState) SingleRequestEnvelope { return SingleRequestEnvelope{RequestID: requestID, Sequence: sequence, Stage: stage} } func submitToFinalizing(req SingleRequestRequest, ctrl SingleRequestController, result *SingleRequestResult) error { for sequence, stage := range []SingleRequestState{ SingleRequestStatePlanning, SingleRequestStateWorking, SingleRequestStateReviewing, SingleRequestStateFinalizing, } { env := testEnvelope(req.RequestID, uint64(sequence+1), stage) if stage == SingleRequestStateFinalizing { env.Result = result } if err := ctrl.SubmitEnvelope(env); err != nil { return err } } return nil } func waitForState(t *testing.T, handle SingleRequestExecution, want SingleRequestState) { t.Helper() deadline := time.After(2 * time.Second) ticker := time.NewTicker(time.Millisecond) defer ticker.Stop() for { if handle.State() == want { return } select { case <-deadline: t.Fatalf("state=%s, want %s", handle.State(), want) case <-ticker.C: } } } func waitForExecution(t *testing.T, handle SingleRequestExecution) (SingleRequestResult, error) { t.Helper() type outcome struct { result SingleRequestResult err error } done := make(chan outcome, 1) go func() { result, err := handle.Wait() done <- outcome{result: result, err: err} }() select { case outcome := <-done: return outcome.result, outcome.err case <-time.After(2 * time.Second): t.Fatal("Wait did not return") return SingleRequestResult{}, nil } } func startTestExecution(t *testing.T, executor SingleRequestExecutor) SingleRequestExecution { t.Helper() handle, err := startSingleRequest(context.Background(), executor, SingleRequestRequest{ RequestID: "request-test", Binding: createTestBinding(t), Prompt: "complete the private task", }) if err != nil { t.Fatalf("StartSingleRequest: %v", err) } return handle } func TestSingleRequestExecutorUnavailable(t *testing.T) { _, err := (&Service{}).StartSingleRequest(context.Background(), SingleRequestRequest{ RequestID: "request-unavailable", Binding: createTestBinding(t), }) if !errors.Is(err, ErrSingleRequestExecutorUnavailable) { t.Fatalf("error=%v, want ErrSingleRequestExecutorUnavailable", err) } } func TestSingleRequestExecutorCannotMutateAdmission(t *testing.T) { result := &SingleRequestResult{Output: "accepted result"} executor := &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { req.Binding.Plan.Options["reasoning_effort"] = "low" ctrl.Binding().Review.Options["reasoning_effort"] = "low" if err := submitToFinalizing(req, ctrl, result); err != nil { return err } result.Output = "executor-mutated result" return nil }} callerBinding := createTestBinding(t) handle, err := startSingleRequest(context.Background(), executor, SingleRequestRequest{ RequestID: "request-test", Binding: callerBinding, Prompt: "complete the private task", }) if err != nil { t.Fatalf("StartSingleRequest: %v", err) } callerBinding.Plan.Options["reasoning_effort"] = "caller-mutated" waitForState(t, handle, SingleRequestStateFinalizing) if got := handle.Binding().Plan.Options["reasoning_effort"]; got != "high" { t.Fatalf("executor mutated retained binding: %v", got) } if err := handle.AcknowledgeTerminal(true); err != nil { t.Fatalf("AcknowledgeTerminal: %v", err) } got, err := waitForExecution(t, handle) if err != nil || got.Output != "accepted result" { t.Fatalf("Wait=(%q, %v), want accepted immutable result", got.Output, err) } } func TestSingleRequestRejectsExecutorCompletedEnvelope(t *testing.T) { executor := &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { if err := submitToFinalizing(req, ctrl, &SingleRequestResult{Output: "candidate"}); err != nil { return err } return ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 5, SingleRequestStateCompleted)) }} handle := startTestExecution(t, executor) _, err := waitForExecution(t, handle) if !errors.Is(err, ErrSingleRequestInvalidState) { t.Fatalf("Wait error=%v, want invalid state", err) } if got := handle.State(); got != SingleRequestStateFailed { t.Fatalf("state=%s, want failed", got) } } func TestSingleRequestExecutorExitFailsClosed(t *testing.T) { for name, executor := range map[string]SingleRequestExecutor{ "no envelopes": &channelFakeExecutor{}, "mid-stage": &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { return ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)) }}, } { t.Run(name, func(t *testing.T) { handle := startTestExecution(t, executor) _, err := waitForExecution(t, handle) if !errors.Is(err, ErrSingleRequestFailed) { t.Fatalf("Wait error=%v, want ErrSingleRequestFailed", err) } if got := handle.State(); got != SingleRequestStateFailed { t.Fatalf("state=%s, want failed", got) } }) } } func TestSingleRequestEnvelopeOrderingFailsClosed(t *testing.T) { tests := map[string]func(SingleRequestRequest, SingleRequestController) error{ "missing sequence": func(req SingleRequestRequest, ctrl SingleRequestController) error { return ctrl.SubmitEnvelope(SingleRequestEnvelope{RequestID: req.RequestID, Stage: SingleRequestStatePlanning}) }, "duplicate sequence": func(req SingleRequestRequest, ctrl SingleRequestController) error { if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil { return err } return ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStateWorking)) }, "reordered sequence": func(req SingleRequestRequest, ctrl SingleRequestController) error { if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 2, SingleRequestStatePlanning)); err != nil { return err } return ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStateWorking)) }, "mismatched saved stage": func(req SingleRequestRequest, ctrl SingleRequestController) error { if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil { return err } tool := testEnvelope(req.RequestID, 2, SingleRequestStateInternalTool) tool.SavedStage = SingleRequestStateWorking return ctrl.SubmitEnvelope(tool) }, "duplicate internal tool": func(req SingleRequestRequest, ctrl SingleRequestController) error { if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil { return err } tool := testEnvelope(req.RequestID, 2, SingleRequestStateInternalTool) tool.SavedStage = SingleRequestStatePlanning if err := ctrl.SubmitEnvelope(tool); err != nil { return err } tool.Sequence = 3 return ctrl.SubmitEnvelope(tool) }, } for name, submit := range tests { t.Run(name, func(t *testing.T) { handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { return submit(req, ctrl) }}) _, err := waitForExecution(t, handle) if !errors.Is(err, ErrSingleRequestInvalidSequence) && !errors.Is(err, ErrSingleRequestInvalidState) { t.Fatalf("Wait error=%v, want sequence or state failure", err) } }) } } func TestSingleRequestFinalCandidateRequired(t *testing.T) { tests := map[string]func(SingleRequestRequest, SingleRequestController) error{ "nil finalizing candidate": func(req SingleRequestRequest, ctrl SingleRequestController) error { for sequence, stage := range []SingleRequestState{ SingleRequestStatePlanning, SingleRequestStateWorking, SingleRequestStateReviewing, SingleRequestStateFinalizing, } { if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, uint64(sequence+1), stage)); err != nil { return err } } return nil }, "stale earlier-stage candidate": func(req SingleRequestRequest, ctrl SingleRequestController) error { env := testEnvelope(req.RequestID, 1, SingleRequestStatePlanning) env.Result = &SingleRequestResult{Output: "stale candidate"} return ctrl.SubmitEnvelope(env) }, } for name, submit := range tests { t.Run(name, func(t *testing.T) { handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { return submit(req, ctrl) }}) _, err := waitForExecution(t, handle) if !errors.Is(err, ErrSingleRequestInvalidState) { t.Fatalf("Wait error=%v, want ErrSingleRequestInvalidState", err) } if got := handle.State(); got != SingleRequestStateFailed { t.Fatalf("state=%s, want failed", got) } }) } } func TestSingleRequestAcknowledgementRequiresFinalCandidate(t *testing.T) { executor := &channelFakeExecutor{fn: func(ctx context.Context, _ SingleRequestRequest, _ SingleRequestController) error { <-ctx.Done() return ctx.Err() }} handle := startTestExecution(t, executor) internal := handle.(*singleRequestHandle) internal.mu.Lock() internal.state = SingleRequestStateFinalizing internal.mu.Unlock() err := handle.AcknowledgeTerminal(true) if !errors.Is(err, ErrSingleRequestInvalidState) { t.Fatalf("AcknowledgeTerminal error=%v, want ErrSingleRequestInvalidState", err) } if got := handle.State(); got != SingleRequestStateFailed { t.Fatalf("state=%s, want failed", got) } if _, err := waitForExecution(t, handle); !errors.Is(err, ErrSingleRequestInvalidState) { t.Fatalf("Wait error=%v, want ErrSingleRequestInvalidState", err) } } func TestSingleRequestProgressRedactionAndFinalCandidateDelivery(t *testing.T) { ready := make(chan struct{}) release := make(chan struct{}) executor := &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { planning := testEnvelope(req.RequestID, 1, SingleRequestStatePlanning) planning.Message = "raw executor secret" if err := ctrl.SubmitEnvelope(planning); err != nil { return err } close(ready) <-release if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 2, SingleRequestStateWorking)); err != nil { return err } if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 3, SingleRequestStateReviewing)); err != nil { return err } final := testEnvelope(req.RequestID, 4, SingleRequestStateFinalizing) final.Message = "raw executor secret" final.Result = &SingleRequestResult{Output: "raw executor result"} return ctrl.SubmitEnvelope(final) }} handle := startTestExecution(t, executor) <-ready // Saturate the ordinary lane before the final candidate is emitted. internal := handle.(*singleRequestHandle) internal.mu.Lock() for range 100 { internal.notifyProgressLocked(SingleRequestProgress{RequestID: internal.req.RequestID, Message: "filler"}, false) } internal.mu.Unlock() close(release) waitForState(t, handle, SingleRequestStateFinalizing) seenFinalizingCandidate := false for len(internal.progressCh) > 0 { progress := <-internal.progressCh if progress.Message == "raw executor secret" || progress.Err != nil { t.Fatalf("progress leaked executor-controlled data: %#v", progress) } if progress.Result == nil { if progress.Stage == SingleRequestStateFinalizing { t.Fatal("finalizing progress did not include a final candidate") } continue } if progress.Stage != SingleRequestStateFinalizing { t.Fatalf("non-finalizing progress exposed a result: %#v", progress) } if progress.Result.Output != "raw executor result" { t.Fatalf("finalizing result=%q, want final candidate", progress.Result.Output) } progress.Result.Output = "surface-mutated result" seenFinalizingCandidate = true } if !seenFinalizingCandidate { t.Fatal("finalizing candidate was dropped after ordinary progress saturation") } if err := handle.AcknowledgeTerminal(true); err != nil { t.Fatalf("AcknowledgeTerminal: %v", err) } result, err := waitForExecution(t, handle) if err != nil { t.Fatalf("Wait: %v", err) } if result.Output != "raw executor result" { t.Fatalf("Wait result=%q, want immutable final candidate", result.Output) } } func TestSingleRequestTerminalRaces(t *testing.T) { for i := 0; i < 20; i++ { release := make(chan struct{}) executor := &channelFakeExecutor{fn: func(ctx context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { if err := submitToFinalizing(req, ctrl, &SingleRequestResult{Output: "candidate"}); err != nil { return err } select { case <-release: return nil case <-ctx.Done(): return ctx.Err() } }} handle := startTestExecution(t, executor) waitForState(t, handle, SingleRequestStateFinalizing) var wg sync.WaitGroup wg.Add(4) go func() { defer wg.Done(); _ = handle.AcknowledgeTerminal(true) }() go func() { defer wg.Done(); _ = handle.AcknowledgeTerminal(false) }() go func() { defer wg.Done(); handle.Cancel() }() go func() { defer wg.Done() _ = handle.SubmitEnvelope(SingleRequestEnvelope{RequestID: "request-test", Sequence: 5, Stage: SingleRequestStateFailed, Err: errors.New("executor failure")}) }() wg.Wait() close(release) if _, err := waitForExecution(t, handle); err == nil && handle.State() != SingleRequestStateCompleted { t.Fatalf("non-completed terminal state must retain an error") } terminalCount := 0 for progress := range handle.Progress() { if progress.Terminal != nil { terminalCount++ } } if terminalCount != 1 { t.Fatalf("terminal disposition progress count=%d, want exactly one", terminalCount) } } } func TestSingleRequestRequestWallClockBudgetDisposition(t *testing.T) { for iteration := 0; iteration < 40; iteration++ { binding := createTestBinding(t) binding.Limits.WallClockMS = 10 binding.Limits.StageTimeoutMS = 10 executorReturned := make(chan struct{}) executor := &channelFakeExecutor{fn: func(ctx context.Context, _ SingleRequestRequest, _ SingleRequestController) error { defer close(executorReturned) <-ctx.Done() return ctx.Err() }} handle, err := startSingleRequest(context.Background(), executor, SingleRequestRequest{ RequestID: "request-budget", Binding: binding, Prompt: "exercise the immutable wall-clock budget", }) if err != nil { t.Fatalf("iteration=%d StartSingleRequest: %v", iteration, err) } terminalCount := 0 var terminal SingleRequestTerminalDisposition for progress := range handle.Progress() { if progress.Terminal != nil { terminalCount++ terminal = *progress.Terminal } } result, waitErr := waitForExecution(t, handle) want := SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorBudget} if !errors.Is(waitErr, ErrSingleRequestInternalToolBudget) || result.Output != "" || terminal != want || terminalCount != 1 { t.Fatalf("iteration=%d Wait=(%+v, %v) terminal=%+v count=%d, want budget/1", iteration, result, waitErr, terminal, terminalCount) } if handle.State() != SingleRequestStateFailed { t.Fatalf("iteration=%d state=%s, want failed", iteration, handle.State()) } select { case <-executorReturned: default: t.Fatalf("iteration=%d executor remained active after Wait", iteration) } } } func TestSingleRequestTerminalDispositionValidationAndLegacyNormalization(t *testing.T) { for _, invalid := range []SingleRequestTerminalDisposition{ {}, {Kind: SingleRequestTerminalEndTurn, ErrorClass: SingleRequestTerminalErrorProvider}, {Kind: SingleRequestTerminalError}, {Kind: SingleRequestTerminalError, ErrorClass: "raw-private-value"}, {Kind: "unknown"}, } { if invalid.Validate() == nil { t.Fatalf("invalid disposition accepted: %+v", invalid) } } handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { return submitToFinalizing(req, ctrl, &SingleRequestResult{Output: "legacy output"}) }}) waitForState(t, handle, SingleRequestStateFinalizing) var terminal *SingleRequestTerminalDisposition for terminal == nil { progress := <-handle.Progress() if progress.Stage == SingleRequestStateFinalizing { terminal = progress.Terminal if progress.Result == nil || progress.Result.Terminal.Kind != SingleRequestTerminalEndTurn { t.Fatalf("legacy result was not normalized: %+v", progress) } } } terminal.Kind = SingleRequestTerminalLength if err := handle.AcknowledgeTerminal(true); err != nil { t.Fatalf("AcknowledgeTerminal: %v", err) } result, err := waitForExecution(t, handle) if err != nil || result.Terminal.Kind != SingleRequestTerminalEndTurn { t.Fatalf("Wait=(%+v, %v), want immutable end_turn", result, err) } } func TestSingleRequestTerminalDispositionEarlyLengthOnly(t *testing.T) { t.Run("length from planning", func(t *testing.T) { handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil { return err } return ctrl.SubmitEnvelope(SingleRequestEnvelope{ RequestID: req.RequestID, Sequence: 2, Stage: SingleRequestStateFinalizing, Result: &SingleRequestResult{Terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalLength}}, }) }}) terminalCount := 0 for progress := range handle.Progress() { if progress.Terminal != nil { terminalCount++ if progress.Stage != SingleRequestStateFinalizing || progress.Terminal.Kind != SingleRequestTerminalLength { t.Fatalf("progress=%+v, want planning length final", progress) } if err := handle.AcknowledgeTerminal(true); err != nil { t.Fatalf("AcknowledgeTerminal: %v", err) } } } result, err := waitForExecution(t, handle) if err != nil || result.Terminal.Kind != SingleRequestTerminalLength || terminalCount != 1 { t.Fatalf("Wait=(%+v, %v), terminals=%d", result, err, terminalCount) } }) t.Run("end turn cannot skip work and review", func(t *testing.T) { handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil { return err } return ctrl.SubmitEnvelope(SingleRequestEnvelope{ RequestID: req.RequestID, Sequence: 2, Stage: SingleRequestStateFinalizing, Result: &SingleRequestResult{Output: "invalid early success", Terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalEndTurn}}, }) }}) var terminal *SingleRequestTerminalDisposition for progress := range handle.Progress() { if progress.Terminal != nil { terminal = progress.Terminal } } result, err := waitForExecution(t, handle) want := SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorValidation} if !errors.Is(err, ErrSingleRequestInvalidState) || result.Output != "" || terminal == nil || *terminal != want { t.Fatalf("Wait=(%+v, %v), terminal=%+v, want validation failure", result, err, terminal) } }) } func TestSingleRequestTerminalDispositionFailureAndCancelPropagation(t *testing.T) { tests := []struct { name string stage SingleRequestState terminal SingleRequestTerminalDisposition }{ {name: "provider", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorProvider}}, {name: "timeout", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorTimeout}}, {name: "budget", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorBudget}}, {name: "repetition", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorRepetition}}, {name: "malformed", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorMalformed}}, {name: "context", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorContext}}, {name: "cancel", stage: SingleRequestStateCancelled, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalCancelled}}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil { return err } env := testEnvelope(req.RequestID, 2, test.stage) env.Terminal = &test.terminal if test.stage == SingleRequestStateFailed { env.Err = errors.New("private provider detail") } return ctrl.SubmitEnvelope(env) }}) _, _ = waitForExecution(t, handle) var got *SingleRequestTerminalDisposition for progress := range handle.Progress() { if progress.Stage == test.stage { got = progress.Terminal if progress.Err != nil || progress.Result != nil { t.Fatalf("terminal progress leaked internal data: %+v", progress) } } } if got == nil || *got != test.terminal { t.Fatalf("terminal=%+v, want %+v", got, test.terminal) } }) } } type terminalDispositionCleanupFailure struct{} func (terminalDispositionCleanupFailure) CleanupWorkspace(context.Context, *SingleRequestWorkspaceBinding, string) error { return errors.New("private cleanup detail") } func TestSingleRequestTerminalDispositionCleanupConversionBeforeFreeze(t *testing.T) { handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { internal := ctrl.(*singleRequestHandle) internal.mu.Lock() internal.toolLoop.opened = true internal.toolLoop.lifecycle = terminalDispositionCleanupFailure{} internal.mu.Unlock() return submitToFinalizing(req, ctrl, &SingleRequestResult{ Output: "must not escape", Terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalLength}, }) }}) result, err := waitForExecution(t, handle) if !errors.Is(err, ErrSingleRequestWorkspaceCleanup) || result.Output != "" { t.Fatalf("Wait=(%+v, %v), want cleanup failure without partial result", result, err) } var got *SingleRequestTerminalDisposition for progress := range handle.Progress() { if progress.Stage == SingleRequestStateFailed { got = progress.Terminal } } want := SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorWorkspaceCleanup} if got == nil || *got != want { t.Fatalf("cleanup terminal=%+v, want %+v", got, want) } } func TestSingleRequestTerminalDispositionPostFreezeWinnerStability(t *testing.T) { handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error { return submitToFinalizing(req, ctrl, &SingleRequestResult{Output: "candidate", Terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalEndTurn}}) }}) waitForState(t, handle, SingleRequestStateFinalizing) for { progress := <-handle.Progress() if progress.Stage != SingleRequestStateFinalizing { continue } if progress.Terminal == nil || progress.Terminal.Kind != SingleRequestTerminalEndTurn { t.Fatalf("finalizing terminal=%+v", progress.Terminal) } progress.Terminal.Kind = SingleRequestTerminalLength break } if err := handle.AcknowledgeTerminal(false); err != nil { t.Fatalf("AcknowledgeTerminal(false): %v", err) } _, err := waitForExecution(t, handle) if !errors.Is(err, ErrSingleRequestFailed) { t.Fatalf("Wait error=%v, want endpoint failure", err) } internal := handle.(*singleRequestHandle) internal.mu.Lock() frozen := cloneSingleRequestTerminal(internal.terminal) internal.mu.Unlock() if frozen == nil || frozen.Kind != SingleRequestTerminalEndTurn { t.Fatalf("frozen terminal changed after acknowledgement failure: %+v", frozen) } for progress := range handle.Progress() { if progress.Terminal != nil { t.Fatalf("post-freeze acknowledgement emitted a second terminal: %+v", progress) } } }