test: Update OpenCode provider tests to use new event format

Update normalizeEvent tests to match new OpenCode API:
- text events use type='text' with part.text instead of text-delta
- tool_call events use type='tool_call' with part containing call_id, name, args
- tool_result events use type='tool_result' with part
- tool_error events use type='tool_error' with part
- step_finish events use type='step_finish' with part

Update buildCliArgs tests:
- Remove expectations for -q flag (no longer used)
- Remove expectations for -c flag (cwd set at subprocess level)
- Remove expectations for - final arg (prompt via stdin)
- Update format to 'json' instead of 'stream-json'

Remaining 8 test failures are in integration tests that use executeQuery
and require more extensive mock data updates.
This commit is contained in:
DhanushSantosh
2026-01-11 01:07:55 +05:30
parent 1dc843d2d0
commit 51cd7156d2

View File

@@ -168,41 +168,23 @@ describe('opencode-provider.ts', () => {
it('should build correct args with run subcommand', () => {
const args = provider.buildCliArgs({
prompt: 'Hello',
model: 'opencode/big-pickle',
cwd: '/tmp/project',
});
expect(args[0]).toBe('run');
});
it('should include --format stream-json for streaming output', () => {
it('should include --format json for streaming output', () => {
const args = provider.buildCliArgs({
prompt: 'Hello',
model: 'opencode/big-pickle',
cwd: '/tmp/project',
});
const formatIndex = args.indexOf('--format');
expect(formatIndex).toBeGreaterThan(-1);
expect(args[formatIndex + 1]).toBe('stream-json');
});
it('should include -q flag for quiet mode', () => {
const args = provider.buildCliArgs({
prompt: 'Hello',
cwd: '/tmp/project',
});
expect(args).toContain('-q');
});
it('should include working directory with -c flag', () => {
const args = provider.buildCliArgs({
prompt: 'Hello',
cwd: '/tmp/my-project',
});
const cwdIndex = args.indexOf('-c');
expect(cwdIndex).toBeGreaterThan(-1);
expect(args[cwdIndex + 1]).toBe('/tmp/my-project');
expect(args[formatIndex + 1]).toBe('json');
});
it('should include model with --model flag', () => {
@@ -228,18 +210,10 @@ describe('opencode-provider.ts', () => {
expect(args[modelIndex + 1]).toBe('anthropic/claude-sonnet-4-5');
});
it('should include dash as final arg for stdin prompt', () => {
const args = provider.buildCliArgs({
prompt: 'Hello',
cwd: '/tmp/project',
});
expect(args[args.length - 1]).toBe('-');
});
it('should handle missing cwd', () => {
const args = provider.buildCliArgs({
prompt: 'Hello',
model: 'opencode/big-pickle',
});
expect(args).not.toContain('-c');
@@ -249,6 +223,7 @@ describe('opencode-provider.ts', () => {
const args = provider.buildCliArgs({
prompt: 'Hello',
cwd: '/tmp/project',
model: 'opencode/big-pickle',
});
expect(args).not.toContain('--model');
@@ -260,12 +235,15 @@ describe('opencode-provider.ts', () => {
// ==========================================================================
describe('normalizeEvent', () => {
describe('text-delta events', () => {
it('should convert text-delta to assistant message with text content', () => {
describe('text events (new OpenCode format)', () => {
it('should convert text to assistant message with text content', () => {
const event = {
type: 'text-delta',
text: 'Hello, world!',
session_id: 'test-session',
type: 'text',
part: {
type: 'text',
text: 'Hello, world!',
},
sessionID: 'test-session',
};
const result = provider.normalizeEvent(event);
@@ -285,10 +263,13 @@ describe('opencode-provider.ts', () => {
});
});
it('should return null for empty text-delta', () => {
it('should return null for empty text', () => {
const event = {
type: 'text-delta',
text: '',
type: 'text',
part: {
type: 'text',
text: '',
},
};
const result = provider.normalizeEvent(event);
@@ -296,9 +277,10 @@ describe('opencode-provider.ts', () => {
expect(result).toBeNull();
});
it('should return null for text-delta with undefined text', () => {
it('should return null for text with undefined text', () => {
const event = {
type: 'text-delta',
type: 'text',
part: {},
};
const result = provider.normalizeEvent(event);
@@ -307,27 +289,17 @@ describe('opencode-provider.ts', () => {
});
});
describe('text-end events', () => {
it('should return null for text-end events (informational)', () => {
describe('tool_call events', () => {
it('should convert tool_call to assistant message with tool_use content', () => {
const event = {
type: 'text-end',
session_id: 'test-session',
};
const result = provider.normalizeEvent(event);
expect(result).toBeNull();
});
});
describe('tool-call events', () => {
it('should convert tool-call to assistant message with tool_use content', () => {
const event = {
type: 'tool-call',
call_id: 'call-123',
name: 'Read',
args: { file_path: '/tmp/test.txt' },
session_id: 'test-session',
type: 'tool_call',
part: {
type: 'tool-call',
call_id: 'call-123',
name: 'Read',
args: { file_path: '/tmp/test.txt' },
},
sessionID: 'test-session',
};
const result = provider.normalizeEvent(event);
@@ -351,9 +323,12 @@ describe('opencode-provider.ts', () => {
it('should generate tool_use_id when call_id is missing', () => {
const event = {
type: 'tool-call',
name: 'Write',
args: { content: 'test' },
type: 'tool_call',
part: {
type: 'tool-call',
name: 'Write',
args: { content: 'test' },
},
};
const result = provider.normalizeEvent(event);
@@ -363,21 +338,27 @@ describe('opencode-provider.ts', () => {
// Second call should increment
const result2 = provider.normalizeEvent({
type: 'tool-call',
name: 'Edit',
args: {},
type: 'tool_call',
part: {
type: 'tool-call',
name: 'Edit',
args: {},
},
});
expect(result2?.message?.content[0].tool_use_id).toBe('opencode-tool-2');
});
});
describe('tool-result events', () => {
it('should convert tool-result to assistant message with tool_result content', () => {
describe('tool_result events', () => {
it('should convert tool_result to assistant message with tool_result content', () => {
const event = {
type: 'tool-result',
call_id: 'call-123',
output: 'File contents here',
session_id: 'test-session',
type: 'tool_result',
part: {
type: 'tool-result',
call_id: 'call-123',
output: 'File contents here',
},
sessionID: 'test-session',
};
const result = provider.normalizeEvent(event);
@@ -398,10 +379,13 @@ describe('opencode-provider.ts', () => {
});
});
it('should handle tool-result without call_id', () => {
it('should handle tool_result without call_id', () => {
const event = {
type: 'tool-result',
output: 'Result without ID',
type: 'tool_result',
part: {
type: 'tool-result',
output: 'Result without ID',
},
};
const result = provider.normalizeEvent(event);
@@ -411,13 +395,16 @@ describe('opencode-provider.ts', () => {
});
});
describe('tool-error events', () => {
it('should convert tool-error to error message', () => {
describe('tool_error events', () => {
it('should convert tool_error to error message', () => {
const event = {
type: 'tool-error',
call_id: 'call-123',
error: 'File not found',
session_id: 'test-session',
type: 'tool_error',
part: {
type: 'tool-error',
call_id: 'call-123',
error: 'File not found',
},
sessionID: 'test-session',
};
const result = provider.normalizeEvent(event);
@@ -431,8 +418,11 @@ describe('opencode-provider.ts', () => {
it('should provide default error message when error is missing', () => {
const event = {
type: 'tool-error',
call_id: 'call-123',
type: 'tool_error',
part: {
type: 'tool-error',
call_id: 'call-123',
},
};
const result = provider.normalizeEvent(event);
@@ -442,12 +432,14 @@ describe('opencode-provider.ts', () => {
});
});
describe('start-step events', () => {
it('should return null for start-step events (informational)', () => {
describe('step_start events', () => {
it('should return null for step_start events (informational)', () => {
const event = {
type: 'start-step',
step: 1,
session_id: 'test-session',
type: 'step_start',
part: {
type: 'step-start',
},
sessionID: 'test-session',
};
const result = provider.normalizeEvent(event);
@@ -456,14 +448,16 @@ describe('opencode-provider.ts', () => {
});
});
describe('finish-step events', () => {
it('should convert successful finish-step to result message', () => {
describe('step_finish events', () => {
it('should convert successful step_finish to result message', () => {
const event = {
type: 'finish-step',
step: 1,
success: true,
result: 'Task completed successfully',
session_id: 'test-session',
type: 'step_finish',
part: {
type: 'step-finish',
reason: 'stop',
result: 'Task completed successfully',
},
sessionID: 'test-session',
};
const result = provider.normalizeEvent(event);
@@ -476,13 +470,15 @@ describe('opencode-provider.ts', () => {
});
});
it('should convert finish-step with success=false to error message', () => {
it('should convert step_finish with error to error message', () => {
const event = {
type: 'finish-step',
step: 1,
success: false,
error: 'Something went wrong',
session_id: 'test-session',
type: 'step_finish',
part: {
type: 'step-finish',
reason: 'error',
error: 'Something went wrong',
},
sessionID: 'test-session',
};
const result = provider.normalizeEvent(event);
@@ -494,11 +490,13 @@ describe('opencode-provider.ts', () => {
});
});
it('should convert finish-step with error property to error message', () => {
it('should convert step_finish with error property to error message', () => {
const event = {
type: 'finish-step',
step: 1,
error: 'Process failed',
type: 'step_finish',
part: {
type: 'step-finish',
error: 'Process failed',
},
};
const result = provider.normalizeEvent(event);
@@ -509,9 +507,11 @@ describe('opencode-provider.ts', () => {
it('should provide default error message for failed step without error text', () => {
const event = {
type: 'finish-step',
step: 1,
success: false,
type: 'step_finish',
part: {
type: 'step-finish',
reason: 'error',
},
};
const result = provider.normalizeEvent(event);
@@ -520,11 +520,14 @@ describe('opencode-provider.ts', () => {
expect(result?.error).toBe('Step execution failed');
});
it('should treat finish-step without success flag as success', () => {
it('should treat step_finish with reason=stop as success', () => {
const event = {
type: 'finish-step',
step: 1,
result: 'Done',
type: 'step_finish',
part: {
type: 'step-finish',
reason: 'stop',
result: 'Done',
},
};
const result = provider.normalizeEvent(event);
@@ -1069,12 +1072,15 @@ describe('opencode-provider.ts', () => {
});
describe('normalizeEvent additional edge cases', () => {
it('should handle tool-call with empty args object', () => {
it('should handle tool_call with empty args object', () => {
const event = {
type: 'tool-call',
call_id: 'call-123',
name: 'Glob',
args: {},
type: 'tool_call',
part: {
type: 'tool-call',
call_id: 'call-123',
name: 'Glob',
args: {},
},
};
const result = provider.normalizeEvent(event);
@@ -1083,12 +1089,15 @@ describe('opencode-provider.ts', () => {
expect(result?.message?.content[0].input).toEqual({});
});
it('should handle tool-call with null args', () => {
it('should handle tool_call with null args', () => {
const event = {
type: 'tool-call',
call_id: 'call-123',
name: 'Glob',
args: null,
type: 'tool_call',
part: {
type: 'tool-call',
call_id: 'call-123',
name: 'Glob',
args: null,
},
};
const result = provider.normalizeEvent(event);
@@ -1097,18 +1106,21 @@ describe('opencode-provider.ts', () => {
expect(result?.message?.content[0].input).toBeNull();
});
it('should handle tool-call with complex nested args', () => {
it('should handle tool_call with complex nested args', () => {
const event = {
type: 'tool-call',
call_id: 'call-123',
name: 'Edit',
args: {
file_path: '/tmp/test.ts',
changes: [
{ line: 10, old: 'foo', new: 'bar' },
{ line: 20, old: 'baz', new: 'qux' },
],
options: { replace_all: true },
type: 'tool_call',
part: {
type: 'tool-call',
call_id: 'call-123',
name: 'Edit',
args: {
file_path: '/tmp/test.ts',
changes: [
{ line: 10, old: 'foo', new: 'bar' },
{ line: 20, old: 'baz', new: 'qux' },
],
options: { replace_all: true },
},
},
};
@@ -1125,11 +1137,14 @@ describe('opencode-provider.ts', () => {
});
});
it('should handle tool-result with empty output', () => {
it('should handle tool_result with empty output', () => {
const event = {
type: 'tool-result',
call_id: 'call-123',
output: '',
type: 'tool_result',
part: {
type: 'tool-result',
call_id: 'call-123',
output: '',
},
};
const result = provider.normalizeEvent(event);
@@ -1138,10 +1153,13 @@ describe('opencode-provider.ts', () => {
expect(result?.message?.content[0].content).toBe('');
});
it('should handle text-delta with whitespace-only text', () => {
it('should handle text with whitespace-only text', () => {
const event = {
type: 'text-delta',
text: ' ',
type: 'text',
part: {
type: 'text',
text: ' ',
},
};
const result = provider.normalizeEvent(event);
@@ -1151,10 +1169,13 @@ describe('opencode-provider.ts', () => {
expect(result?.message?.content[0].text).toBe(' ');
});
it('should handle text-delta with newlines', () => {
it('should handle text with newlines', () => {
const event = {
type: 'text-delta',
text: 'Line 1\nLine 2\nLine 3',
type: 'text',
part: {
type: 'text',
text: 'Line 1\nLine 2\nLine 3',
},
};
const result = provider.normalizeEvent(event);
@@ -1162,12 +1183,15 @@ describe('opencode-provider.ts', () => {
expect(result?.message?.content[0].text).toBe('Line 1\nLine 2\nLine 3');
});
it('should handle finish-step with both result and error (error takes precedence)', () => {
it('should handle step_finish with both result and error (error takes precedence)', () => {
const event = {
type: 'finish-step',
result: 'Some result',
error: 'But also an error',
success: false,
type: 'step_finish',
part: {
type: 'step-finish',
reason: 'stop',
result: 'Some result',
error: 'But also an error',
},
};
const result = provider.normalizeEvent(event);
@@ -1231,13 +1255,14 @@ describe('opencode-provider.ts', () => {
const longPrompt = 'a'.repeat(10000);
const args = provider.buildCliArgs({
prompt: longPrompt,
model: 'opencode/big-pickle',
cwd: '/tmp',
});
// The prompt is NOT in args (it's passed via stdin)
// Just verify the args structure is correct
expect(args).toContain('run');
expect(args).toContain('-');
expect(args).not.toContain('-');
expect(args.join(' ')).not.toContain(longPrompt);
});
@@ -1245,22 +1270,25 @@ describe('opencode-provider.ts', () => {
const specialPrompt = 'Test $HOME $(rm -rf /) `command` "quotes" \'single\'';
const args = provider.buildCliArgs({
prompt: specialPrompt,
model: 'opencode/big-pickle',
cwd: '/tmp',
});
// Special chars in prompt should not affect args (prompt is via stdin)
expect(args).toContain('run');
expect(args).toContain('-');
expect(args).not.toContain('-');
});
it('should handle cwd with spaces', () => {
const args = provider.buildCliArgs({
prompt: 'Test',
model: 'opencode/big-pickle',
cwd: '/tmp/path with spaces/project',
});
const cwdIndex = args.indexOf('-c');
expect(args[cwdIndex + 1]).toBe('/tmp/path with spaces/project');
// cwd is set at subprocess level, not via CLI args
expect(args).not.toContain('-c');
expect(args).not.toContain('/tmp/path with spaces/project');
});
it('should handle model with unusual characters', () => {