refactor(schedule): make absolute times explicit
This commit is contained in:
@@ -22,10 +22,7 @@ import { MemoryStorageBackend } from '../../../storage/storage-domain/tests/help
|
||||
let nextRpc = 1
|
||||
|
||||
function request<P>(payload: P): RpcRequest<P> {
|
||||
return {
|
||||
rpcId: RpcId(`workspace-${String(nextRpc++)}`),
|
||||
payload: { timeZone: 'UTC', clientTimeZone: 'UTC', ...payload },
|
||||
}
|
||||
return { rpcId: RpcId(`workspace-${String(nextRpc++)}`), payload }
|
||||
}
|
||||
|
||||
function expectOk<T>(response: RpcResponse<T>): T {
|
||||
@@ -362,157 +359,6 @@ describe('session creation and Workspace membership', () => {
|
||||
expectOk(await api.sessions.create(request({ workspaceId: created.workspaceId, sessionId })))
|
||||
expect(expectOk(await api.workspace.list(request({}))).items[0]?.sessionIds).toEqual([sessionId])
|
||||
})
|
||||
|
||||
it('canonicalizes the immutable Session zone and rejects identity conflicts', async () => {
|
||||
const { api, ctx, workspaceRoot } = await harness()
|
||||
const sessionId = SessionId('session-zone-identity')
|
||||
const alias = 'US/Eastern'
|
||||
const canonical = new Intl.DateTimeFormat('en-US', { timeZone: alias })
|
||||
.resolvedOptions().timeZone
|
||||
|
||||
expectOk(await api.sessions.create(request({ sessionId, cwd: workspaceRoot, timeZone: alias })))
|
||||
expect(ctx.agents.get(sessionId)?.session.header.timeZone).toBe(canonical)
|
||||
|
||||
expectOk(await api.sessions.create(request({ sessionId, cwd: workspaceRoot, timeZone: canonical })))
|
||||
const conflict = await api.sessions.create(request({
|
||||
sessionId,
|
||||
cwd: workspaceRoot,
|
||||
timeZone: 'Asia/Shanghai',
|
||||
}))
|
||||
expect(conflict.result).toMatchObject({
|
||||
ok: false,
|
||||
error: {
|
||||
code: 'session-conflict',
|
||||
details: {
|
||||
sessionId,
|
||||
requestedCwd: workspaceRoot,
|
||||
requestedTimeZone: 'Asia/Shanghai',
|
||||
existingTimeZone: canonical,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps a live headerless Session compatible without absorbing a request zone', async () => {
|
||||
const { api, ctx, workspaceRoot } = await harness()
|
||||
const session = ctx.sessions.create(SessionId('session-zone-headerless'), {
|
||||
meta: { cwd: workspaceRoot },
|
||||
})
|
||||
ctx.agents.register(stubAgent(session))
|
||||
|
||||
expectOk(await api.sessions.create(request({
|
||||
sessionId: session.id,
|
||||
cwd: workspaceRoot,
|
||||
timeZone: 'Asia/Shanghai',
|
||||
})))
|
||||
expect(session.header.timeZone).toBeUndefined()
|
||||
})
|
||||
|
||||
it('serializes different-zone creates so the first immutable identity wins', async () => {
|
||||
const { api, ctx, workspaceRoot } = await harness()
|
||||
const sessionId = SessionId('session-zone-race')
|
||||
const first = api.sessions.create(request({
|
||||
sessionId,
|
||||
cwd: workspaceRoot,
|
||||
timeZone: 'UTC',
|
||||
}))
|
||||
const second = api.sessions.create(request({
|
||||
sessionId,
|
||||
cwd: workspaceRoot,
|
||||
timeZone: 'Asia/Shanghai',
|
||||
}))
|
||||
const [firstResult, secondResult] = await Promise.all([first, second])
|
||||
|
||||
expect(firstResult.result).toMatchObject({ ok: true, value: { sessionId } })
|
||||
expect(secondResult.result).toMatchObject({
|
||||
ok: false,
|
||||
error: { code: 'session-conflict', details: { existingTimeZone: 'UTC' } },
|
||||
})
|
||||
expect(ctx.agents.get(sessionId)?.session.header.timeZone).toBe('UTC')
|
||||
})
|
||||
|
||||
it.each([
|
||||
[undefined, null],
|
||||
['', ''],
|
||||
[' UTC', ' UTC'],
|
||||
['CST', 'CST'],
|
||||
['GMT', 'GMT'],
|
||||
['+08:00', '+08:00'],
|
||||
['Not/A_Real_Zone', 'Not/A_Real_Zone'],
|
||||
] as const)('rejects invalid Session zone input %j before Agent creation', async (timeZone, value) => {
|
||||
const { api, ctx } = await harness()
|
||||
const invalidRequest = request({})
|
||||
Object.assign(invalidRequest.payload, { timeZone })
|
||||
const response = await api.sessions.create(invalidRequest)
|
||||
|
||||
expect(response.result).toMatchObject({
|
||||
ok: false,
|
||||
error: { code: 'invalid-time-zone', details: { field: 'timeZone', value } },
|
||||
})
|
||||
expect(ctx.agents.list()).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('binds each canonical client zone to its own queued or steering message source', async () => {
|
||||
const { api, ctx } = await harness()
|
||||
const sessionId = expectOk(await api.sessions.create(request({ timeZone: 'UTC' }))).sessionId
|
||||
const agent = ctx.agents.get(sessionId)
|
||||
if (agent === undefined) throw new Error('created Agent missing')
|
||||
const followup = vi.spyOn(agent, 'followup')
|
||||
const steer = vi.spyOn(agent, 'steer')
|
||||
const alias = 'US/Eastern'
|
||||
const canonical = new Intl.DateTimeFormat('en-US', { timeZone: alias })
|
||||
.resolvedOptions().timeZone
|
||||
|
||||
expectOk(await api.sessions.prompt(request({
|
||||
sessionId,
|
||||
mode: 'queue',
|
||||
content: [{ type: 'text', text: 'queue' }],
|
||||
clientTimeZone: alias,
|
||||
})))
|
||||
expectOk(await api.sessions.prompt(request({
|
||||
sessionId,
|
||||
mode: 'steer',
|
||||
content: [{ type: 'text', text: 'steer' }],
|
||||
clientTimeZone: 'Asia/Shanghai',
|
||||
})))
|
||||
|
||||
expect(followup.mock.calls[0]?.[0].source).toMatchObject({
|
||||
kind: 'user',
|
||||
clientTimeZone: canonical,
|
||||
})
|
||||
expect(steer.mock.calls[0]?.[0].source).toMatchObject({
|
||||
kind: 'user',
|
||||
clientTimeZone: 'Asia/Shanghai',
|
||||
})
|
||||
})
|
||||
|
||||
it.each([undefined, '', 'CST', 'Not/A_Real_Zone'] as const)(
|
||||
'rejects invalid prompt zone input %j before delivery',
|
||||
async (clientTimeZone) => {
|
||||
const { api, ctx } = await harness()
|
||||
const sessionId = expectOk(await api.sessions.create(request({ timeZone: 'UTC' }))).sessionId
|
||||
const agent = ctx.agents.get(sessionId)
|
||||
if (agent === undefined) throw new Error('created Agent missing')
|
||||
const followup = vi.spyOn(agent, 'followup')
|
||||
|
||||
const invalidRequest = request({
|
||||
sessionId,
|
||||
mode: 'queue' as const,
|
||||
content: [{ type: 'text' as const, text: 'rejected' }],
|
||||
})
|
||||
Object.assign(invalidRequest.payload, { clientTimeZone })
|
||||
const response = await api.sessions.prompt(invalidRequest)
|
||||
|
||||
expect(response.result).toMatchObject({
|
||||
ok: false,
|
||||
error: {
|
||||
code: 'invalid-time-zone',
|
||||
details: { field: 'clientTimeZone', value: clientTimeZone ?? null },
|
||||
},
|
||||
})
|
||||
expect(followup).not.toHaveBeenCalled()
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
describe('Host Workspace increments', () => {
|
||||
|
||||
Reference in New Issue
Block a user