mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 00:27:39 +00:00
752 lines
25 KiB
TypeScript
752 lines
25 KiB
TypeScript
/* eslint-disable max-lines -- Why: file RPC routing coverage stays together so
|
|
the dispatcher contract for read, write, mutation, and watch methods is 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 { FILE_METHODS } from './files'
|
|
|
|
function makeRequest(method: string, params?: unknown): RpcRequest {
|
|
return { id: 'req-1', authToken: 'tok', method, params }
|
|
}
|
|
|
|
describe('file RPC methods', () => {
|
|
it('lists files for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
listMobileFiles: vi.fn().mockResolvedValue({
|
|
worktree: 'wt-1',
|
|
rootPath: '/repo',
|
|
files: [],
|
|
totalCount: 0,
|
|
truncated: false
|
|
})
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(makeRequest('files.list', { worktree: 'id:wt-1' }))
|
|
|
|
expect(runtime.listMobileFiles).toHaveBeenCalledWith('id:wt-1')
|
|
expect(response).toMatchObject({
|
|
ok: true,
|
|
result: { worktree: 'wt-1', files: [] }
|
|
})
|
|
})
|
|
|
|
it('opens a relative file path for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
openMobileFile: vi.fn().mockResolvedValue({
|
|
worktree: 'wt-1',
|
|
relativePath: 'docs/readme.md',
|
|
kind: 'markdown',
|
|
opened: true
|
|
})
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.open', { worktree: 'id:wt-1', relativePath: 'docs/readme.md' })
|
|
)
|
|
|
|
expect(runtime.openMobileFile).toHaveBeenCalledWith('id:wt-1', 'docs/readme.md')
|
|
expect(response).toMatchObject({
|
|
ok: true,
|
|
result: { kind: 'markdown', opened: true }
|
|
})
|
|
})
|
|
|
|
it('opens a source control diff for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
openMobileDiff: vi.fn().mockResolvedValue({
|
|
worktree: 'wt-1',
|
|
relativePath: 'docs/readme.md',
|
|
kind: 'markdown',
|
|
opened: true
|
|
})
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.openDiff', {
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'docs/readme.md',
|
|
staged: true
|
|
})
|
|
)
|
|
|
|
expect(runtime.openMobileDiff).toHaveBeenCalledWith('id:wt-1', 'docs/readme.md', true)
|
|
expect(response).toMatchObject({
|
|
ok: true,
|
|
result: { kind: 'markdown', opened: true }
|
|
})
|
|
})
|
|
|
|
it('browses server directories before a project is added', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
browseServerDir: vi.fn().mockResolvedValue({
|
|
resolvedPath: '/home/me',
|
|
entries: [{ name: 'project', isDirectory: true, isSymlink: false }]
|
|
})
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(makeRequest('files.browseServerDir', { path: '~' }))
|
|
|
|
expect(runtime.browseServerDir).toHaveBeenCalledWith('~')
|
|
expect(response).toMatchObject({
|
|
ok: true,
|
|
result: { resolvedPath: '/home/me', entries: [{ name: 'project', isDirectory: true }] }
|
|
})
|
|
})
|
|
|
|
it('streams file watch changes until the subscription is cleaned up', async () => {
|
|
vi.useFakeTimers()
|
|
try {
|
|
type WatchCallback = (
|
|
events: { kind: 'update'; absolutePath: string; isDirectory?: boolean }[]
|
|
) => void
|
|
const watchFileExplorer = vi.fn(async (_worktree: string, _callback: WatchCallback) => {
|
|
return vi.fn()
|
|
})
|
|
const cleanups = new Map<string, () => void>()
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
watchFileExplorer,
|
|
registerSubscriptionCleanup: vi.fn().mockImplementation((id, cleanup) => {
|
|
cleanups.set(id, cleanup)
|
|
})
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
const replies: unknown[] = []
|
|
|
|
const dispatch = dispatcher.dispatchStreaming(
|
|
makeRequest('files.watch', { worktree: 'id:wt-1' }),
|
|
(response) => replies.push(JSON.parse(response))
|
|
)
|
|
|
|
await vi.waitFor(() => {
|
|
expect(replies).toHaveLength(1)
|
|
})
|
|
expect(runtime.watchFileExplorer).toHaveBeenCalledWith('id:wt-1', expect.any(Function))
|
|
expect(replies[0]).toMatchObject({
|
|
ok: true,
|
|
streaming: true,
|
|
result: { type: 'ready', subscriptionId: expect.stringContaining('files-watch-') }
|
|
})
|
|
|
|
const emitWatchChange = watchFileExplorer.mock.calls[0]?.[1]
|
|
expect(emitWatchChange).toBeDefined()
|
|
emitWatchChange?.([{ kind: 'update', absolutePath: '/repo/readme.md', isDirectory: false }])
|
|
emitWatchChange?.([
|
|
{ kind: 'update', absolutePath: '/repo/package.json', isDirectory: false }
|
|
])
|
|
expect(replies).toHaveLength(1)
|
|
|
|
await vi.runOnlyPendingTimersAsync()
|
|
|
|
expect(replies[1]).toMatchObject({
|
|
ok: true,
|
|
streaming: true,
|
|
result: {
|
|
type: 'changed',
|
|
worktree: 'id:wt-1',
|
|
events: [
|
|
{ kind: 'update', absolutePath: '/repo/readme.md', isDirectory: false },
|
|
{ kind: 'update', absolutePath: '/repo/package.json', isDirectory: false }
|
|
]
|
|
}
|
|
})
|
|
|
|
const ready = replies[0] as { result: { subscriptionId: string } }
|
|
cleanups.get(ready.result.subscriptionId)?.()
|
|
await dispatch
|
|
|
|
expect(replies[2]).toMatchObject({
|
|
ok: true,
|
|
streaming: true,
|
|
result: { type: 'end' }
|
|
})
|
|
} finally {
|
|
vi.useRealTimers()
|
|
}
|
|
})
|
|
|
|
it('tears down a file watch that resolves after the connection already closed', async () => {
|
|
type WatchCallback = (
|
|
events: { kind: 'update'; absolutePath: string; isDirectory?: boolean }[]
|
|
) => void
|
|
const unwatch = vi.fn()
|
|
let resolveWatch: (value: () => void) => void = () => {}
|
|
const watchFileExplorer = vi.fn((_worktree: string, _callback: WatchCallback) => {
|
|
return new Promise<() => void>((resolve) => {
|
|
resolveWatch = resolve
|
|
})
|
|
})
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
watchFileExplorer,
|
|
registerSubscriptionCleanup: vi.fn()
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
const abortController = new AbortController()
|
|
const replies: unknown[] = []
|
|
|
|
const dispatch = dispatcher.dispatchStreaming(
|
|
makeRequest('files.watch', { worktree: 'id:wt-1' }),
|
|
(response) => replies.push(JSON.parse(response)),
|
|
{ connectionId: 'conn-1', signal: abortController.signal }
|
|
)
|
|
await vi.waitFor(() => {
|
|
expect(watchFileExplorer).toHaveBeenCalled()
|
|
})
|
|
abortController.abort()
|
|
await dispatch
|
|
|
|
resolveWatch(unwatch)
|
|
await vi.waitFor(() => {
|
|
expect(unwatch).toHaveBeenCalled()
|
|
})
|
|
expect(runtime.registerSubscriptionCleanup).not.toHaveBeenCalled()
|
|
expect(replies).toEqual([])
|
|
})
|
|
|
|
it('drops queued file watch events when aborted before setup resolves', async () => {
|
|
vi.useFakeTimers()
|
|
try {
|
|
type WatchCallback = (
|
|
events: { kind: 'update'; absolutePath: string; isDirectory?: boolean }[]
|
|
) => void
|
|
const unwatch = vi.fn()
|
|
let resolveWatch: (value: () => void) => void = () => {}
|
|
const watchFileExplorer = vi.fn((_worktree: string, callback: WatchCallback) => {
|
|
callback([{ kind: 'update', absolutePath: '/repo/queued.ts', isDirectory: false }])
|
|
return new Promise<() => void>((resolve) => {
|
|
resolveWatch = resolve
|
|
})
|
|
})
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
watchFileExplorer,
|
|
registerSubscriptionCleanup: vi.fn()
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
const abortController = new AbortController()
|
|
const replies: unknown[] = []
|
|
|
|
const dispatch = dispatcher.dispatchStreaming(
|
|
makeRequest('files.watch', { worktree: 'id:wt-1' }),
|
|
(response) => replies.push(JSON.parse(response)),
|
|
{ connectionId: 'conn-1', signal: abortController.signal }
|
|
)
|
|
await vi.waitFor(() => {
|
|
expect(watchFileExplorer).toHaveBeenCalled()
|
|
})
|
|
|
|
abortController.abort()
|
|
await dispatch
|
|
await vi.runOnlyPendingTimersAsync()
|
|
resolveWatch(unwatch)
|
|
await vi.waitFor(() => {
|
|
expect(unwatch).toHaveBeenCalled()
|
|
})
|
|
|
|
expect(runtime.registerSubscriptionCleanup).not.toHaveBeenCalled()
|
|
expect(replies).toEqual([])
|
|
} finally {
|
|
vi.useRealTimers()
|
|
}
|
|
})
|
|
|
|
it('reads a relative file path for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
readMobileFile: vi.fn().mockResolvedValue({
|
|
worktree: 'wt-1',
|
|
relativePath: 'src/index.ts',
|
|
content: 'export {}\\n',
|
|
truncated: false,
|
|
byteLength: 10
|
|
})
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.read', { worktree: 'id:wt-1', relativePath: 'src/index.ts' })
|
|
)
|
|
|
|
expect(runtime.readMobileFile).toHaveBeenCalledWith('id:wt-1', 'src/index.ts')
|
|
expect(response).toMatchObject({
|
|
ok: true,
|
|
result: { content: 'export {}\\n', truncated: false }
|
|
})
|
|
})
|
|
|
|
it('resolves a tapped terminal path for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
resolveTerminalPath: vi.fn().mockResolvedValue({
|
|
worktree: 'wt-1',
|
|
relativePath: 'src/index.ts',
|
|
exists: true,
|
|
isDirectory: false
|
|
})
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.resolveTerminalPath', {
|
|
worktree: 'id:wt-1',
|
|
pathText: '/repo/src/index.ts',
|
|
cwd: '/repo'
|
|
})
|
|
)
|
|
|
|
expect(runtime.resolveTerminalPath).toHaveBeenCalledWith(
|
|
'id:wt-1',
|
|
'/repo/src/index.ts',
|
|
'/repo'
|
|
)
|
|
expect(response).toMatchObject({
|
|
ok: true,
|
|
result: { relativePath: 'src/index.ts', exists: true, isDirectory: false }
|
|
})
|
|
})
|
|
|
|
it('reads a preview file for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
readFileExplorerPreview: vi.fn().mockResolvedValue({
|
|
content: 'base64',
|
|
isBinary: true,
|
|
isImage: true,
|
|
mimeType: 'image/png'
|
|
})
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.readPreview', { worktree: 'id:wt-1', relativePath: 'img/logo.png' })
|
|
)
|
|
|
|
expect(runtime.readFileExplorerPreview).toHaveBeenCalledWith('id:wt-1', 'img/logo.png')
|
|
expect(response).toMatchObject({
|
|
ok: true,
|
|
result: { content: 'base64', isBinary: true, mimeType: 'image/png' }
|
|
})
|
|
})
|
|
|
|
it('reads a file explorer directory for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
readFileExplorerDir: vi.fn().mockResolvedValue([{ name: 'src', isDirectory: true }])
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.readDir', { worktree: 'id:wt-1', relativePath: '' })
|
|
)
|
|
|
|
expect(runtime.readFileExplorerDir).toHaveBeenCalledWith('id:wt-1', '')
|
|
expect(response).toMatchObject({
|
|
ok: true,
|
|
result: [{ name: 'src', isDirectory: true }]
|
|
})
|
|
})
|
|
|
|
it('writes file explorer content for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
writeFileExplorerFile: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.write', {
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'src/index.ts',
|
|
content: 'export {}'
|
|
})
|
|
)
|
|
|
|
expect(runtime.writeFileExplorerFile).toHaveBeenCalledWith(
|
|
'id:wt-1',
|
|
'src/index.ts',
|
|
'export {}'
|
|
)
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it('writes base64 file explorer content for runtime uploads', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
writeFileExplorerFileBase64: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.writeBase64', {
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'assets/logo.png',
|
|
contentBase64: 'cG5n'
|
|
})
|
|
)
|
|
|
|
expect(runtime.writeFileExplorerFileBase64).toHaveBeenCalledWith(
|
|
'id:wt-1',
|
|
'assets/logo.png',
|
|
'cG5n'
|
|
)
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it('writes base64 file explorer content chunks for large runtime uploads', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
writeFileExplorerFileBase64Chunk: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.writeBase64Chunk', {
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'assets/video.mov',
|
|
contentBase64: 'AAAA',
|
|
append: true
|
|
})
|
|
)
|
|
|
|
expect(runtime.writeFileExplorerFileBase64Chunk).toHaveBeenCalledWith(
|
|
'id:wt-1',
|
|
'assets/video.mov',
|
|
'AAAA',
|
|
true
|
|
)
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it.each([
|
|
['missing content', { worktree: 'id:wt-1', relativePath: 'src/index.ts' }],
|
|
['null content', { worktree: 'id:wt-1', relativePath: 'src/index.ts', content: null }],
|
|
['non-string content', { worktree: 'id:wt-1', relativePath: 'src/index.ts', content: 0 }]
|
|
])('rejects a write with %s instead of truncating the file', async (_name, params) => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
writeFileExplorerFile: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(makeRequest('files.write', params))
|
|
|
|
expect(response).toMatchObject({ ok: false })
|
|
expect(runtime.writeFileExplorerFile).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('still allows writing an explicit empty string (empty file)', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
writeFileExplorerFile: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.write', { worktree: 'id:wt-1', relativePath: 'src/index.ts', content: '' })
|
|
)
|
|
|
|
expect(runtime.writeFileExplorerFile).toHaveBeenCalledWith('id:wt-1', 'src/index.ts', '')
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it('allows writing explicit empty base64 content', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
writeFileExplorerFileBase64: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.writeBase64', {
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'assets/logo.png',
|
|
contentBase64: ''
|
|
})
|
|
)
|
|
|
|
expect(runtime.writeFileExplorerFileBase64).toHaveBeenCalledWith(
|
|
'id:wt-1',
|
|
'assets/logo.png',
|
|
''
|
|
)
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it.each([
|
|
['missing content', { worktree: 'id:wt-1', relativePath: 'assets/logo.png' }],
|
|
['null content', { worktree: 'id:wt-1', relativePath: 'assets/logo.png', contentBase64: null }],
|
|
[
|
|
'non-string content',
|
|
{ worktree: 'id:wt-1', relativePath: 'assets/logo.png', contentBase64: 0 }
|
|
],
|
|
[
|
|
'malformed content',
|
|
{ worktree: 'id:wt-1', relativePath: 'assets/logo.png', contentBase64: '!!!!' }
|
|
]
|
|
])('rejects a base64 write with %s', async (_name, params) => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
writeFileExplorerFileBase64: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(makeRequest('files.writeBase64', params))
|
|
|
|
expect(response).toMatchObject({ ok: false })
|
|
expect(runtime.writeFileExplorerFileBase64).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('allows writing an explicit empty base64 chunk', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
writeFileExplorerFileBase64Chunk: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.writeBase64Chunk', {
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'assets/video.mov',
|
|
contentBase64: '',
|
|
append: true
|
|
})
|
|
)
|
|
|
|
expect(runtime.writeFileExplorerFileBase64Chunk).toHaveBeenCalledWith(
|
|
'id:wt-1',
|
|
'assets/video.mov',
|
|
'',
|
|
true
|
|
)
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it.each([
|
|
[
|
|
'missing content',
|
|
{
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'assets/video.mov',
|
|
append: true
|
|
}
|
|
],
|
|
[
|
|
'null content',
|
|
{
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'assets/video.mov',
|
|
contentBase64: null,
|
|
append: true
|
|
}
|
|
],
|
|
[
|
|
'non-string content',
|
|
{
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'assets/video.mov',
|
|
contentBase64: 0,
|
|
append: true
|
|
}
|
|
],
|
|
[
|
|
'malformed content',
|
|
{
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'assets/video.mov',
|
|
contentBase64: '!!!!',
|
|
append: true
|
|
}
|
|
]
|
|
])('rejects a base64 chunk write with %s (inherits the schema)', async (_name, params) => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
writeFileExplorerFileBase64Chunk: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(makeRequest('files.writeBase64Chunk', params))
|
|
|
|
expect(response).toMatchObject({ ok: false })
|
|
expect(runtime.writeFileExplorerFileBase64Chunk).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('commits staged runtime uploads without clobbering the final destination', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
commitFileExplorerUpload: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.commitUpload', {
|
|
worktree: 'id:wt-1',
|
|
tempRelativePath: 'assets/.logo.png.orca-upload-a',
|
|
finalRelativePath: 'assets/logo.png'
|
|
})
|
|
)
|
|
|
|
expect(runtime.commitFileExplorerUpload).toHaveBeenCalledWith(
|
|
'id:wt-1',
|
|
'assets/.logo.png.orca-upload-a',
|
|
'assets/logo.png'
|
|
)
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it('renames file explorer paths for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
renameFileExplorerPath: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.rename', {
|
|
worktree: 'id:wt-1',
|
|
oldRelativePath: 'old.ts',
|
|
newRelativePath: 'new.ts'
|
|
})
|
|
)
|
|
|
|
expect(runtime.renameFileExplorerPath).toHaveBeenCalledWith('id:wt-1', 'old.ts', 'new.ts')
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it('copies file explorer paths for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
copyFileExplorerPath: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.copy', {
|
|
worktree: 'id:wt-1',
|
|
sourceRelativePath: 'old.ts',
|
|
destinationRelativePath: 'old copy.ts'
|
|
})
|
|
)
|
|
|
|
expect(runtime.copyFileExplorerPath).toHaveBeenCalledWith('id:wt-1', 'old.ts', 'old copy.ts')
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it('deletes file explorer paths for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
deleteFileExplorerPath: vi.fn().mockResolvedValue({ ok: true })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.delete', {
|
|
worktree: 'id:wt-1',
|
|
relativePath: 'src',
|
|
recursive: true
|
|
})
|
|
)
|
|
|
|
expect(runtime.deleteFileExplorerPath).toHaveBeenCalledWith('id:wt-1', 'src', true)
|
|
expect(response).toMatchObject({ ok: true, result: { ok: true } })
|
|
})
|
|
|
|
it('searches files for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
searchRuntimeFiles: vi.fn().mockResolvedValue({
|
|
files: [],
|
|
totalMatches: 0,
|
|
truncated: false
|
|
})
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.search', {
|
|
worktree: 'id:wt-1',
|
|
query: 'needle',
|
|
caseSensitive: true,
|
|
maxResults: 50
|
|
})
|
|
)
|
|
|
|
expect(runtime.searchRuntimeFiles).toHaveBeenCalledWith('id:wt-1', {
|
|
query: 'needle',
|
|
caseSensitive: true,
|
|
wholeWord: undefined,
|
|
useRegex: undefined,
|
|
includePattern: undefined,
|
|
excludePattern: undefined,
|
|
maxResults: 50
|
|
})
|
|
expect(response).toMatchObject({ ok: true, result: { files: [], totalMatches: 0 } })
|
|
})
|
|
|
|
it('lists all quick-open files for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
listRuntimeFiles: vi.fn().mockResolvedValue(['src/index.ts'])
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.listAll', {
|
|
worktree: 'id:wt-1',
|
|
excludePaths: ['/repo/other-worktree']
|
|
})
|
|
)
|
|
|
|
expect(runtime.listRuntimeFiles).toHaveBeenCalledWith('id:wt-1', {
|
|
excludePaths: ['/repo/other-worktree']
|
|
})
|
|
expect(response).toMatchObject({ ok: true, result: ['src/index.ts'] })
|
|
})
|
|
|
|
it('lists markdown documents for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
listRuntimeMarkdownDocuments: vi.fn().mockResolvedValue([
|
|
{
|
|
filePath: '/repo/readme.md',
|
|
relativePath: 'readme.md',
|
|
basename: 'readme.md',
|
|
name: 'readme'
|
|
}
|
|
])
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.listMarkdownDocuments', { worktree: 'id:wt-1' })
|
|
)
|
|
|
|
expect(runtime.listRuntimeMarkdownDocuments).toHaveBeenCalledWith('id:wt-1')
|
|
expect(response).toMatchObject({ ok: true, result: [{ relativePath: 'readme.md' }] })
|
|
})
|
|
|
|
it('stats a relative path for a selected worktree', async () => {
|
|
const runtime = {
|
|
getRuntimeId: () => 'test-runtime',
|
|
statRuntimeFile: vi.fn().mockResolvedValue({ size: 12, isDirectory: false, mtime: 1 })
|
|
} as unknown as OrcaRuntimeService
|
|
const dispatcher = new RpcDispatcher({ runtime, methods: FILE_METHODS })
|
|
|
|
const response = await dispatcher.dispatch(
|
|
makeRequest('files.stat', { worktree: 'id:wt-1', relativePath: 'readme.md' })
|
|
)
|
|
|
|
expect(runtime.statRuntimeFile).toHaveBeenCalledWith('id:wt-1', 'readme.md')
|
|
expect(response).toMatchObject({ ok: true, result: { isDirectory: false } })
|
|
})
|
|
})
|