/* eslint-disable max-lines -- Why: git RPC methods share one dispatcher fixture, and keeping the contract cases together makes method coverage easy to audit. */ import { describe, expect, it, vi } from 'vitest' import { RpcDispatcher } from '../dispatcher' import type { RpcRequest } from '../core' import type { OrcaRuntimeService } from '../../orca-runtime' import { GIT_METHODS } from './git' function makeRequest(method: string, params?: unknown): RpcRequest { return { id: 'req-1', authToken: 'tok', method, params } } describe('git RPC methods', () => { it('returns status for a selected worktree', async () => { const runtime = { getRuntimeId: () => 'test-runtime', getRuntimeGitStatus: vi.fn().mockResolvedValue({ entries: [], conflictOperation: 'unknown', branch: 'main', head: 'abc' }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch(makeRequest('git.status', { worktree: 'id:wt-1' })) expect(runtime.getRuntimeGitStatus).toHaveBeenCalledWith('id:wt-1') expect(response).toMatchObject({ ok: true, result: { entries: [], branch: 'main' } }) }) it('forwards includeIgnored for status requests', async () => { const runtime = { getRuntimeId: () => 'test-runtime', getRuntimeGitStatus: vi.fn().mockResolvedValue({ entries: [], conflictOperation: 'unknown', ignoredPaths: ['dist/'] }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.status', { worktree: 'id:wt-1', includeIgnored: true }) ) expect(runtime.getRuntimeGitStatus).toHaveBeenCalledWith('id:wt-1', { includeIgnored: true }) expect(response).toMatchObject({ ok: true, result: { ignoredPaths: ['dist/'] } }) }) it('returns ignored paths for selected explorer rows', async () => { const runtime = { getRuntimeId: () => 'test-runtime', checkRuntimeGitIgnoredPaths: vi.fn().mockResolvedValue(['dist/bundle.js']) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.checkIgnored', { worktree: 'id:wt-1', paths: ['dist/bundle.js', 'src/index.ts'] }) ) expect(runtime.checkRuntimeGitIgnoredPaths).toHaveBeenCalledWith('id:wt-1', [ 'dist/bundle.js', 'src/index.ts' ]) expect(response).toMatchObject({ ok: true, result: ['dist/bundle.js'] }) }) it('returns a worktree file diff', async () => { const runtime = { getRuntimeId: () => 'test-runtime', getRuntimeGitDiff: vi.fn().mockResolvedValue({ kind: 'text', originalContent: '', modifiedContent: 'hello', originalIsBinary: false, modifiedIsBinary: false }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.diff', { worktree: 'id:wt-1', filePath: 'src/index.ts', staged: false, compareAgainstHead: true }) ) expect(runtime.getRuntimeGitDiff).toHaveBeenCalledWith('id:wt-1', 'src/index.ts', false, true) expect(response).toMatchObject({ ok: true, result: { kind: 'text', modifiedContent: 'hello' } }) }) it('returns bounded git history for a selected worktree', async () => { const history = { items: [], hasIncomingChanges: false, hasOutgoingChanges: false, hasMore: false, limit: 50 } const runtime = { getRuntimeId: () => 'test-runtime', getRuntimeGitHistory: vi.fn().mockResolvedValue(history) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.history', { worktree: 'id:wt-1', limit: 25, baseRef: 'origin/main' }) ) expect(runtime.getRuntimeGitHistory).toHaveBeenCalledWith('id:wt-1', { limit: 25, baseRef: 'origin/main' }) expect(response).toMatchObject({ ok: true, result: history }) }) it('routes common mutations to the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', stageRuntimeGitPath: vi.fn().mockResolvedValue({ ok: true }), bulkUnstageRuntimeGitPaths: vi.fn().mockResolvedValue({ ok: true }), discardRuntimeGitPath: vi.fn().mockResolvedValue({ ok: true }), bulkDiscardRuntimeGitPaths: vi.fn().mockResolvedValue({ ok: true }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) await dispatcher.dispatch( makeRequest('git.stage', { worktree: 'id:wt-1', filePath: 'src/a.ts' }) ) await dispatcher.dispatch( makeRequest('git.bulkUnstage', { worktree: 'id:wt-1', filePaths: ['src/a.ts', 'b.ts'] }) ) await dispatcher.dispatch( makeRequest('git.discard', { worktree: 'id:wt-1', filePath: 'src/a.ts' }) ) await dispatcher.dispatch( makeRequest('git.bulkDiscard', { worktree: 'id:wt-1', filePaths: ['src/a.ts', 'b.ts'] }) ) expect(runtime.stageRuntimeGitPath).toHaveBeenCalledWith('id:wt-1', 'src/a.ts') expect(runtime.bulkUnstageRuntimeGitPaths).toHaveBeenCalledWith('id:wt-1', ['src/a.ts', 'b.ts']) expect(runtime.discardRuntimeGitPath).toHaveBeenCalledWith('id:wt-1', 'src/a.ts') expect(runtime.bulkDiscardRuntimeGitPaths).toHaveBeenCalledWith('id:wt-1', ['src/a.ts', 'b.ts']) }) it('rejects empty bulk mutation paths before calling the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', bulkDiscardRuntimeGitPaths: vi.fn() } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.bulkDiscard', { worktree: 'id:wt-1', filePaths: [''] }) ) expect(response.ok).toBe(false) expect(response).toMatchObject({ error: expect.objectContaining({ code: 'invalid_argument' }) }) expect(runtime.bulkDiscardRuntimeGitPaths).not.toHaveBeenCalled() }) it('routes remote operations to the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', commitRuntimeGit: vi.fn().mockResolvedValue({ success: true }), generateRuntimeCommitMessage: vi .fn() .mockResolvedValue({ success: true, message: 'feat: test' }), discoverRuntimeCommitMessageModels: vi.fn().mockResolvedValue({ success: true, models: [{ id: 'auto', label: 'Auto' }], defaultModelId: 'auto' }), cancelRuntimeGenerateCommitMessage: vi.fn().mockResolvedValue({ ok: true }), abortRuntimeGitMerge: vi.fn().mockResolvedValue({ ok: true }), abortRuntimeGitRebase: vi.fn().mockResolvedValue({ ok: true }), pushRuntimeGit: vi.fn().mockResolvedValue({ ok: true }), getRuntimeGitRemoteFileUrl: vi.fn().mockResolvedValue('https://example.com/file#L3'), getRuntimeGitRemoteCommitUrl: vi.fn().mockResolvedValue('https://example.com/commit/abc') } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const commitOid = '0123456789abcdef0123456789abcdef01234567' await dispatcher.dispatch( makeRequest('git.commit', { worktree: 'id:wt-1', message: 'feat: test' }) ) await dispatcher.dispatch(makeRequest('git.generateCommitMessage', { worktree: 'id:wt-1' })) await dispatcher.dispatch( makeRequest('git.discoverCommitMessageModels', { worktree: 'id:wt-1', agentId: 'cursor', agentCmdOverrides: { cursor: 'cursor-agent' } }) ) await dispatcher.dispatch( makeRequest('git.cancelGenerateCommitMessage', { worktree: 'id:wt-1' }) ) await dispatcher.dispatch(makeRequest('git.abortMerge', { worktree: 'id:wt-1' })) await dispatcher.dispatch(makeRequest('git.abortRebase', { worktree: 'id:wt-1' })) await dispatcher.dispatch( makeRequest('git.push', { worktree: 'id:wt-1', publish: true, pushTarget: { remoteName: 'origin', branchName: 'feature' } }) ) const response = await dispatcher.dispatch( makeRequest('git.remoteFileUrl', { worktree: 'id:wt-1', relativePath: 'src/a.ts', line: 3 }) ) const commitUrlResponse = await dispatcher.dispatch( makeRequest('git.remoteCommitUrl', { worktree: 'id:wt-1', sha: commitOid }) ) expect(runtime.commitRuntimeGit).toHaveBeenCalledWith('id:wt-1', 'feat: test') expect(runtime.generateRuntimeCommitMessage).toHaveBeenCalledWith('id:wt-1') expect(runtime.discoverRuntimeCommitMessageModels).toHaveBeenCalledWith('id:wt-1', 'cursor', { agentCmdOverrides: { cursor: 'cursor-agent' } }) expect(runtime.cancelRuntimeGenerateCommitMessage).toHaveBeenCalledWith('id:wt-1') expect(runtime.abortRuntimeGitMerge).toHaveBeenCalledWith('id:wt-1') expect(runtime.abortRuntimeGitRebase).toHaveBeenCalledWith('id:wt-1') expect(runtime.pushRuntimeGit).toHaveBeenCalledWith( 'id:wt-1', true, { remoteName: 'origin', branchName: 'feature' }, undefined ) expect(response).toMatchObject({ ok: true, result: 'https://example.com/file#L3' }) expect(runtime.getRuntimeGitRemoteCommitUrl).toHaveBeenCalledWith('id:wt-1', commitOid) expect(commitUrlResponse).toMatchObject({ ok: true, result: 'https://example.com/commit/abc' }) }) it('rejects remote commit URL requests without a full git object id', async () => { const runtime = { getRuntimeId: () => 'test-runtime', getRuntimeGitRemoteCommitUrl: vi.fn() } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.remoteCommitUrl', { worktree: 'id:wt-1', sha: 'abc123' }) ) expect(response.ok).toBe(false) expect(runtime.getRuntimeGitRemoteCommitUrl).not.toHaveBeenCalled() }) it('forwards force-with-lease push mode to the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', pushRuntimeGit: vi.fn().mockResolvedValue({ ok: true }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) await dispatcher.dispatch( makeRequest('git.push', { worktree: 'id:wt-1', forceWithLease: true }) ) expect(runtime.pushRuntimeGit).toHaveBeenCalledWith('id:wt-1', undefined, undefined, true) }) it('forwards rebase-from-base to the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', rebaseRuntimeGitFromBase: vi.fn().mockResolvedValue({ ok: true }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) await dispatcher.dispatch( makeRequest('git.rebaseFromBase', { worktree: 'id:wt-1', baseRef: 'origin/main' }) ) expect(runtime.rebaseRuntimeGitFromBase).toHaveBeenCalledWith('id:wt-1', 'origin/main') }) it('forwards fetch push target to the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', fetchRuntimeGit: vi.fn().mockResolvedValue({ ok: true }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const pushTarget = { remoteName: 'fork', branchName: 'feature' } await dispatcher.dispatch( makeRequest('git.fetch', { worktree: 'id:wt-1', pushTarget }) ) expect(runtime.fetchRuntimeGit).toHaveBeenCalledWith('id:wt-1', pushTarget) }) it('forwards fork sync requests to the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', syncRuntimeGitForkDefaultBranch: vi.fn().mockResolvedValue({ status: 'up-to-date', originRemote: 'origin', upstreamRemote: 'upstream', branchName: 'main', ahead: 0, behind: 0 }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.forkSync', { worktree: 'id:wt-1', expectedUpstream: { owner: 'stablyai', repo: 'orca' } }) ) expect(runtime.syncRuntimeGitForkDefaultBranch).toHaveBeenCalledWith('id:wt-1', { owner: 'stablyai', repo: 'orca' }) expect(response).toMatchObject({ ok: true, result: { status: 'up-to-date', branchName: 'main', ahead: 0, behind: 0 } }) }) it('rejects blank fork sync expected upstream fields before calling the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', syncRuntimeGitForkDefaultBranch: vi.fn() } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.forkSync', { worktree: 'id:wt-1', expectedUpstream: { owner: ' ', repo: 'orca' } }) ) expect(response.ok).toBe(false) expect(response).toMatchObject({ error: expect.objectContaining({ code: 'invalid_argument' }) }) expect(runtime.syncRuntimeGitForkDefaultBranch).not.toHaveBeenCalled() }) it('rejects missing fork sync expected upstream before calling the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', syncRuntimeGitForkDefaultBranch: vi.fn() } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.forkSync', { worktree: 'id:wt-1' }) ) expect(response.ok).toBe(false) expect(response).toMatchObject({ error: expect.objectContaining({ code: 'invalid_argument' }) }) expect(runtime.syncRuntimeGitForkDefaultBranch).not.toHaveBeenCalled() }) it('forwards fast-forward push target to the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', fastForwardRuntimeGit: vi.fn().mockResolvedValue({ ok: true }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const pushTarget = { remoteName: 'fork', branchName: 'feature' } await dispatcher.dispatch( makeRequest('git.fastForward', { worktree: 'id:wt-1', pushTarget }) ) expect(runtime.fastForwardRuntimeGit).toHaveBeenCalledWith('id:wt-1', pushTarget) }) it('forwards commit-message settings to the runtime', async () => { const commitMessageAi = { enabled: true, agentId: 'codex', selectedModelByAgent: { codex: 'gpt-5.3-codex-spark' }, selectedModelByAgentByHost: { 'ssh:conn-1': { cursor: 'remote-model' } }, discoveredModelsByAgent: { cursor: [{ id: 'local-model', label: 'Local Model' }] }, discoveredModelsByAgentByHost: { 'ssh:conn-1': { cursor: [{ id: 'remote-model', label: 'Remote Model' }] } }, selectedThinkingByModel: { 'gpt-5.3-codex-spark': 'medium' }, customPrompt: '', customAgentCommand: '' } const agentCmdOverrides = { codex: 'codex --profile work' } const runtime = { getRuntimeId: () => 'test-runtime', generateRuntimeCommitMessage: vi.fn().mockResolvedValue({ success: true, message: 'test' }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) await dispatcher.dispatch( makeRequest('git.generateCommitMessage', { worktree: 'id:wt-1', commitMessageAi, agentCmdOverrides, enableGitHubAttribution: true, commitMessageDiscoveryHostKey: 'runtime:env-1' }) ) expect(runtime.generateRuntimeCommitMessage).toHaveBeenCalledWith('id:wt-1', { commitMessageAi, agentCmdOverrides, enableGitHubAttribution: true, commitMessageDiscoveryHostKey: 'runtime:env-1' }) }) it('forwards one-shot commit-message params to the runtime', async () => { const sourceControlAiResolvedParams = { agentId: 'codex', model: 'gpt-5.5', thinkingLevel: 'high', customPrompt: 'Use Conventional Commits.' } const runtime = { getRuntimeId: () => 'test-runtime', generateRuntimeCommitMessage: vi.fn().mockResolvedValue({ success: true, message: 'test' }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) await dispatcher.dispatch( makeRequest('git.generateCommitMessage', { worktree: 'id:wt-1', sourceControlAiResolvedParams }) ) expect(runtime.generateRuntimeCommitMessage).toHaveBeenCalledWith('id:wt-1', { sourceControlAiResolvedParams }) }) it('forwards one-shot pull-request generation params to the runtime', async () => { const sourceControlAiResolvedParams = { agentId: 'codex', model: 'gpt-5.5', thinkingLevel: 'high', commandInputTemplate: '{basePrompt}\n\nUse release-note style.' } const runtime = { getRuntimeId: () => 'test-runtime', generateRuntimePullRequestFields: vi .fn() .mockResolvedValue({ success: true, fields: { title: 'Test', body: '', draft: false } }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) await dispatcher.dispatch( makeRequest('git.generatePullRequestFields', { worktree: 'id:wt-1', base: 'main', title: '', body: '', draft: false, provider: 'github', useTemplate: true, sourceControlAiResolvedParams }) ) expect(runtime.generateRuntimePullRequestFields).toHaveBeenCalledWith( 'id:wt-1', { base: 'main', title: '', body: '', draft: false, provider: 'github', useTemplate: true }, { sourceControlAiResolvedParams } ) }) it('rejects malformed commit-message settings before calling the runtime', async () => { const runtime = { getRuntimeId: () => 'test-runtime', generateRuntimeCommitMessage: vi.fn() } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.generateCommitMessage', { worktree: 'id:wt-1', commitMessageAi: { enabled: true, agentId: 'codex' } }) ) expect(response.ok).toBe(false) expect(response).toMatchObject({ error: expect.objectContaining({ code: 'invalid_argument' }) }) expect(runtime.generateRuntimeCommitMessage).not.toHaveBeenCalled() }) it('rejects branch diff revisions that are not full object ids', async () => { const runtime = { getRuntimeId: () => 'test-runtime', getRuntimeGitBranchDiff: vi.fn() } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.branchDiff', { worktree: 'id:wt-1', filePath: 'src/a.ts', compare: { headOid: '--output=/tmp/orca-test', mergeBase: 'a'.repeat(40) } }) ) expect(response.ok).toBe(false) expect(runtime.getRuntimeGitBranchDiff).not.toHaveBeenCalled() }) it('rejects branch compare refs that look like git options', async () => { const runtime = { getRuntimeId: () => 'test-runtime', getRuntimeGitBranchCompare: vi.fn() } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.branchCompare', { worktree: 'id:wt-1', baseRef: '--output=/tmp/orca-test' }) ) expect(response.ok).toBe(false) expect(runtime.getRuntimeGitBranchCompare).not.toHaveBeenCalled() }) it('rejects git history limits above the runtime cap', async () => { const runtime = { getRuntimeId: () => 'test-runtime', getRuntimeGitHistory: vi.fn() } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.history', { worktree: 'id:wt-1', limit: 201 }) ) expect(response.ok).toBe(false) expect(runtime.getRuntimeGitHistory).not.toHaveBeenCalled() }) it('checks out a branch', async () => { const runtime = { getRuntimeId: () => 'test-runtime', checkoutRuntimeGitBranch: vi.fn().mockResolvedValue({ ok: true, branch: 'feature/x' }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.checkout', { worktree: 'id:wt-1', branch: 'feature/x' }) ) expect(runtime.checkoutRuntimeGitBranch).toHaveBeenCalledWith('id:wt-1', 'feature/x') expect(response).toMatchObject({ ok: true, result: { ok: true, branch: 'feature/x' } }) }) it('rejects a checkout branch that starts with a dash', async () => { const runtime = { getRuntimeId: () => 'test-runtime', checkoutRuntimeGitBranch: vi.fn() } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.checkout', { worktree: 'id:wt-1', branch: '--force' }) ) expect(response.ok).toBe(false) expect(runtime.checkoutRuntimeGitBranch).not.toHaveBeenCalled() }) it('lists local branches', async () => { const runtime = { getRuntimeId: () => 'test-runtime', listRuntimeGitLocalBranches: vi .fn() .mockResolvedValue({ current: 'main', branches: ['main', 'feature/x'] }) } as unknown as OrcaRuntimeService const dispatcher = new RpcDispatcher({ runtime, methods: GIT_METHODS }) const response = await dispatcher.dispatch( makeRequest('git.localBranches', { worktree: 'id:wt-1' }) ) expect(runtime.listRuntimeGitLocalBranches).toHaveBeenCalledWith('id:wt-1') expect(response).toMatchObject({ ok: true, result: { current: 'main', branches: ['main', 'feature/x'] } }) }) })