test(skills): cover interrupted transfer commit

This commit is contained in:
Jinwoo-H
2026-08-12 12:52:31 -04:00
parent c75d772e7f
commit 9da5c69e46
2 changed files with 116 additions and 2 deletions
@@ -1011,8 +1011,9 @@ does not mean the surrounding phase is complete.
- [ ] Test connection loss and resumption or restart behavior at every transfer boundary. Lost
final install-response convergence is covered for direct and staged paired-runtime paths;
deterministic cancellation now settles in-flight and queued paired calls across every
transport without leaking request admission or disrupting peers. Restart and the remaining
physical disconnect boundaries are still open.
transport without leaking request admission or disrupting peers. Client-mediated transfer
also cancels and cleans a fully uploaded session when cancellation interrupts the commit RPC.
Restart and the remaining physical disconnect boundaries are still open.
### Mixed versions
@@ -0,0 +1,113 @@
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
const mocks = vi.hoisted(() => ({
callRuntimeEnvironment: vi.fn(),
downloadSkillPackageGrant: vi.fn()
}))
vi.mock('../ipc/runtime-environment-transport-routing', () => ({
callRuntimeEnvironment: mocks.callRuntimeEnvironment
}))
vi.mock('./skill-package-download', () => ({
downloadSkillPackageGrant: mocks.downloadSkillPackageGrant
}))
import { transferSkillPackageToRuntime } from './skill-client-mediated-transfer'
describe('transferSkillPackageToRuntime cancellation', () => {
let root = ''
beforeEach(async () => {
root = await mkdtemp(join(tmpdir(), 'orca-skill-transfer-cancel-test-'))
mocks.callRuntimeEnvironment.mockReset()
mocks.downloadSkillPackageGrant.mockReset()
})
afterEach(async () => {
await rm(root, { recursive: true, force: true })
})
it('cancels a committed chunk session when the commit request is interrupted', async () => {
const bytes = Buffer.from('package-data')
const archivePath = join(root, 'package.tar.gz')
await writeFile(archivePath, bytes)
const downloadCleanup = vi.fn(async () => undefined)
const controller = new AbortController()
let commitStarted: () => void = () => {}
const commitPending = new Promise<void>((resolve) => {
commitStarted = resolve
})
mocks.downloadSkillPackageGrant.mockResolvedValue({ archivePath, cleanup: downloadCleanup })
mocks.callRuntimeEnvironment.mockImplementation((...args: unknown[]) => {
const method = args[2]
if (method === 'skills.beginUpload') {
return Promise.resolve({
id: 'rpc-begin',
ok: true,
result: { uploadId: 'upload-1', chunkBytes: 256 * 1024 },
_meta: { runtimeId: 'runtime-1' }
})
}
if (method === 'skills.uploadChunk') {
return Promise.resolve({
id: 'rpc-chunk',
ok: true,
result: { acknowledgedOffset: bytes.length },
_meta: { runtimeId: 'runtime-1' }
})
}
if (method === 'skills.commitUpload') {
const options = args[7] as { signal?: AbortSignal }
return new Promise((_resolve, reject) => {
options.signal?.addEventListener('abort', () => reject(options.signal?.reason), {
once: true
})
commitStarted()
})
}
return Promise.resolve({
id: 'rpc-cancel',
ok: true,
result: undefined,
_meta: { runtimeId: 'runtime-1' }
})
})
const pending = transferSkillPackageToRuntime({
userDataPath: root,
environmentId: 'environment-1',
transferId: 'operation-1',
package: {
packageId: 'package-1',
versionId: 'version-1',
packageDigest: 'a'.repeat(64),
archiveSha256: 'b'.repeat(64),
compressedBytes: bytes.length
},
grant: {
url: 'https://storage.googleapis.com/bucket/package.tar.gz',
expiresAt: '2099-01-01T00:00:00.000Z'
},
requireHttps: true,
signal: controller.signal
})
await commitPending
controller.abort()
await expect(pending).rejects.toThrow('skill-install-cancelled')
expect(downloadCleanup).toHaveBeenCalledOnce()
expect(
mocks.callRuntimeEnvironment.mock.calls.filter((call) => call[2] === 'skills.commitUpload')
).toHaveLength(1)
expect(mocks.callRuntimeEnvironment).toHaveBeenCalledWith(
root,
'environment-1',
'skills.cancelUpload',
{ uploadId: 'upload-1' },
15_000
)
})
})