Merge remote-tracking branch 'origin/master' into dshw/pr-2250
This commit is contained in:
@@ -31,6 +31,10 @@ async function mount(): Promise<Bench> {
|
||||
const handle: ConnectionHandle = {
|
||||
api,
|
||||
isLoopback: true,
|
||||
hostDescription: {
|
||||
getSnapshot: () => undefined,
|
||||
subscribe: () => () => {},
|
||||
},
|
||||
rpc: {
|
||||
call: () => Promise.reject(new Error('unexpected generic RPC call')),
|
||||
},
|
||||
@@ -86,7 +90,7 @@ describe('runtime client apply', () => {
|
||||
expect(workspaces.list.getSnapshot().items[0]?.workspaceId).toBe('w-new')
|
||||
// Mux sink and onConnected route without throwing (manager semantics own the behavior).
|
||||
bench.sinks?.onMuxEnvelope?.({ rpcId: 'r2' as never, payload: { type: 'stream/error', message: 'x' } as never })
|
||||
bench.sinks?.onConnected?.()
|
||||
bench.sinks?.onConnected?.({ version: '0', cwd: '/f', attachedSessions: 0, canOpenPath: true })
|
||||
})
|
||||
|
||||
it('selects the recent Workspace once when the first baselines have no current session', async () => {
|
||||
@@ -99,7 +103,7 @@ describe('runtime client apply', () => {
|
||||
}))
|
||||
bench.api.onList = () => Promise.resolve(ok({ items: [] }))
|
||||
|
||||
bench.sinks?.onConnected?.()
|
||||
bench.sinks?.onConnected?.({ version: '0', cwd: '/f', attachedSessions: 0, canOpenPath: true })
|
||||
await flushMicrotasks()
|
||||
|
||||
const sessions = bench.ctx.get('sessions') as SessionsService
|
||||
|
||||
@@ -90,8 +90,15 @@ export class FakeApiClient implements IApiClient {
|
||||
onUpdateQueue: (payload: unknown) => Promise<RpcResponse<{ accepted: true }>> = () => Promise.resolve(ok({ accepted: true as const }))
|
||||
onCancel: (payload: unknown) => Promise<RpcResponse<{ accepted: true }>> = () => Promise.resolve(ok({ accepted: true as const }))
|
||||
|
||||
onDescribe: (payload: unknown) => Promise<RpcResponse<{ version: string; cwd: string; attachedSessions: number }>> =
|
||||
() => Promise.resolve(ok({ version: '0-fake', cwd: '/f', attachedSessions: 0 }))
|
||||
onDescribe: (payload: unknown) => Promise<RpcResponse<{
|
||||
version: string
|
||||
cwd: string
|
||||
attachedSessions: number
|
||||
canOpenPath: boolean
|
||||
}>> =
|
||||
() => Promise.resolve(ok({
|
||||
version: '0-fake', cwd: '/f', attachedSessions: 0, canOpenPath: true,
|
||||
}))
|
||||
onPickDirectory: (payload: unknown) => Promise<RpcResponse<{ path: string | null }>> =
|
||||
() => Promise.resolve(ok({ path: null }))
|
||||
onOpenPath: (payload: unknown) => Promise<RpcResponse<{ opened: true }>> =
|
||||
|
||||
@@ -374,6 +374,7 @@ describe('subagent catalogs', () => {
|
||||
{
|
||||
parentSessionId: S1, childSessionId: S2, mode: 'continuable',
|
||||
content: [{ type: 'text', text: 'continue' }],
|
||||
clientTimeZone: new Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
},
|
||||
])
|
||||
expect(api.callsOf('session.history')).toEqual([])
|
||||
|
||||
@@ -465,6 +465,7 @@ describe('prompt and cancel errors', () => {
|
||||
{
|
||||
parentSessionId: PARENT, childSessionId: SID, mode: 'continuable',
|
||||
content: [{ type: 'text', text: '继续' }],
|
||||
clientTimeZone: new Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
},
|
||||
])
|
||||
expect(api.callsOf('subagent.interrupt')).toEqual([
|
||||
@@ -530,7 +531,12 @@ describe('prompt and cancel errors', () => {
|
||||
expect(result.ok).toBe(true)
|
||||
// Monotone: settlement alone does not step the phase anywhere.
|
||||
expect(session.getSnapshot().composerPhase).toBe('engaging')
|
||||
expect(api.callsOf('session.prompt')).toMatchObject([{ sessionId: SID, mode: 'queue', content: [{ type: 'text', text: '要发的' }] }])
|
||||
expect(api.callsOf('session.prompt')).toMatchObject([{
|
||||
sessionId: SID,
|
||||
mode: 'queue',
|
||||
content: [{ type: 'text', text: '要发的' }],
|
||||
clientTimeZone: new Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
}])
|
||||
// First content lands (running turn): engaging → active.
|
||||
session.handleRunning(true)
|
||||
expect(session.getSnapshot().composerPhase).toBe('active')
|
||||
|
||||
24
packages/client/runtime/tests/time-zone.spec.ts
Normal file
24
packages/client/runtime/tests/time-zone.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { resolvedClientTimeZone } from '../src/client/time-zone.ts'
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('browser time zone', () => {
|
||||
it('returns the runtime-resolved zone', () => {
|
||||
expect(resolvedClientTimeZone()).toBe(
|
||||
new Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
)
|
||||
})
|
||||
|
||||
it.each([undefined, ''])('fails loud when the runtime exposes no zone %#', (timeZone) => {
|
||||
const options = new Intl.DateTimeFormat().resolvedOptions()
|
||||
vi.spyOn(Intl.DateTimeFormat.prototype, 'resolvedOptions').mockReturnValue({
|
||||
...options,
|
||||
timeZone: timeZone as string,
|
||||
})
|
||||
|
||||
expect(() => resolvedClientTimeZone()).toThrow('browser time zone is unavailable')
|
||||
})
|
||||
})
|
||||
@@ -61,6 +61,10 @@ async function mount(): Promise<Bench> {
|
||||
const handle: ConnectionHandle = {
|
||||
api,
|
||||
isLoopback: true,
|
||||
hostDescription: {
|
||||
getSnapshot: () => undefined,
|
||||
subscribe: () => () => {},
|
||||
},
|
||||
rpc: {
|
||||
call: () => Promise.reject(new Error('unexpected generic RPC call')),
|
||||
},
|
||||
@@ -122,8 +126,9 @@ describe('wire event bridge', () => {
|
||||
const bench = await mount()
|
||||
let resets = 0
|
||||
bench.ctx.on('connection/reset', () => { resets++ })
|
||||
bench.sinks?.onConnected?.()
|
||||
bench.sinks?.onConnected?.() // second generation after a reconnect
|
||||
const description = { version: '0', cwd: '/f', attachedSessions: 0, canOpenPath: true }
|
||||
bench.sinks?.onConnected?.(description)
|
||||
bench.sinks?.onConnected?.(description) // second generation after a reconnect
|
||||
expect(resets).toBe(2)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user