mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-07 19:06:11 +00:00
179 lines
6.3 KiB
TypeScript
179 lines
6.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
ClearOpencodeSessionCallbackRequestSchema,
|
|
ClearOpencodeSessionResponseSchema,
|
|
ListClaudeSessionsRpcResponseSchema,
|
|
ListCodexSessionsRpcResponseSchema,
|
|
ListPiSessionsRpcResponseSchema,
|
|
MessagesQuerySchema,
|
|
SendMessageRequestSchema
|
|
} from './apiTypes'
|
|
|
|
describe('ListCodexSessionsRpcResponseSchema', () => {
|
|
it('preserves Codex session messages when parsing runner RPC responses', () => {
|
|
const parsed = ListCodexSessionsRpcResponseSchema.parse({
|
|
success: true,
|
|
sessions: [{
|
|
id: 'codex-session-id',
|
|
title: 'Codex Session',
|
|
file: '/home/user/.codex/sessions/session.jsonl',
|
|
modifiedAt: 1_000,
|
|
messages: [{
|
|
role: 'user',
|
|
content: {
|
|
type: 'text',
|
|
text: 'hello'
|
|
},
|
|
meta: {
|
|
sentFrom: 'cli'
|
|
}
|
|
}]
|
|
}]
|
|
})
|
|
|
|
expect(parsed.success).toBe(true)
|
|
if (parsed.success) {
|
|
expect(parsed.sessions[0]?.messages).toHaveLength(1)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('ListClaudeSessionsRpcResponseSchema', () => {
|
|
it('preserves Claude transcript messages when parsing runner RPC responses', () => {
|
|
const parsed = ListClaudeSessionsRpcResponseSchema.parse({
|
|
success: true,
|
|
sessions: [{
|
|
id: 'claude-session-id',
|
|
title: 'Claude Session',
|
|
file: '/home/user/.claude/projects/project/session.jsonl',
|
|
modifiedAt: 1_000,
|
|
messageCount: 1,
|
|
messages: [{
|
|
localId: 'claude:claude-session-id:user-1',
|
|
createdAt: 900,
|
|
content: {
|
|
role: 'user',
|
|
content: { type: 'text', text: 'hello' },
|
|
meta: { sentFrom: 'cli' }
|
|
}
|
|
}]
|
|
}]
|
|
})
|
|
|
|
expect(parsed.success).toBe(true)
|
|
if (parsed.success) {
|
|
const first = parsed.sessions[0]
|
|
expect(first && 'messages' in first ? first.messages : []).toHaveLength(1)
|
|
}
|
|
})
|
|
})
|
|
|
|
describe('ListPiSessionsRpcResponseSchema', () => {
|
|
it('preserves stable Pi entry and local ids', () => {
|
|
const parsed = ListPiSessionsRpcResponseSchema.parse({
|
|
success: true,
|
|
sessions: [{
|
|
id: 'pi-session-id',
|
|
title: 'Pi Session',
|
|
file: '/home/user/.pi/agent/sessions/session.jsonl',
|
|
modifiedAt: 1_000,
|
|
messageCount: 1,
|
|
activeEntryIds: ['entry-1'],
|
|
messages: [{
|
|
localId: 'pi:pi-session-id:entry-1:user',
|
|
entryId: 'entry-1',
|
|
parentEntryId: null,
|
|
createdAt: 900,
|
|
content: {
|
|
role: 'user',
|
|
content: { type: 'text', text: 'hello' },
|
|
meta: { sentFrom: 'cli' }
|
|
}
|
|
}]
|
|
}]
|
|
})
|
|
|
|
expect(parsed.success).toBe(true)
|
|
if (parsed.success) expect(parsed.sessions[0]?.messages?.[0]?.entryId).toBe('entry-1')
|
|
})
|
|
})
|
|
|
|
describe('ClearOpencodeSessionResponseSchema', () => {
|
|
it('requires the new HAPI session identity', () => {
|
|
expect(ClearOpencodeSessionResponseSchema.parse({ ok: true, sessionId: 'fresh-session' })).toEqual({
|
|
ok: true,
|
|
sessionId: 'fresh-session'
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('ClearOpencodeSessionCallbackRequestSchema', () => {
|
|
it('requires the reservation identity', () => {
|
|
expect(ClearOpencodeSessionCallbackRequestSchema.parse({ replacementSessionId: 'fresh-session' })).toEqual({
|
|
replacementSessionId: 'fresh-session'
|
|
})
|
|
expect(ClearOpencodeSessionCallbackRequestSchema.safeParse({}).success).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('MessagesQuerySchema', () => {
|
|
it('parses a forward cursor with a bounded snapshot and epoch', () => {
|
|
expect(MessagesQuerySchema.parse({
|
|
afterAt: '1000',
|
|
afterSeq: '10',
|
|
untilAt: '2000',
|
|
untilSeq: '20',
|
|
epoch: '3',
|
|
limit: '200'
|
|
})).toEqual({
|
|
afterAt: 1000,
|
|
afterSeq: 10,
|
|
untilAt: 2000,
|
|
untilSeq: 20,
|
|
epoch: 3,
|
|
limit: 200
|
|
})
|
|
})
|
|
|
|
it('rejects mixed before and after directions', () => {
|
|
expect(MessagesQuerySchema.safeParse({
|
|
beforeAt: 1000,
|
|
beforeSeq: 10,
|
|
afterAt: 2000,
|
|
afterSeq: 20
|
|
}).success).toBe(false)
|
|
})
|
|
|
|
it('rejects an unpaired or unscoped until cursor', () => {
|
|
expect(MessagesQuerySchema.safeParse({ untilAt: 2000, untilSeq: 20 }).success).toBe(false)
|
|
expect(MessagesQuerySchema.safeParse({ afterAt: 1000, afterSeq: 10, untilAt: 2000 }).success).toBe(false)
|
|
})
|
|
|
|
it('rejects epoch without a forward cursor', () => {
|
|
expect(MessagesQuerySchema.safeParse({ epoch: 1 }).success).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('SendMessageRequestSchema deliveryMode', () => {
|
|
it('accepts queue and steer delivery modes while leaving the field optional', () => {
|
|
expect(SendMessageRequestSchema.parse({ text: 'queue' }).deliveryMode).toBeUndefined()
|
|
expect(SendMessageRequestSchema.parse({ text: 'steer', deliveryMode: 'steer' }).deliveryMode).toBe('steer')
|
|
expect(SendMessageRequestSchema.parse({ text: 'queue', deliveryMode: 'queue' }).deliveryMode).toBe('queue')
|
|
})
|
|
|
|
it('rejects scheduled steer delivery', () => {
|
|
const parsed = SendMessageRequestSchema.safeParse({
|
|
text: 'later',
|
|
localId: 'scheduled-steer',
|
|
scheduledAt: Date.now() + 60_000,
|
|
deliveryMode: 'steer'
|
|
})
|
|
|
|
expect(parsed.success).toBe(false)
|
|
if (!parsed.success) {
|
|
expect(parsed.error.issues.some((issue) => issue.path[0] === 'deliveryMode')).toBe(true)
|
|
expect(parsed.error.issues.some((issue) => issue.message.includes('cannot use steer'))).toBe(true)
|
|
}
|
|
})
|
|
})
|