mirror of
https://github.com/JackChen-me/open-multi-agent.git
synced 2026-10-22 11:02:59 -05:00
* feat(release): add OMA-powered release bot * fix(release): harden bot execution boundaries * chore(release): schedule bot for Friday evening
144 lines
5.1 KiB
TypeScript
144 lines
5.1 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import {
|
|
TokenBudgetExceededError,
|
|
InvalidMessageError,
|
|
LLMCallTimeoutError,
|
|
StructuredOutputValidationError,
|
|
UnsupportedToolCallError,
|
|
UnsupportedToolResultContentError,
|
|
} from '../src/errors.js'
|
|
|
|
describe('TokenBudgetExceededError', () => {
|
|
it('sets .name to TokenBudgetExceededError', () => {
|
|
const err = new TokenBudgetExceededError('agent-1', 500, 400)
|
|
expect(err.name).toBe('TokenBudgetExceededError')
|
|
})
|
|
|
|
it('sets .code to TOKEN_BUDGET_EXCEEDED', () => {
|
|
const err = new TokenBudgetExceededError('agent-1', 500, 400)
|
|
expect(err.code).toBe('TOKEN_BUDGET_EXCEEDED')
|
|
})
|
|
|
|
it('stores agent, tokensUsed, and budget as readonly properties', () => {
|
|
const err = new TokenBudgetExceededError('worker-a', 1234, 1000)
|
|
expect(err.agent).toBe('worker-a')
|
|
expect(err.tokensUsed).toBe(1234)
|
|
expect(err.budget).toBe(1000)
|
|
})
|
|
|
|
it('formats the message with agent name, tokens used, and budget', () => {
|
|
const err = new TokenBudgetExceededError('analyst', 750, 500)
|
|
expect(err.message).toBe('Agent "analyst" exceeded token budget: 750 tokens used (budget: 500)')
|
|
})
|
|
|
|
it('is an instance of TokenBudgetExceededError', () => {
|
|
const err = new TokenBudgetExceededError('b', 1, 2)
|
|
expect(err).toBeInstanceOf(TokenBudgetExceededError)
|
|
})
|
|
|
|
it('is an instance of Error (extends built-in Error)', () => {
|
|
const err = new TokenBudgetExceededError('b', 1, 2)
|
|
expect(err).toBeInstanceOf(Error)
|
|
})
|
|
})
|
|
|
|
describe('InvalidMessageError', () => {
|
|
it('sets .name to InvalidMessageError', () => {
|
|
const err = new InvalidMessageError('bad content')
|
|
expect(err.name).toBe('InvalidMessageError')
|
|
})
|
|
|
|
it('sets .code to INVALID_MESSAGE', () => {
|
|
const err = new InvalidMessageError('some reason')
|
|
expect(err.code).toBe('INVALID_MESSAGE')
|
|
})
|
|
|
|
it('uses the constructor argument as the message', () => {
|
|
const err = new InvalidMessageError('content must be a ContentBlock[]')
|
|
expect(err.message).toBe('content must be a ContentBlock[]')
|
|
})
|
|
|
|
it('is an instance of InvalidMessageError', () => {
|
|
const err = new InvalidMessageError('test')
|
|
expect(err).toBeInstanceOf(InvalidMessageError)
|
|
})
|
|
|
|
it('is an instance of Error (extends built-in Error)', () => {
|
|
const err = new InvalidMessageError('test')
|
|
expect(err).toBeInstanceOf(Error)
|
|
})
|
|
})
|
|
|
|
describe('StructuredOutputValidationError', () => {
|
|
it('exposes a stable code, message, and validation cause', () => {
|
|
const cause = new Error('schema mismatch')
|
|
const err = new StructuredOutputValidationError(cause)
|
|
expect(err.name).toBe('StructuredOutputValidationError')
|
|
expect(err.code).toBe('STRUCTURED_OUTPUT_VALIDATION_FAILED')
|
|
expect(err.message).toBe('Structured output validation failed after retry.')
|
|
expect(err.cause).toBe(cause)
|
|
expect(err).toBeInstanceOf(Error)
|
|
})
|
|
})
|
|
|
|
describe('UnsupportedToolCallError', () => {
|
|
it('identifies the provider and unsupported tool type', () => {
|
|
const err = new UnsupportedToolCallError('openai', 'custom')
|
|
expect(err.name).toBe('UnsupportedToolCallError')
|
|
expect(err.code).toBe('UNSUPPORTED_TOOL_CALL')
|
|
expect(err.provider).toBe('openai')
|
|
expect(err.toolType).toBe('custom')
|
|
expect(err.message).toBe('openai returned unsupported tool-call type "custom"')
|
|
})
|
|
})
|
|
|
|
describe('UnsupportedToolResultContentError', () => {
|
|
it('identifies the provider and unmappable content type', () => {
|
|
const err = new UnsupportedToolResultContentError(
|
|
'Anthropic Messages',
|
|
'file:text/plain',
|
|
'only PDF files are supported',
|
|
)
|
|
expect(err.name).toBe('UnsupportedToolResultContentError')
|
|
expect(err.code).toBe('UNSUPPORTED_TOOL_RESULT_CONTENT')
|
|
expect(err.provider).toBe('Anthropic Messages')
|
|
expect(err.contentType).toBe('file:text/plain')
|
|
expect(err.message).toContain('only PDF files are supported')
|
|
})
|
|
})
|
|
|
|
describe('LLMCallTimeoutError', () => {
|
|
it('sets .name to LLMCallTimeoutError', () => {
|
|
const err = new LLMCallTimeoutError(30_000, 'agent-1')
|
|
expect(err.name).toBe('LLMCallTimeoutError')
|
|
})
|
|
|
|
it('sets .code to LLM_CALL_TIMEOUT', () => {
|
|
const err = new LLMCallTimeoutError(30_000, 'agent-1')
|
|
expect(err.code).toBe('LLM_CALL_TIMEOUT')
|
|
})
|
|
|
|
it('stores timeoutMs and agent as readonly properties', () => {
|
|
const err = new LLMCallTimeoutError(15_000, 'worker-a')
|
|
expect(err.timeoutMs).toBe(15_000)
|
|
expect(err.agent).toBe('worker-a')
|
|
})
|
|
|
|
it('formats the message with agent name and timeout', () => {
|
|
const err = new LLMCallTimeoutError(20_000, 'analyst')
|
|
expect(err.message).toBe('Agent "analyst" LLM call exceeded per-call timeout of 20000ms')
|
|
})
|
|
|
|
it('omits the agent name from the message when unknown', () => {
|
|
const err = new LLMCallTimeoutError(20_000)
|
|
expect(err.agent).toBeUndefined()
|
|
expect(err.message).toBe('LLM call exceeded per-call timeout of 20000ms')
|
|
})
|
|
|
|
it('is an instance of LLMCallTimeoutError and Error', () => {
|
|
const err = new LLMCallTimeoutError(1000, 'b')
|
|
expect(err).toBeInstanceOf(LLMCallTimeoutError)
|
|
expect(err).toBeInstanceOf(Error)
|
|
})
|
|
})
|