test: Fix remaining OpenCode provider test failures

Fix all 8 remaining test failures:

1. Update executeQuery integration tests to use new OpenCode event format:
   - text events use type='text' with part.text
   - tool_call events use type='tool_call' with part containing call_id, name, args
   - tool_result events use type='tool_result' with part
   - step_finish events use type='step_finish' with part
   - Use sessionID field instead of session_id

2. Fix step_finish event handling:
   - Include result field in successful completion response
   - Check for reason === 'error' to detect failed steps
   - Provide default error message when error field is missing

3. Update model test expectations:
   - Model 'opencode/big-pickle' stays as-is (not stripped to 'big-pickle')
   - PROVIDER_PREFIXES only strips 'opencode-' prefix, not 'opencode/'

All 84 tests now pass successfully!
This commit is contained in:
DhanushSantosh
2026-01-11 01:23:42 +05:30
parent 51cd7156d2
commit 7cc092cd59
2 changed files with 60 additions and 33 deletions

View File

@@ -362,21 +362,32 @@ export class OpencodeProvider extends CliProvider {
case 'step_finish': { case 'step_finish': {
const finishEvent = openCodeEvent as OpenCodeFinishStepEvent; const finishEvent = openCodeEvent as OpenCodeFinishStepEvent;
// Check if the step failed // Check if the step failed (either has error field or reason is 'error')
if (finishEvent.part?.error) { if (finishEvent.part?.error || finishEvent.part?.reason === 'error') {
return { return {
type: 'error', type: 'error',
session_id: finishEvent.sessionID, session_id: finishEvent.sessionID,
error: finishEvent.part.error, error: finishEvent.part?.error || 'Step execution failed',
}; };
} }
// Successful completion // Successful completion
return { const result: { type: 'result'; subtype: 'success'; session_id?: string; result?: string } =
type: 'result', {
subtype: 'success', type: 'result',
session_id: finishEvent.sessionID, subtype: 'success',
}; };
if (finishEvent.sessionID) {
result.session_id = finishEvent.sessionID;
}
// Include result text if provided
if (finishEvent.part?.result) {
result.result = finishEvent.part.result;
}
return result;
} }
case 'tool_call': { case 'tool_call': {

View File

@@ -219,14 +219,15 @@ describe('opencode-provider.ts', () => {
expect(args).not.toContain('-c'); expect(args).not.toContain('-c');
}); });
it('should handle missing model', () => { it('should handle model from opencode provider', () => {
const args = provider.buildCliArgs({ const args = provider.buildCliArgs({
prompt: 'Hello', prompt: 'Hello',
cwd: '/tmp/project',
model: 'opencode/big-pickle', model: 'opencode/big-pickle',
cwd: '/tmp/project',
}); });
expect(args).not.toContain('--model'); expect(args).toContain('--model');
expect(args).toContain('opencode/big-pickle');
}); });
}); });
@@ -589,13 +590,12 @@ describe('opencode-provider.ts', () => {
return mockedProvider; return mockedProvider;
} }
it('should stream text-delta events as assistant messages', async () => { it('should stream text events as assistant messages', async () => {
const mockedProvider = setupMockedProvider(); const mockedProvider = setupMockedProvider();
const mockEvents = [ const mockEvents = [
{ type: 'text-delta', text: 'Hello ' }, { type: 'text', part: { type: 'text', text: 'Hello ' } },
{ type: 'text-delta', text: 'World!' }, { type: 'text', part: { type: 'text', text: 'World!' } },
{ type: 'text-end' },
]; ];
vi.mocked(spawnJSONLProcess).mockReturnValue( vi.mocked(spawnJSONLProcess).mockReturnValue(
@@ -614,7 +614,6 @@ describe('opencode-provider.ts', () => {
}) })
); );
// text-end should be filtered out (returns null)
expect(results).toHaveLength(2); expect(results).toHaveLength(2);
expect(results[0].type).toBe('assistant'); expect(results[0].type).toBe('assistant');
expect(results[0].message?.content[0].text).toBe('Hello '); expect(results[0].message?.content[0].text).toBe('Hello ');
@@ -626,15 +625,21 @@ describe('opencode-provider.ts', () => {
const mockEvents = [ const mockEvents = [
{ {
type: 'tool-call', type: 'tool_call',
call_id: 'tool-1', part: {
name: 'Read', type: 'tool-call',
args: { file_path: '/tmp/test.txt' }, call_id: 'tool-1',
name: 'Read',
args: { file_path: '/tmp/test.txt' },
},
}, },
{ {
type: 'tool-result', type: 'tool_result',
call_id: 'tool-1', part: {
output: 'File contents', type: 'tool-result',
call_id: 'tool-1',
output: 'File contents',
},
}, },
]; ];
@@ -721,10 +726,7 @@ describe('opencode-provider.ts', () => {
const call = vi.mocked(spawnJSONLProcess).mock.calls[0][0]; const call = vi.mocked(spawnJSONLProcess).mock.calls[0][0];
expect(call.args).toContain('run'); expect(call.args).toContain('run');
expect(call.args).toContain('--format'); expect(call.args).toContain('--format');
expect(call.args).toContain('stream-json'); expect(call.args).toContain('json');
expect(call.args).toContain('-q');
expect(call.args).toContain('-c');
expect(call.args).toContain('/tmp/workspace');
expect(call.args).toContain('--model'); expect(call.args).toContain('--model');
expect(call.args).toContain('anthropic/claude-opus-4-5'); expect(call.args).toContain('anthropic/claude-opus-4-5');
}); });
@@ -734,9 +736,9 @@ describe('opencode-provider.ts', () => {
const mockEvents = [ const mockEvents = [
{ type: 'unknown-internal-event', data: 'ignored' }, { type: 'unknown-internal-event', data: 'ignored' },
{ type: 'text-delta', text: 'Valid text' }, { type: 'text', part: { type: 'text', text: 'Valid text' } },
{ type: 'another-unknown', foo: 'bar' }, { type: 'another-unknown', foo: 'bar' },
{ type: 'finish-step', result: 'Done' }, { type: 'step_finish', part: { type: 'step-finish', reason: 'stop', result: 'Done' } },
]; ];
vi.mocked(spawnJSONLProcess).mockReturnValue( vi.mocked(spawnJSONLProcess).mockReturnValue(
@@ -750,6 +752,7 @@ describe('opencode-provider.ts', () => {
const results = await collectAsyncGenerator<ProviderMessage>( const results = await collectAsyncGenerator<ProviderMessage>(
mockedProvider.executeQuery({ mockedProvider.executeQuery({
prompt: 'Test', prompt: 'Test',
model: 'opencode/big-pickle',
cwd: '/test', cwd: '/test',
}) })
); );
@@ -1042,10 +1045,22 @@ describe('opencode-provider.ts', () => {
const sessionId = 'test-session-123'; const sessionId = 'test-session-123';
const mockEvents = [ const mockEvents = [
{ type: 'text-delta', text: 'Hello ', session_id: sessionId }, { type: 'text', part: { type: 'text', text: 'Hello ' }, sessionID: sessionId },
{ type: 'tool-call', name: 'Read', args: {}, call_id: 'c1', session_id: sessionId }, {
{ type: 'tool-result', call_id: 'c1', output: 'file content', session_id: sessionId }, type: 'tool_call',
{ type: 'finish-step', result: 'Done', session_id: sessionId }, part: { type: 'tool-call', name: 'Read', args: {}, call_id: 'c1' },
sessionID: sessionId,
},
{
type: 'tool_result',
part: { type: 'tool-result', call_id: 'c1', output: 'file content' },
sessionID: sessionId,
},
{
type: 'step_finish',
part: { type: 'step-finish', reason: 'stop', result: 'Done' },
sessionID: sessionId,
},
]; ];
vi.mocked(spawnJSONLProcess).mockReturnValue( vi.mocked(spawnJSONLProcess).mockReturnValue(
@@ -1059,6 +1074,7 @@ describe('opencode-provider.ts', () => {
const results = await collectAsyncGenerator<ProviderMessage>( const results = await collectAsyncGenerator<ProviderMessage>(
mockedProvider.executeQuery({ mockedProvider.executeQuery({
prompt: 'Test', prompt: 'Test',
model: 'opencode/big-pickle',
cwd: '/tmp', cwd: '/tmp',
}) })
); );