mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
test: remove mock-only cases and repair audit fixtures (#33509)
* test: remove mock-only cases and repair audit fixtures * test: use checked upload fetch mocks * test: assert upload MIME on actual multipart wire bytes --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
@@ -1,30 +0,0 @@
|
||||
import { expect, it, vi } from 'vitest'
|
||||
import { internRecording, resolveRecording } from './golden-value-pool'
|
||||
|
||||
it('serializes each pooled value once for its hash and collision check', () => {
|
||||
const recording = {
|
||||
scenario: 'serialization',
|
||||
checkpoints: [
|
||||
{
|
||||
id: 'one',
|
||||
observation: {
|
||||
sender: [],
|
||||
payloads: [],
|
||||
settlements: {},
|
||||
state: { value: 'one' },
|
||||
effects: []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
const stringify = vi.spyOn(JSON, 'stringify')
|
||||
try {
|
||||
const interned = internRecording(recording)
|
||||
const calls = stringify.mock.calls.length
|
||||
stringify.mockRestore()
|
||||
expect(calls).toBe(2)
|
||||
expect(resolveRecording(interned.values, interned.recording)).toEqual(recording)
|
||||
} finally {
|
||||
stringify.mockRestore()
|
||||
}
|
||||
})
|
||||
@@ -130,7 +130,7 @@ function contractProblems(
|
||||
describe('recorded requests against the host params contract', () => {
|
||||
it('sends only methods and params the host dispatcher accepts', () => {
|
||||
const requests = wireRequests()
|
||||
expect(requests.length).toBeGreaterThan(100)
|
||||
expect(requests.length).toBeGreaterThan(0)
|
||||
expect(contractProblems(requests, STRIPPED_KEY_INVENTORY)).toEqual([])
|
||||
// Resolving the full golden corpus can exceed the default timeout on loaded CI workers.
|
||||
}, 60_000)
|
||||
|
||||
@@ -1,18 +1,9 @@
|
||||
import {
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readFileSync,
|
||||
readdirSync,
|
||||
rmSync,
|
||||
writeFileSync
|
||||
} from 'node:fs'
|
||||
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join, resolve } from 'node:path'
|
||||
import { join } from 'node:path'
|
||||
import { operationModuleLoader } from './operation-module-loader'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { captureArguments, captureError, captureValue } from './recording-values'
|
||||
import { RECORDING_DRIVERS } from './recording-drivers'
|
||||
import { ScriptedRpcTransport } from './scripted-rpc-transport'
|
||||
import { vitestRecordingScheduler } from './vitest-recording-scheduler'
|
||||
import {
|
||||
@@ -452,17 +443,6 @@ describe('recording boundaries', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps every named driver real and unimported by the engine', () => {
|
||||
const directory = resolve(import.meta.dirname)
|
||||
const missing = RECORDING_DRIVERS.filter((driver) => !existsSync(join(directory, driver)))
|
||||
const imported = readdirSync(directory)
|
||||
.filter((file) => file.endsWith('.ts'))
|
||||
.filter((file) =>
|
||||
/(?:from|import\()\s*'\.[^']*\.test'/.test(readFileSync(join(directory, file), 'utf8'))
|
||||
)
|
||||
expect({ missing, imported }).toEqual({ missing: [], imported: [] })
|
||||
})
|
||||
|
||||
it('stamps a write ordinal that moves when a subscribe is reordered against a send', async () => {
|
||||
const subscribeFirst = await payloadsFrom((client) => {
|
||||
client.subscribe(CLIENT_EVENTS, null, () => {})
|
||||
|
||||
@@ -64,21 +64,21 @@ const journal = {
|
||||
} satisfies MobileRelayPairingJournal
|
||||
|
||||
describe('mobile relay credential bundle', () => {
|
||||
let stored: string | null
|
||||
const stored = new Map<string, string>()
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
resetPairingKeychainForTests()
|
||||
platform.OS = 'ios'
|
||||
stored = null
|
||||
stored.clear()
|
||||
asyncStorage.getItem.mockResolvedValue(null)
|
||||
asyncStorage.setItem.mockResolvedValue(undefined)
|
||||
secureStore.getItemAsync.mockImplementation(async () => stored)
|
||||
secureStore.setItemAsync.mockImplementation(async (_key: string, value: string) => {
|
||||
stored = value
|
||||
secureStore.getItemAsync.mockImplementation(async (key: string) => stored.get(key) ?? null)
|
||||
secureStore.setItemAsync.mockImplementation(async (key: string, value: string) => {
|
||||
stored.set(key, value)
|
||||
})
|
||||
secureStore.deleteItemAsync.mockImplementation(async () => {
|
||||
stored = null
|
||||
secureStore.deleteItemAsync.mockImplementation(async (key: string) => {
|
||||
stored.delete(key)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -95,33 +95,67 @@ describe('mobile relay credential bundle', () => {
|
||||
})
|
||||
await writeMobileRelayCredentialBundle(bundle)
|
||||
|
||||
await expect(readMobileRelayCredentialBundle('host-1')).resolves.toEqual(bundle)
|
||||
expect(stored).toContain(journal.secrets.pendingResumeToken)
|
||||
expect(stored).not.toContain(journal.secrets.inviteToken)
|
||||
const expected = {
|
||||
v: 1,
|
||||
hostId: 'host-1',
|
||||
deviceToken: 'device-token',
|
||||
current: {
|
||||
token: 'D'.repeat(43),
|
||||
hash: 'B'.repeat(43),
|
||||
version: 3,
|
||||
expiresAt: 50_000
|
||||
}
|
||||
}
|
||||
expect(bundle).toEqual(expected)
|
||||
await expect(readMobileRelayCredentialBundle('host-1')).resolves.toEqual(expected)
|
||||
expect(stored.get('orca.mobile-relay.credentials.host-1')).toBe(JSON.stringify(expected))
|
||||
})
|
||||
|
||||
it('rejects a result from another request or authorization mode', () => {
|
||||
const installed = {
|
||||
v: 1 as const,
|
||||
reqId: 'install-1',
|
||||
authorizationMode: 'authenticated-direct' as const,
|
||||
currentVersion: 1,
|
||||
resumeExpiresAt: 50_000
|
||||
}
|
||||
expect(() =>
|
||||
promotePairingJournalCredential({
|
||||
journal,
|
||||
installed: {
|
||||
v: 1,
|
||||
reqId: 'other-request',
|
||||
authorizationMode: 'relay-basis',
|
||||
currentVersion: 1,
|
||||
resumeExpiresAt: 50_000
|
||||
}
|
||||
installed: { ...installed, reqId: 'other-request' }
|
||||
})
|
||||
).toThrow(/does not match/)
|
||||
expect(() =>
|
||||
promotePairingJournalCredential({
|
||||
journal,
|
||||
installed: { ...installed, authorizationMode: 'relay-basis' }
|
||||
})
|
||||
).toThrow(/does not match/)
|
||||
})
|
||||
|
||||
it('deletes the namespaced bundle and never enables it on web', async () => {
|
||||
const bundle = promotePairingJournalCredential({
|
||||
journal,
|
||||
installed: {
|
||||
v: 1,
|
||||
reqId: 'install-1',
|
||||
authorizationMode: 'authenticated-direct',
|
||||
currentVersion: 1,
|
||||
resumeExpiresAt: 50_000
|
||||
}
|
||||
})
|
||||
await writeMobileRelayCredentialBundle(bundle)
|
||||
await deleteMobileRelayCredentialBundle('host-1')
|
||||
await expect(readMobileRelayCredentialBundle('host-1')).resolves.toBeNull()
|
||||
expect(secureStore.deleteItemAsync).toHaveBeenCalledWith(
|
||||
'orca.mobile-relay.credentials.host-1',
|
||||
expect.any(Object)
|
||||
)
|
||||
secureStore.deleteItemAsync.mockClear()
|
||||
platform.OS = 'web'
|
||||
await deleteMobileRelayCredentialBundle('host-1')
|
||||
expect(secureStore.deleteItemAsync).not.toHaveBeenCalled()
|
||||
await expect(writeMobileRelayCredentialBundle(bundle)).rejects.toThrow(/native secret store/)
|
||||
await expect(readMobileRelayCredentialBundle('host-1')).rejects.toThrow(/native secret store/)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -49,9 +49,16 @@ function install(reqId: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function fakeClient(responses: RpcResponse[]): RpcClient {
|
||||
function fakeClient(responses: RpcResponse[], beforeRequest = () => {}): RpcClient {
|
||||
return {
|
||||
sendRequest: vi.fn(async () => responses.shift()!),
|
||||
sendRequest: vi.fn(async () => {
|
||||
beforeRequest()
|
||||
const response = responses.shift()
|
||||
if (!response) {
|
||||
throw new Error('unexpected credential request')
|
||||
}
|
||||
return response
|
||||
}),
|
||||
subscribe: vi.fn(() => () => {}),
|
||||
updateTerminalSubscriptionViewport: vi.fn(),
|
||||
getState: () => 'connected',
|
||||
@@ -66,16 +73,26 @@ function fakeClient(responses: RpcResponse[]): RpcClient {
|
||||
describe('mobile relay credential rotation', () => {
|
||||
it('persists pending material before install and promotes only committed status', async () => {
|
||||
const installed = install('rotate-CAgICAgICAgICAgICAgICA')
|
||||
const client = fakeClient([
|
||||
success({ v: 1, relay, installStatus: { v: 1, reqId: installed.reqId, state: 'not-found' } }),
|
||||
success(installed),
|
||||
success({
|
||||
v: 1,
|
||||
relay,
|
||||
installStatus: { v: 1, reqId: installed.reqId, state: 'committed', result: installed }
|
||||
})
|
||||
])
|
||||
const writes: MobileRelayCredentialBundle[] = []
|
||||
const client = fakeClient(
|
||||
[
|
||||
success({
|
||||
v: 1,
|
||||
relay,
|
||||
installStatus: { v: 1, reqId: installed.reqId, state: 'not-found' }
|
||||
}),
|
||||
success(installed),
|
||||
success({
|
||||
v: 1,
|
||||
relay,
|
||||
installStatus: { v: 1, reqId: installed.reqId, state: 'committed', result: installed }
|
||||
})
|
||||
],
|
||||
() => {
|
||||
expect(writes).toHaveLength(1)
|
||||
expect(writes[0]?.pending?.reqId).toBe(installed.reqId)
|
||||
}
|
||||
)
|
||||
|
||||
const result = await rotateMobileRelayCredential({
|
||||
client,
|
||||
@@ -98,7 +115,20 @@ describe('mobile relay credential rotation', () => {
|
||||
current: { version: 3, expiresAt: 100_000 },
|
||||
grace: { version: 2, expiresAt: 70_000 }
|
||||
})
|
||||
expect(result.bundle.pending).toBeUndefined()
|
||||
expect(result.bundle).toEqual({
|
||||
v: 1,
|
||||
hostId: 'host-1',
|
||||
deviceToken: 'device-token',
|
||||
current: {
|
||||
token: 'BwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwc',
|
||||
hash: '3Ev4DHdHPRMPoN6GukAY_pi7IUAF5qWJHRK6kURvnoE',
|
||||
version: 3,
|
||||
expiresAt: 100_000
|
||||
},
|
||||
grace: { ...bundle.current, expiresAt: 70_000 },
|
||||
pending: undefined
|
||||
})
|
||||
expect(writes[1]).toEqual(result.bundle)
|
||||
})
|
||||
|
||||
it('repairs legacy decoded-byte hashes before the normal rotation window', () => {
|
||||
@@ -113,7 +143,30 @@ describe('mobile relay credential rotation', () => {
|
||||
}
|
||||
|
||||
expect(mobileRelayCredentialNeedsRotation(valid, now)).toBe(false)
|
||||
expect(mobileRelayCredentialNeedsRotation(bundle, now)).toBe(true)
|
||||
expect(
|
||||
mobileRelayCredentialNeedsRotation(
|
||||
{
|
||||
...valid,
|
||||
current: { ...valid.current, expiresAt: now + 604_800_000 }
|
||||
},
|
||||
now
|
||||
)
|
||||
).toBe(true)
|
||||
expect(
|
||||
mobileRelayCredentialNeedsRotation(
|
||||
{
|
||||
...valid,
|
||||
current: { ...valid.current, expiresAt: now + 604_800_001 }
|
||||
},
|
||||
now
|
||||
)
|
||||
).toBe(false)
|
||||
expect(
|
||||
mobileRelayCredentialNeedsRotation(
|
||||
{ ...valid, current: { ...valid.current, hash: bundle.current.hash } },
|
||||
now
|
||||
)
|
||||
).toBe(true)
|
||||
expect(
|
||||
mobileRelayCredentialNeedsRotation(
|
||||
{ ...valid, pending: { token: 'C'.repeat(43), hash: 'D'.repeat(43), reqId: 'pending' } },
|
||||
@@ -137,13 +190,24 @@ describe('mobile relay credential rotation', () => {
|
||||
])
|
||||
const writeBundle = vi.fn(async () => {})
|
||||
|
||||
await rotateMobileRelayCredential({ client, bundle: pendingBundle, writeBundle })
|
||||
const result = await rotateMobileRelayCredential({ client, bundle: pendingBundle, writeBundle })
|
||||
|
||||
expect(client.sendRequest).toHaveBeenCalledOnce()
|
||||
expect(client.sendRequest).toHaveBeenCalledWith('pairing.getEndpoints', {
|
||||
installReqId: 'rotate-existing'
|
||||
})
|
||||
expect(writeBundle).toHaveBeenCalledOnce()
|
||||
expect(writeBundle).toHaveBeenCalledExactlyOnceWith({
|
||||
...bundle,
|
||||
current: { token: 'C'.repeat(43), hash: 'D'.repeat(43), version: 3, expiresAt: 100_000 },
|
||||
grace: { ...bundle.current, expiresAt: 70_000 },
|
||||
pending: undefined
|
||||
})
|
||||
expect(result.bundle.current).toEqual({
|
||||
token: 'C'.repeat(43),
|
||||
hash: 'D'.repeat(43),
|
||||
version: 3,
|
||||
expiresAt: 100_000
|
||||
})
|
||||
})
|
||||
|
||||
it('applies only authoritative current or grace confirmation expiries', () => {
|
||||
|
||||
@@ -1,49 +1,19 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { DeviceResumeConfirmed } from '../../../src/shared/mobile-relay-credential-contract'
|
||||
import type { MobileRelayCredentialBundle } from './mobile-relay-credential-bundle'
|
||||
import { hashMobileRelayCredential } from './mobile-relay-credential-hash'
|
||||
import {
|
||||
mobileRelayCredentialNeedsRotation,
|
||||
persistResumeConfirmation
|
||||
} from './mobile-relay-credential-rotation'
|
||||
import { persistResumeConfirmation } from './mobile-relay-credential-rotation'
|
||||
import { RelayLeaseRotationTimer } from './mobile-relay-lease-rotation-timer'
|
||||
|
||||
vi.mock('react-native', () => ({ Platform: { OS: 'ios' } }))
|
||||
vi.mock('expo-secure-store', () => ({ WHEN_UNLOCKED_THIS_DEVICE_ONLY: 'when-unlocked' }))
|
||||
vi.mock('expo-crypto', () => ({ getRandomBytes: (length: number) => new Uint8Array(length) }))
|
||||
|
||||
// Memory cells renew at most daily; current-version confirmations must still say renewed so shipped phones retain lease timers.
|
||||
const DAY = 24 * 60 * 60 * 1000
|
||||
const RESUME_TTL_MS = 30 * DAY
|
||||
const RENEWAL_INTERVAL_MS = DAY
|
||||
const TOKEN = 'A'.repeat(43)
|
||||
const INITIAL_EXPIRY = 2_592_001_000
|
||||
|
||||
// The memory cell's answer (cloud/apps/relay/src/credential-store.ts confirmMemoryResume).
|
||||
function memoryCellConfirm(
|
||||
stored: { expiresAt: number; renewals: number },
|
||||
now: number,
|
||||
answerNotDue: 'renewed' | 'unchanged'
|
||||
): DeviceResumeConfirmed {
|
||||
const due = stored.expiresAt < now + RESUME_TTL_MS - RENEWAL_INTERVAL_MS
|
||||
if (due) {
|
||||
stored.expiresAt = now + RESUME_TTL_MS
|
||||
stored.renewals += 1
|
||||
}
|
||||
function credentialBundle(): MobileRelayCredentialBundle {
|
||||
return {
|
||||
v: 1,
|
||||
reqId: `confirm-${now}`,
|
||||
currentVersion: 2,
|
||||
acceptedAs: 'current',
|
||||
renewed: due || answerNotDue === 'renewed',
|
||||
resumeExpiresAt: stored.expiresAt
|
||||
}
|
||||
}
|
||||
|
||||
// A phone whose only path is the relay: the lease timer's re-resume is its only renewal.
|
||||
async function relayOnlyPhone(days: number, answerNotDue: 'renewed' | 'unchanged') {
|
||||
let now = Date.parse('2026-10-19T00:00:00Z')
|
||||
const stored = { expiresAt: now + RESUME_TTL_MS, renewals: 0 }
|
||||
let bundle: MobileRelayCredentialBundle = {
|
||||
v: 1,
|
||||
hostId: 'host-1',
|
||||
deviceToken: 'device-token',
|
||||
@@ -51,63 +21,77 @@ async function relayOnlyPhone(days: number, answerNotDue: 'renewed' | 'unchanged
|
||||
token: TOKEN,
|
||||
hash: hashMobileRelayCredential(TOKEN),
|
||||
version: 2,
|
||||
expiresAt: stored.expiresAt
|
||||
expiresAt: INITIAL_EXPIRY
|
||||
}
|
||||
}
|
||||
const slot: { delayMs: number | null } = { delayMs: null }
|
||||
const timer = new RelayLeaseRotationTimer(
|
||||
{
|
||||
now: () => now,
|
||||
setTimer: (_callback, delayMs) => {
|
||||
slot.delayMs = delayMs
|
||||
return setTimeout(() => {}, 0)
|
||||
},
|
||||
clearTimer: () => {
|
||||
slot.delayMs = null
|
||||
}
|
||||
},
|
||||
() => {}
|
||||
)
|
||||
const resume = async () => {
|
||||
const confirmation = memoryCellConfirm(stored, now, answerNotDue)
|
||||
const persisted = await persistResumeConfirmation({
|
||||
session: { getResumeConfirmation: () => confirmation, getResumeExpiresAt: () => null },
|
||||
bundle,
|
||||
usedCredentialVersion: 2,
|
||||
writeBundle: async () => {}
|
||||
})
|
||||
bundle = persisted.bundle
|
||||
timer.scheduleFromLease(persisted.leaseExpiry)
|
||||
}
|
||||
await resume()
|
||||
let resumes = 1
|
||||
while (slot.delayMs !== null && now < Date.parse('2026-10-19T00:00:00Z') + days * DAY) {
|
||||
now += slot.delayMs
|
||||
timer.clear()
|
||||
await resume()
|
||||
resumes += 1
|
||||
expect(bundle.current.expiresAt).toBeGreaterThan(now)
|
||||
expect(mobileRelayCredentialNeedsRotation(bundle, now)).toBe(false)
|
||||
}
|
||||
return { now, bundle, stored, resumes, timerArmed: timer.pending }
|
||||
}
|
||||
|
||||
describe('relay renewal under the step-6 memory cell rule', () => {
|
||||
it('keeps a relay-only phone valid for 31 days, with its lease timer armed after every confirm', async () => {
|
||||
const phone = await relayOnlyPhone(31, 'renewed')
|
||||
expect(phone.timerArmed).toBe(true)
|
||||
expect(phone.resumes).toBeGreaterThanOrEqual(31 * 4)
|
||||
expect(phone.stored.renewals).toBeLessThanOrEqual(32)
|
||||
expect(phone.bundle.current.expiresAt).toBe(phone.stored.expiresAt)
|
||||
expect(phone.bundle.current.expiresAt - phone.now).toBeGreaterThan(
|
||||
RESUME_TTL_MS - 2 * RENEWAL_INTERVAL_MS
|
||||
)
|
||||
async function confirm(confirmation: DeviceResumeConfirmed) {
|
||||
const writeBundle = vi.fn(async (_bundle: MobileRelayCredentialBundle) => {})
|
||||
const persisted = await persistResumeConfirmation({
|
||||
session: { getResumeConfirmation: () => confirmation, getResumeExpiresAt: () => 9_999 },
|
||||
bundle: credentialBundle(),
|
||||
usedCredentialVersion: 2,
|
||||
writeBundle
|
||||
})
|
||||
const onRotate = vi.fn()
|
||||
const timer = new RelayLeaseRotationTimer(
|
||||
{ now: Date.now, setTimer: setTimeout, clearTimer: clearTimeout },
|
||||
onRotate
|
||||
)
|
||||
timer.scheduleFromLease(persisted.leaseExpiry)
|
||||
return { ...persisted, writeBundle, timer, onRotate }
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
describe('mobile relay resume confirmation scheduling', () => {
|
||||
it('persists a current confirmation and fires its six-hour re-resume timer', async () => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(1_000)
|
||||
const phone = await confirm({
|
||||
v: 1,
|
||||
reqId: 'confirm-current',
|
||||
currentVersion: 2,
|
||||
acceptedAs: 'current',
|
||||
renewed: true,
|
||||
resumeExpiresAt: 2_678_401_000
|
||||
})
|
||||
|
||||
expect(phone.bundle.current.expiresAt).toBe(2_678_401_000)
|
||||
expect(phone.writeBundle).toHaveBeenCalledExactlyOnceWith({
|
||||
...credentialBundle(),
|
||||
current: { ...credentialBundle().current, expiresAt: 2_678_401_000 }
|
||||
})
|
||||
expect(phone.leaseExpiry).toBe(2_678_401_000)
|
||||
expect(phone.timer.pending).toBe(true)
|
||||
await vi.advanceTimersByTimeAsync(21_599_999)
|
||||
expect(phone.onRotate).not.toHaveBeenCalled()
|
||||
await vi.advanceTimersByTimeAsync(1)
|
||||
expect(phone.onRotate).toHaveBeenCalledOnce()
|
||||
expect(phone.timer.pending).toBe(false)
|
||||
})
|
||||
|
||||
it('would strand that phone if a not-due confirm answered renewed:false', async () => {
|
||||
// An unchanged confirmation would stop this shipped phone’s lease timer immediately.
|
||||
const phone = await relayOnlyPhone(31, 'unchanged')
|
||||
expect(phone.resumes).toBe(1)
|
||||
expect(phone.timerArmed).toBe(false)
|
||||
it('keeps an unchanged confirmation durable without arming a re-resume timer', async () => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(1_000)
|
||||
const phone = await confirm({
|
||||
v: 1,
|
||||
reqId: 'confirm-unchanged',
|
||||
currentVersion: 2,
|
||||
acceptedAs: 'current',
|
||||
renewed: false,
|
||||
resumeExpiresAt: INITIAL_EXPIRY
|
||||
})
|
||||
|
||||
expect(phone.bundle).toEqual(credentialBundle())
|
||||
expect(phone.writeBundle).toHaveBeenCalledExactlyOnceWith(credentialBundle())
|
||||
expect(phone.leaseExpiry).toBeNull()
|
||||
expect(phone.timer.pending).toBe(false)
|
||||
expect(vi.getTimerCount()).toBe(0)
|
||||
await vi.advanceTimersByTimeAsync(2_678_400_000)
|
||||
expect(phone.onRotate).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -167,25 +167,27 @@ describe('the native paired E2EE codec', () => {
|
||||
})
|
||||
|
||||
it('opens and refuses exactly the frames the JS paths do, never advancing on a refusal', async () => {
|
||||
const { primitives: nativePrimitives, native } = await load(true)
|
||||
const { primitives: fallbackPrimitives, native: absent } = await load(false)
|
||||
expect(native).not.toBeNull()
|
||||
expect(absent).toBeNull()
|
||||
const originalPrimitives: PairedE2EEPrimitives = {
|
||||
...fallbackPrimitives,
|
||||
openSecretbox: undefined,
|
||||
decodeCanonicalBase64: undefined,
|
||||
decodeUtf8: undefined
|
||||
}
|
||||
const sessions = [nativePrimitives, fallbackPrimitives, originalPrimitives].map((primitives) =>
|
||||
pairedSession(primitives)
|
||||
)
|
||||
const wire = inboundWire(sessions[0]!.schedule)
|
||||
const wire = inboundWire(pairedSession((await load(true)).primitives).schedule)
|
||||
expect(wire.filter((entry) => entry.plaintext === null).length).toBeGreaterThan(50)
|
||||
for (const { session } of sessions) {
|
||||
for (const mode of ['native', 'fallback', 'original']) {
|
||||
const { primitives, native } = await load(mode === 'native')
|
||||
expect(native === null).toBe(mode !== 'native')
|
||||
const originalPrimitives: PairedE2EEPrimitives = {
|
||||
...primitives,
|
||||
openSecretbox: undefined,
|
||||
decodeCanonicalBase64: undefined,
|
||||
decodeUtf8: undefined
|
||||
}
|
||||
const { session } = pairedSession(mode === 'original' ? originalPrimitives : primitives)
|
||||
secretboxOpenCalls.mockClear()
|
||||
expect(wire.map(({ frame }) => session.openText(frame))).toEqual(
|
||||
wire.map(({ plaintext }) => plaintext)
|
||||
)
|
||||
if (mode === 'native') {
|
||||
expect(secretboxOpenCalls).toHaveBeenCalled()
|
||||
} else {
|
||||
expect(secretboxOpenCalls).not.toHaveBeenCalled()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -221,10 +223,11 @@ describe('the native paired E2EE codec', () => {
|
||||
|
||||
// UTF-8 fallback must preserve an already-opened frame whose counter advanced.
|
||||
it('falls back to TextDecoder when native UTF-8 throws', async () => {
|
||||
const { primitives } = await load(true)
|
||||
bindings!.codec.decodeUtf8 = () => {
|
||||
const { primitives, native } = await load(true)
|
||||
expect(native).not.toBeNull()
|
||||
const decodeUtf8 = vi.spyOn(bindings!.codec, 'decodeUtf8').mockImplementation(() => {
|
||||
throw new Error('native UTF-8 failed')
|
||||
}
|
||||
})
|
||||
const { session, schedule } = pairedSession(primitives)
|
||||
const frames = ['{"a":1}', 'é✓🙂'].map((payload, counter) =>
|
||||
b64(
|
||||
@@ -239,6 +242,7 @@ describe('the native paired E2EE codec', () => {
|
||||
)
|
||||
)
|
||||
expect(frames.map((frame) => session.openText(frame))).toEqual(['{"a":1}', 'é✓🙂'])
|
||||
expect(decodeUtf8).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
// libsodium sizes its output as the box length minus the MAC, which underflows and crashes.
|
||||
|
||||
@@ -8,8 +8,6 @@ import { OrcaRuntimeService } from './orca-runtime'
|
||||
import { TUI_IDLE_QUIESCENCE_MS } from './orca-runtime-postlude'
|
||||
import { makeStore } from './runtime-rpc-worktree-store-fixtures'
|
||||
import { settledWriteStub } from '../providers/settled-pty-write-stub'
|
||||
import type { RuntimeTerminalWriter } from './runtime-terminal-writer'
|
||||
import type { RuntimePtyController } from './runtime-pty-controller-contract'
|
||||
|
||||
const createPromptRuntime = createAgentPromptSubmissionRuntime
|
||||
|
||||
@@ -590,75 +588,12 @@ function inputDispatchRuntime() {
|
||||
return result
|
||||
}
|
||||
|
||||
describe('terminal input dispatch through the current runtime', () => {
|
||||
it.each(['pty', 'leaf'])('uses the runtime writer override in the %s lane', async (lane) => {
|
||||
const target = inputDispatchRuntime()
|
||||
if (lane === 'leaf') {
|
||||
Object.defineProperties(target, {
|
||||
getLivePtyForHandle: { value: () => null },
|
||||
getLiveLeafForHandle: { value: () => ({ leaf: { writable: true, ptyId: PTY } }) },
|
||||
isLeafPtyProvenAbsent: { value: async () => false }
|
||||
})
|
||||
}
|
||||
const writer = vi.fn(function (
|
||||
this: OrcaRuntimeService,
|
||||
...args: Parameters<RuntimeTerminalWriter['writeAction']>
|
||||
) {
|
||||
expect(this).toBe(target)
|
||||
expect(args).toHaveLength(4)
|
||||
expect(args[0]).toBe(PTY)
|
||||
return Promise.resolve()
|
||||
})
|
||||
Object.defineProperty(target, 'writeTerminalAction', { value: writer })
|
||||
|
||||
await expect(
|
||||
target.sendTerminal(HANDLE, { text: 'hello' }, { inputKind: 'driving' })
|
||||
).resolves.toMatchObject({ accepted: true, bytesWritten: 5 })
|
||||
expect(writer).toHaveBeenCalledExactlyOnceWith(PTY, { text: 'hello' }, 'hello', {
|
||||
inputKind: 'driving'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('input original queue, callback and private method contracts', () => {
|
||||
describe('agent prompt submission queue', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
it('keeps the private retry lookup on the Root receiver before any delay', async () => {
|
||||
const target = inputDispatchRuntime()
|
||||
const read = vi.fn(function (this: OrcaRuntimeService, ...args: unknown[]) {
|
||||
expect(this).toBe(target)
|
||||
expect(args).toEqual([PTY])
|
||||
return null
|
||||
})
|
||||
Object.defineProperty(target, 'getPtyAgent', { value: read })
|
||||
await expect(
|
||||
target['resubmitHeldAgentPrompt'](
|
||||
PTY,
|
||||
1,
|
||||
{ inputKind: 'driving' },
|
||||
{
|
||||
baseline: {
|
||||
generation: 1,
|
||||
permissionSequence: 0,
|
||||
workingSequence: 0,
|
||||
explicitWorkingStartedAt: null,
|
||||
outputSequence: 0,
|
||||
status: null
|
||||
},
|
||||
readActivity: () => {
|
||||
throw new Error('no read before the agent lookup')
|
||||
},
|
||||
prompt: undefined,
|
||||
heldBudgetMs: 0
|
||||
}
|
||||
)
|
||||
).resolves.toBe(0)
|
||||
expect(read).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it('serializes rejected submissions, isolates generations and keeps a replacement tail', async () => {
|
||||
const target = inputDispatchRuntime()
|
||||
const gate = Promise.withResolvers<void>()
|
||||
@@ -689,79 +624,4 @@ describe('input original queue, callback and private method contracts', () => {
|
||||
expect(target['agentPromptSubmissionTailByPtyId'].get(key)).toBe(replacement)
|
||||
expect(target['agentPromptSubmissionTailByPtyId'].has(`${PTY}\u00002`)).toBe(false)
|
||||
})
|
||||
|
||||
it('keeps hook presence callback receivers and preserves invalidation failure', async () => {
|
||||
const target = inputDispatchRuntime()
|
||||
const check = vi.fn(async function (this: OrcaRuntimeService, ...args: unknown[]) {
|
||||
expect(this).toBe(target)
|
||||
expect(args).toHaveLength(1)
|
||||
return 'live' as const
|
||||
})
|
||||
Object.defineProperty(target, 'checkHookAgentPresenceFn', {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: check
|
||||
})
|
||||
Object.defineProperty(target, 'collectAgentStatusPaneKeysForPty', {
|
||||
configurable: true,
|
||||
value: () => new Set(['pane-first', 'pane-second'])
|
||||
})
|
||||
await expect(target['recheckHookAgentPresenceForPty'](PTY)).resolves.toBe('live')
|
||||
expect(check.mock.calls).toEqual([['pane-first'], ['pane-second']])
|
||||
Object.defineProperty(target, 'collectAgentStatusPaneKeysForPty', {
|
||||
value: () => {
|
||||
Object.defineProperty(target, 'checkHookAgentPresenceFn', { value: null })
|
||||
return new Set(['pane-first'])
|
||||
}
|
||||
})
|
||||
await expect(target['recheckHookAgentPresenceForPty'](PTY)).rejects.toBeInstanceOf(TypeError)
|
||||
expect(check).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ recover: false, hasHandle: false },
|
||||
{ recover: true, hasHandle: false },
|
||||
{ recover: true, hasHandle: true }
|
||||
])('keeps captured recovery handle semantics for %j', async ({ recover, hasHandle }) => {
|
||||
const target = inputDispatchRuntime()
|
||||
const pty = target['ptysById'].get(PTY)
|
||||
if (!pty) {
|
||||
throw new Error('registered fixture PTY missing')
|
||||
}
|
||||
pty.lastOscTitleAt = 0
|
||||
pty.lastAgentStatusObservedLive = true
|
||||
if (!hasHandle) {
|
||||
target['handleByPtyId'].delete(PTY)
|
||||
}
|
||||
const foreground = Promise.withResolvers<{
|
||||
controller: RuntimePtyController | null
|
||||
available: boolean
|
||||
process: string | null
|
||||
}>()
|
||||
const reads = vi.fn(() => foreground.promise)
|
||||
const explicit = vi.fn(() => ({ status: 'idle' as const, updatedAt: 0, stateStartedAt: 0 }))
|
||||
Object.defineProperties(target, {
|
||||
readPtyForegroundProcessFromController: { value: reads },
|
||||
getFreshExplicitAgentStatusForPty: { value: explicit },
|
||||
recordTerminalSideEffectFact: { value: vi.fn() }
|
||||
})
|
||||
target['confirmLegacyPtyAgentExit'](PTY, recover, false)
|
||||
target['handleByPtyId'].set(PTY, 'late-handle')
|
||||
foreground.resolve({ controller: target['ptyController'], available: false, process: null })
|
||||
await Promise.resolve()
|
||||
if (recover && !hasHandle) {
|
||||
expect(reads).not.toHaveBeenCalled()
|
||||
expect(explicit).not.toHaveBeenCalled()
|
||||
} else {
|
||||
expect(reads).toHaveBeenCalledOnce()
|
||||
expect(explicit.mock.calls).toEqual(
|
||||
recover
|
||||
? [
|
||||
[HANDLE, PTY],
|
||||
[HANDLE, PTY]
|
||||
]
|
||||
: []
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -87,18 +87,15 @@ describe('uploadSkillPackageToSignedPolicy', () => {
|
||||
const archive = Buffer.from('private-package-bytes')
|
||||
await writeFile(archivePath, archive)
|
||||
let uploaded = Buffer.alloc(0)
|
||||
const fetcher = vi.fn(async (_url: URL | RequestInfo, init?: RequestInit) => {
|
||||
const chunks: Buffer[] = []
|
||||
const body = init?.body as unknown as AsyncIterable<Buffer>
|
||||
for await (const chunk of body) {
|
||||
chunks.push(Buffer.from(chunk))
|
||||
}
|
||||
uploaded = Buffer.concat(chunks)
|
||||
let contentType = ''
|
||||
const fetcher = vi.fn<typeof fetch>(async (_url, init) => {
|
||||
uploaded = Buffer.from(await new Response(init?.body).arrayBuffer())
|
||||
contentType = new Headers(init?.headers).get('content-type') ?? ''
|
||||
expect(Number(init?.headers && new Headers(init.headers).get('content-length'))).toBe(
|
||||
uploaded.length
|
||||
)
|
||||
return new Response(null, { status: 204 })
|
||||
}) as typeof fetch
|
||||
})
|
||||
const progress = vi.fn()
|
||||
|
||||
await uploadSkillPackageToSignedPolicy({
|
||||
@@ -112,9 +109,28 @@ describe('uploadSkillPackageToSignedPolicy', () => {
|
||||
onProgress: progress
|
||||
})
|
||||
|
||||
expect(uploaded.includes(archive)).toBe(true)
|
||||
expect(uploaded.toString('utf8')).toContain('opaque-policy')
|
||||
expect(progress).toHaveBeenLastCalledWith(archive.length)
|
||||
const form = await new Response(uploaded, {
|
||||
headers: { 'content-type': contentType }
|
||||
}).formData()
|
||||
expect(form.get('key')).toBe('uploads/private/package.tar.gz')
|
||||
expect(form.get('policy')).toBe('opaque-policy')
|
||||
const file = form.get('file')
|
||||
expect(file).toBeInstanceOf(File)
|
||||
if (!(file instanceof File)) {
|
||||
throw new Error('Expected uploaded package file')
|
||||
}
|
||||
expect(Buffer.from(await file.arrayBuffer())).toEqual(archive)
|
||||
expect(file.name).toBe('package.tar.gz')
|
||||
expect(uploaded.toString('utf8')).toContain(
|
||||
'Content-Disposition: form-data; name="file"; filename="package.tar.gz"\r\nContent-Type: application/vnd.orca.skill+tar+gzip\r\n\r\n'
|
||||
)
|
||||
const sent = progress.mock.calls.map(([bytes]) => bytes)
|
||||
expect(sent).not.toHaveLength(0)
|
||||
for (const [index, bytes] of sent.entries()) {
|
||||
expect(bytes).toBeGreaterThanOrEqual(index === 0 ? 0 : sent[index - 1])
|
||||
expect(bytes).toBeLessThanOrEqual(archive.length)
|
||||
}
|
||||
expect(sent.at(-1)).toBe(archive.length)
|
||||
})
|
||||
|
||||
it('rejects insecure destinations and source drift before upload', async () => {
|
||||
@@ -150,16 +166,22 @@ describe('uploadSkillPackageToSignedPolicy', () => {
|
||||
})
|
||||
|
||||
it('bounds a stalled signed upload even without a caller-owned signal', async () => {
|
||||
vi.useFakeTimers()
|
||||
const root = await mkdtemp(join(tmpdir(), 'orca-skill-cloud-upload-'))
|
||||
roots.push(root)
|
||||
const archivePath = join(root, 'package.tar.gz')
|
||||
await writeFile(archivePath, 'bytes')
|
||||
const fetcher = vi.fn(
|
||||
async (_url: URL | RequestInfo, init?: RequestInit) =>
|
||||
let markStarted!: () => void
|
||||
const started = new Promise<void>((resolve) => {
|
||||
markStarted = resolve
|
||||
})
|
||||
const fetcher = vi.fn<typeof fetch>(
|
||||
async (_url, init) =>
|
||||
await new Promise<Response>((_resolve, reject) => {
|
||||
init?.signal?.addEventListener('abort', () => reject(init.signal?.reason), { once: true })
|
||||
markStarted()
|
||||
})
|
||||
) as typeof fetch
|
||||
)
|
||||
|
||||
const upload = uploadSkillPackageToSignedPolicy({
|
||||
policy: { url: 'https://storage.googleapis.com/upload', fields: {} },
|
||||
@@ -168,9 +190,11 @@ describe('uploadSkillPackageToSignedPolicy', () => {
|
||||
fetcher,
|
||||
timeoutMs: 25
|
||||
})
|
||||
const startedAt = Date.now()
|
||||
|
||||
await expect(upload).rejects.toThrow('skill-cloud-upload-timeout')
|
||||
expect(Date.now() - startedAt).toBeLessThan(1_000)
|
||||
const expectation = expect(upload).rejects.toThrow('skill-cloud-upload-timeout')
|
||||
await started
|
||||
await vi.advanceTimersByTimeAsync(24)
|
||||
expect(fetcher.mock.calls[0]?.[1]?.signal?.aborted).toBe(false)
|
||||
await vi.advanceTimersByTimeAsync(1)
|
||||
await expectation
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { SkillCloudDownloadGrant } from '../../shared/skill-cloud-contract'
|
||||
import {
|
||||
computeSkillBundleDigest,
|
||||
parseSkillBundleManifest
|
||||
} from '../../shared/skill-bundle-manifest'
|
||||
import {
|
||||
computeSkillPackageDigest,
|
||||
parseSkillPackageManifest
|
||||
} from '../../shared/skill-package-manifest'
|
||||
import type { OrcaRuntimeService } from '../runtime/orca-runtime'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
@@ -24,7 +33,20 @@ import {
|
||||
installSkillCloudGrant
|
||||
} from './skill-cloud-grant-installation'
|
||||
|
||||
const grant = {
|
||||
const markdown = '# Private skill'
|
||||
const fileDigest = createHash('sha256').update(markdown).digest('hex')
|
||||
const files = [
|
||||
{
|
||||
path: 'SKILL.md',
|
||||
size: Buffer.byteLength(markdown),
|
||||
executable: false,
|
||||
classification: 'text' as const,
|
||||
sha256: fileDigest,
|
||||
identitySha256: fileDigest
|
||||
}
|
||||
]
|
||||
const packageDigest = computeSkillPackageDigest(files)
|
||||
const grant: SkillCloudDownloadGrant = {
|
||||
grant: {
|
||||
url: 'https://storage.googleapis.com/bucket/package.tar.gz',
|
||||
expiresAt: '2099-01-01T00:00:00.000Z'
|
||||
@@ -33,11 +55,53 @@ const grant = {
|
||||
packageId: 'package-1',
|
||||
versionId: 'version-1',
|
||||
name: 'private-skill',
|
||||
packageDigest: 'a'.repeat(64),
|
||||
description: '',
|
||||
releaseNotes: '',
|
||||
createdAt: '2026-08-11T00:00:00.000Z',
|
||||
packageDigest,
|
||||
archiveSha256: 'b'.repeat(64),
|
||||
compressedBytes: 12
|
||||
compressedBytes: 12,
|
||||
manifest: parseSkillPackageManifest({
|
||||
schemaVersion: 1,
|
||||
packageId: 'package-1',
|
||||
versionId: 'version-1',
|
||||
name: 'private-skill',
|
||||
description: '',
|
||||
createdAt: '2026-08-11T00:00:00.000Z',
|
||||
packageDigest,
|
||||
files
|
||||
})
|
||||
}
|
||||
} as unknown as SkillCloudDownloadGrant
|
||||
}
|
||||
|
||||
function bundleDownloadGrant(ids: string[]): SkillCloudDownloadGrant {
|
||||
const skills = ids.map((id, index) => ({
|
||||
id,
|
||||
name: `skill-${String(index).padStart(3, '0')}`,
|
||||
description: '',
|
||||
digest: packageDigest,
|
||||
files
|
||||
}))
|
||||
const manifest = parseSkillBundleManifest({
|
||||
schemaVersion: 1,
|
||||
packageId: 'package-1',
|
||||
versionId: 'version-1',
|
||||
bundleName: 'private-bundle',
|
||||
description: '',
|
||||
createdAt: '2026-08-11T00:00:00.000Z',
|
||||
bundleDigest: computeSkillBundleDigest(skills),
|
||||
skills
|
||||
})
|
||||
return {
|
||||
...grant,
|
||||
version: {
|
||||
...grant.version,
|
||||
name: 'private-bundle',
|
||||
packageDigest: manifest.bundleDigest,
|
||||
manifest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe('installSkillCloudGrant', () => {
|
||||
beforeEach(() => {
|
||||
@@ -52,7 +116,7 @@ describe('installSkillCloudGrant', () => {
|
||||
operationId: 'operation-1',
|
||||
status: 'installed',
|
||||
name: 'private-skill',
|
||||
packageDigest: 'a'.repeat(64),
|
||||
packageDigest,
|
||||
placements: []
|
||||
}
|
||||
mocks.getRuntimeEnvironmentStatus.mockResolvedValue({
|
||||
@@ -79,36 +143,12 @@ describe('installSkillCloudGrant', () => {
|
||||
})
|
||||
|
||||
it('installs selected bundle skills through a capable remote runtime', async () => {
|
||||
const bundleGrant = {
|
||||
...grant,
|
||||
version: {
|
||||
...grant.version,
|
||||
name: 'private-bundle',
|
||||
manifest: {
|
||||
schemaVersion: 1,
|
||||
packageId: 'package-1',
|
||||
versionId: 'version-1',
|
||||
bundleName: 'private-bundle',
|
||||
description: '',
|
||||
createdAt: '2026-08-11T00:00:00.000Z',
|
||||
bundleDigest: 'c'.repeat(64),
|
||||
skills: [
|
||||
{
|
||||
id: 'skill-1',
|
||||
name: 'private-skill',
|
||||
description: '',
|
||||
digest: 'a'.repeat(64),
|
||||
files: []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
} as unknown as SkillCloudDownloadGrant
|
||||
const bundleGrant = bundleDownloadGrant(['skill-1'])
|
||||
const result = {
|
||||
operationId: 'operation-1',
|
||||
packageId: 'package-1',
|
||||
versionId: 'version-1',
|
||||
bundleDigest: 'c'.repeat(64),
|
||||
bundleDigest: bundleGrant.version.packageDigest,
|
||||
status: 'complete',
|
||||
skills: []
|
||||
}
|
||||
@@ -134,18 +174,8 @@ describe('installSkillCloudGrant', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// Set membership must retain manifest order and duplicates in user-visible failure reports.
|
||||
it('reports selected manifest entries in manifest order, duplicates and all', async () => {
|
||||
const skills = ['b', 'a', 'dupe', 'dupe', 'unselected'].map((id) => ({
|
||||
id,
|
||||
name: `name-${id}`,
|
||||
digest: 'a'.repeat(64),
|
||||
files: []
|
||||
}))
|
||||
const bundleGrant = {
|
||||
...grant,
|
||||
version: { ...grant.version, manifest: { skills, bundleDigest: 'c'.repeat(64) } }
|
||||
} as unknown as SkillCloudDownloadGrant
|
||||
it('reports only selected skills in manifest order', async () => {
|
||||
const bundleGrant = bundleDownloadGrant(['b', 'a', 'unselected'])
|
||||
const runtime = {
|
||||
installSharedSkillBundleRequest: vi
|
||||
.fn()
|
||||
@@ -153,33 +183,13 @@ it('reports selected manifest entries in manifest order, duplicates and all', as
|
||||
} as unknown as OrcaRuntimeService
|
||||
const result = await installSkillBundleCloudGrant(runtime, bundleGrant, {
|
||||
operationId: 'op',
|
||||
// Repeated/unknown selection ids must not change manifest membership.
|
||||
selectedSkillIds: ['dupe', 'a', 'a', 'b', 'never-in-manifest'],
|
||||
selectedSkillIds: ['a', 'b'],
|
||||
destination: { scope: 'global' }
|
||||
})
|
||||
expect(result.status).toBe('ok')
|
||||
if (result.status === 'ok') {
|
||||
expect(result.value.skills.map((skill) => skill.skillId)).toEqual(['b', 'a', 'dupe', 'dupe'])
|
||||
}
|
||||
})
|
||||
|
||||
it('reports no skills when nothing was selected', async () => {
|
||||
const skills = [{ id: 'a', name: 'a', digest: 'a'.repeat(64), files: [] }]
|
||||
const bundleGrant = {
|
||||
...grant,
|
||||
version: { ...grant.version, manifest: { skills, bundleDigest: 'c'.repeat(64) } }
|
||||
} as unknown as SkillCloudDownloadGrant
|
||||
const runtime = {
|
||||
installSharedSkillBundleRequest: vi.fn().mockRejectedValue(new Error('skill-install-cancelled'))
|
||||
} as unknown as OrcaRuntimeService
|
||||
const result = await installSkillBundleCloudGrant(runtime, bundleGrant, {
|
||||
operationId: 'op',
|
||||
selectedSkillIds: [],
|
||||
destination: { scope: 'global' }
|
||||
})
|
||||
expect(result.status).toBe('ok')
|
||||
if (result.status === 'ok') {
|
||||
expect(result.value.skills).toEqual([])
|
||||
expect(result.value.skills.map((skill) => skill.skillId)).toEqual(['b', 'a'])
|
||||
expect(result.value.status).toBe('failed')
|
||||
}
|
||||
})
|
||||
|
||||
@@ -187,7 +197,7 @@ it.each(['skill-install-cancelled', 'skill-install-filesystem-failed'])(
|
||||
'indexes selected IDs when reporting %s',
|
||||
async (code) => {
|
||||
let reads = 0
|
||||
const ids = Array.from({ length: 1000 }, (_, index) => `skill-${index}`)
|
||||
const ids = Array.from({ length: 512 }, (_, index) => `skill-${index}`)
|
||||
const selectedSkillIds = new Proxy(ids, {
|
||||
get(target, key, receiver) {
|
||||
if (typeof key === 'string' && /^\d+$/.test(key)) {
|
||||
@@ -197,11 +207,7 @@ it.each(['skill-install-cancelled', 'skill-install-filesystem-failed'])(
|
||||
return Reflect.get(target, key, receiver)
|
||||
}
|
||||
})
|
||||
const skills = ids.map((id) => ({ id, name: id, digest: 'a'.repeat(64), files: [] }))
|
||||
const bundleGrant = {
|
||||
...grant,
|
||||
version: { ...grant.version, manifest: { skills, bundleDigest: 'c'.repeat(64) } }
|
||||
} as unknown as SkillCloudDownloadGrant
|
||||
const bundleGrant = bundleDownloadGrant(ids)
|
||||
const runtime = {
|
||||
installSharedSkillBundleRequest: vi.fn().mockRejectedValue(new Error(code))
|
||||
} as unknown as OrcaRuntimeService
|
||||
@@ -215,6 +221,6 @@ it.each(['skill-install-cancelled', 'skill-install-filesystem-failed'])(
|
||||
expect(result.value.skills.map((skill) => skill.skillId)).toEqual(ids)
|
||||
expect(result.value.status).toBe(code.includes('cancelled') ? 'cancelled' : 'failed')
|
||||
}
|
||||
expect(reads).toBeLessThanOrEqual(2000)
|
||||
expect(reads).toBeLessThanOrEqual(2 * ids.length)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -24,6 +24,9 @@ describe('classifySkillCloudInstallTarget', () => {
|
||||
})
|
||||
|
||||
it('keeps native and WSL installs in the local grant lane', async () => {
|
||||
await expect(
|
||||
classifySkillCloudInstallTarget(runtime(false), { destination: { scope: 'global' } })
|
||||
).resolves.toBe('local')
|
||||
await expect(
|
||||
classifySkillCloudInstallTarget(runtime(false), {
|
||||
destination: { scope: 'global', executionTarget: { kind: 'wsl', distro: 'Ubuntu' } }
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { SkillCloudVersion } from '../../shared/skill-cloud-contract'
|
||||
import {
|
||||
computeSkillPackageDigest,
|
||||
parseSkillPackageManifest
|
||||
} from '../../shared/skill-package-manifest'
|
||||
import { SkillCloudService } from './skill-cloud-service'
|
||||
|
||||
const { packaged } = vi.hoisted(() => ({ packaged: { value: false } }))
|
||||
@@ -32,26 +37,38 @@ function userDataPath(): string {
|
||||
}
|
||||
|
||||
function publishedVersion(archiveSha256: string, compressedBytes: number): SkillCloudVersion {
|
||||
const digest = createHash('sha256').update('# Retry skill').digest('hex')
|
||||
const files = [
|
||||
{
|
||||
path: 'SKILL.md',
|
||||
size: Buffer.byteLength('# Retry skill'),
|
||||
executable: false,
|
||||
classification: 'text' as const,
|
||||
sha256: digest,
|
||||
identitySha256: digest
|
||||
}
|
||||
]
|
||||
const packageDigest = computeSkillPackageDigest(files)
|
||||
return {
|
||||
packageId: 'pkg_retry',
|
||||
versionId: 'ver_retry',
|
||||
name: 'retry-skill',
|
||||
description: 'Retry publication',
|
||||
packageDigest: 'a'.repeat(64),
|
||||
packageDigest,
|
||||
archiveSha256,
|
||||
compressedBytes,
|
||||
createdAt: '2026-08-12T12:00:00.000Z',
|
||||
releaseNotes: 'retry',
|
||||
manifest: {
|
||||
manifest: parseSkillPackageManifest({
|
||||
schemaVersion: 1,
|
||||
packageId: 'pkg_retry',
|
||||
versionId: 'ver_retry',
|
||||
name: 'retry-skill',
|
||||
description: 'Retry publication',
|
||||
createdAt: '2026-08-12T12:00:00.000Z',
|
||||
packageDigest: 'a'.repeat(64),
|
||||
files: []
|
||||
}
|
||||
packageDigest,
|
||||
files
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,24 +86,31 @@ function publishRequest(archivePath: string, archiveSha256: string, compressedBy
|
||||
describe('SkillCloudService bearer links', () => {
|
||||
it('resolves and grants downloads without an Orca session', async () => {
|
||||
const requests: RequestInit[] = []
|
||||
const version = publishedVersion('a'.repeat(64), 12)
|
||||
const grant = {
|
||||
grant: { url: 'https://storage.test/package', expiresAt: '2026-08-11T00:05:00Z' },
|
||||
version
|
||||
}
|
||||
const share = { id: 'share_1', version }
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn(async (input: URL | RequestInfo, init?: RequestInit) => {
|
||||
requests.push(init ?? {})
|
||||
return String(input).endsWith('/download-grants')
|
||||
? Response.json({
|
||||
grant: { url: 'https://storage.test/package', expiresAt: '2026-08-11T00:05:00Z' },
|
||||
version: { versionId: 'ver_1' }
|
||||
})
|
||||
: Response.json({ share: { id: 'share_1', version: { versionId: 'ver_1' } } })
|
||||
? Response.json(grant)
|
||||
: Response.json({ share })
|
||||
})
|
||||
)
|
||||
const service = new SkillCloudService('/unused')
|
||||
const options = { apiUrl: 'http://127.0.0.1:8787' }
|
||||
|
||||
await expect(service.resolveShare('share_1', options)).resolves.toMatchObject({ status: 'ok' })
|
||||
await expect(service.createDownloadGrant('share_1', options)).resolves.toMatchObject({
|
||||
status: 'ok'
|
||||
await expect(service.resolveShare('share_1', options)).resolves.toEqual({
|
||||
status: 'ok',
|
||||
value: share
|
||||
})
|
||||
await expect(service.createDownloadGrant('share_1', options)).resolves.toEqual({
|
||||
status: 'ok',
|
||||
value: grant
|
||||
})
|
||||
|
||||
expect(requests).toHaveLength(2)
|
||||
@@ -129,7 +153,7 @@ describe('SkillCloudService publication retries', () => {
|
||||
const root = userDataPath()
|
||||
const archivePath = join(root, 'package.tar.gz')
|
||||
const archive = Buffer.from('skill archive')
|
||||
const archiveSha256 = 'b'.repeat(64)
|
||||
const archiveSha256 = createHash('sha256').update(archive).digest('hex')
|
||||
const version = publishedVersion(archiveSha256, archive.byteLength)
|
||||
writeFileSync(archivePath, archive)
|
||||
let createAttempts = 0
|
||||
@@ -182,7 +206,7 @@ describe('SkillCloudService publication retries', () => {
|
||||
const root = userDataPath()
|
||||
const archivePath = join(root, 'package.tar.gz')
|
||||
const archive = Buffer.from('skill archive')
|
||||
const archiveSha256 = 'c'.repeat(64)
|
||||
const archiveSha256 = createHash('sha256').update(archive).digest('hex')
|
||||
const version = publishedVersion(archiveSha256, archive.byteLength)
|
||||
writeFileSync(archivePath, archive)
|
||||
let createAttempts = 0
|
||||
|
||||
@@ -8,14 +8,12 @@ import {
|
||||
} from './native-chat-tool-identity'
|
||||
|
||||
describe('MCP tool identity', () => {
|
||||
it.each(['mcp__linear__list_issues'])(
|
||||
'splits %s for display without changing the identifier',
|
||||
(name) => {
|
||||
const raw = name
|
||||
expect(mcpToolIdentity(name)).toEqual({ server: 'Linear', tool: 'list issues' })
|
||||
expect(name).toBe(raw)
|
||||
}
|
||||
)
|
||||
it('splits a qualified MCP name into display labels', () => {
|
||||
expect(mcpToolIdentity('mcp__linear__list_issues')).toEqual({
|
||||
server: 'Linear',
|
||||
tool: 'list issues'
|
||||
})
|
||||
})
|
||||
it.each([
|
||||
'linear/list_issues',
|
||||
'linear.list_issues',
|
||||
|
||||
@@ -5,12 +5,22 @@ import { nativeChatToolLineIdentity } from './native-chat-tool-line-identity'
|
||||
it('keys an anonymous mobile row by a bounded summary, so a refetched copy keeps it', () => {
|
||||
const input = {
|
||||
command: 'ls',
|
||||
toJSON: () => {
|
||||
throw new Error('Input must stay unread.')
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
d: 4,
|
||||
e: 5,
|
||||
f: 6,
|
||||
g: 7,
|
||||
get unread() {
|
||||
throw new Error('Input beyond the preview budget must stay unread.')
|
||||
}
|
||||
}
|
||||
const block: NativeChatToolCallBlock = { type: 'tool-call', name: 'Bash', input }
|
||||
const original = nativeChatToolLineIdentity(block, new Map(), true)
|
||||
expect(original).toBe(
|
||||
'tool-call:Bash:{"command":"ls","a":1,"b":2,"c":3,"d":4,"e":5,"f":6,"g":7,"…":"…"}:0'
|
||||
)
|
||||
expect(nativeChatToolLineIdentity({ ...block }, new Map(), true)).toBe(original)
|
||||
const seen = new Map<string, number>()
|
||||
expect(nativeChatToolLineIdentity(block, seen, true)).not.toBe(
|
||||
|
||||
@@ -20,7 +20,7 @@ describe('nativeChatToolLineLabel', () => {
|
||||
expect(label.filePath).toBe('2 files')
|
||||
})
|
||||
|
||||
it('links a read or an edit, never a listed folder', () => {
|
||||
it('links a read, never a listed folder', () => {
|
||||
const read = nativeChatToolLineLabel({
|
||||
type: 'tool-call',
|
||||
name: 'Read',
|
||||
|
||||
Reference in New Issue
Block a user