69 lines
2.5 KiB
TypeScript
69 lines
2.5 KiB
TypeScript
import {
|
|
ChatServiceError,
|
|
extractChatText,
|
|
normalizeChatMessages,
|
|
sendReportableCommandMessage,
|
|
sendChatMessage,
|
|
} from '../src/features/chat/chat-service'
|
|
|
|
const originalFetch = global.fetch
|
|
|
|
afterEach(() => {
|
|
global.fetch = originalFetch
|
|
})
|
|
|
|
describe('chat service', () => {
|
|
it('normalizes bounded messages and extracts text blocks', () => {
|
|
expect(normalizeChatMessages([{ role: 'user', content: ' hello ' }]))
|
|
.toEqual([{ role: 'user', content: 'hello' }])
|
|
expect(extractChatText({ content: [
|
|
{ type: 'text', text: 'hello ' },
|
|
{ type: 'tool_use', input: {} },
|
|
{ type: 'text', text: 'world' },
|
|
] })).toBe('hello world')
|
|
})
|
|
|
|
it('rejects invalid requests before network I/O', async () => {
|
|
global.fetch = jest.fn()
|
|
await expect(sendChatMessage([], { accessToken: 'token' }))
|
|
.rejects.toMatchObject({ code: 'INVALID_REQUEST', retryable: false })
|
|
expect(global.fetch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('maps quota responses without creating a fallback answer', async () => {
|
|
global.fetch = jest.fn().mockResolvedValue({
|
|
ok: false,
|
|
status: 429,
|
|
json: async () => ({ error: 'quota_exceeded' }),
|
|
})
|
|
await expect(sendChatMessage([{ role: 'user', content: 'hello' }], { accessToken: 'token' }))
|
|
.rejects.toMatchObject({ code: 'QUOTA_EXCEEDED', retryable: false, status: 429 })
|
|
})
|
|
|
|
it('requires a non-empty provider response', async () => {
|
|
global.fetch = jest.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
json: async () => ({ content: [{ type: 'text', text: '' }] }),
|
|
})
|
|
await expect(sendChatMessage([{ role: 'user', content: 'hello' }], { accessToken: 'token' }))
|
|
.rejects.toBeInstanceOf(ChatServiceError)
|
|
})
|
|
|
|
it('requires and returns a server-issued receipt for reportable command output', async () => {
|
|
const generationId = '11111111-1111-4111-8111-111111111111'
|
|
global.fetch = jest.fn().mockResolvedValue({
|
|
ok: true,
|
|
status: 200,
|
|
headers: { get: (name: string) => name.toLowerCase() === 'x-d3ro-generation-id' ? generationId : null },
|
|
json: async () => ({ content: [{ type: 'text', text: 'result' }] }),
|
|
})
|
|
|
|
await expect(sendReportableCommandMessage(
|
|
[{ role: 'user', content: 'hello' }],
|
|
{ accessToken: 'token' },
|
|
)).resolves.toEqual({ text: 'result', generationId })
|
|
const request = (global.fetch as jest.Mock).mock.calls[0][1]
|
|
expect(request.headers['X-D3RO-Generation-Purpose']).toBe('command_response')
|
|
})
|
|
})
|