import { describe, expect, test } from 'vitest'; import { resolveTurnOutcome, shouldAppendCompletedContent, } from '../../src/runtime/turn-outcome'; describe('shouldAppendCompletedContent', () => { test('does not re-append when deltas already streamed the answer', () => { // Claude/OpenCode emit the final answer as assistant_delta AND as the // content-bearing assistant_completed event. Folding the completed // content in again would produce "". expect(shouldAppendCompletedContent('the answer', 'the answer')).toBe( false, ); }); test('appends completed content when nothing has streamed yet', () => { expect(shouldAppendCompletedContent('', 'the final result')).toBe(true); }); test('treats whitespace-only streamed text as empty', () => { expect(shouldAppendCompletedContent(' \n', 'the final result')).toBe( true, ); }); test('does not append when the completed event carries no content', () => { expect(shouldAppendCompletedContent('the answer', undefined)).toBe(false); expect(shouldAppendCompletedContent('', undefined)).toBe(false); expect(shouldAppendCompletedContent('', '')).toBe(false); }); }); describe('resolveTurnOutcome', () => { test('partial streamed text + error is surfaced as failed and text preserved', () => { const outcome = resolveTurnOutcome({ assistantText: 'partial answer before crash', error: 'exit code 1: rate limited', runtime: 'codex', }); expect(outcome.text).toBe('partial answer before crash'); expect(outcome.failure).toBe('codex failed:\nexit code 1: rate limited'); }); test('successful turn with streamed text and no error has no failure', () => { const outcome = resolveTurnOutcome({ assistantText: 'complete answer', runtime: 'codex', }); expect(outcome.text).toBe('complete answer'); expect(outcome.failure).toBeUndefined(); }); test('empty turn with error is surfaced as failed', () => { const outcome = resolveTurnOutcome({ assistantText: ' ', error: 'exit code 137', runtime: 'claude', }); expect(outcome.failure).toBe('claude failed:\nexit code 137'); }); test('empty turn with no error reports the no-response failure', () => { const outcome = resolveTurnOutcome({ assistantText: '', runtime: 'codex', }); expect(outcome.failure).toBe( 'Codex completed without producing an assistant response.', ); }); test('recovers finalMessage text when nothing streamed and no error', () => { const outcome = resolveTurnOutcome({ assistantText: '', recoveredText: 'recovered from last-message file', runtime: 'codex', }); expect(outcome.text).toBe('recovered from last-message file'); expect(outcome.failure).toBeUndefined(); }); test('recovered finalMessage text + error is still surfaced as failed', () => { const outcome = resolveTurnOutcome({ assistantText: '', recoveredText: 'recovered partial', error: 'network drop', runtime: 'codex', }); expect(outcome.text).toBe('recovered partial'); expect(outcome.failure).toBe('codex failed:\nnetwork drop'); }); });