Skip to main content
Glama

stripe

Official
by stripe
utils.test.ts14.5 kB
/** * Tests for Stripe provider utility functions */ import { convertToOpenAIMessages, mapOpenAIFinishReason, normalizeModelId, } from '../utils'; describe('Stripe Provider Utils', () => { describe('convertToOpenAIMessages', () => { it('should convert system message', () => { const result = convertToOpenAIMessages([ { role: 'system', content: 'You are a helpful assistant.', }, ]); expect(result).toHaveLength(1); expect(result[0]).toEqual({ role: 'system', content: 'You are a helpful assistant.', }); }); it('should convert user text message', () => { const result = convertToOpenAIMessages([ { role: 'user', content: [{type: 'text', text: 'Hello!'}], }, ]); expect(result).toHaveLength(1); expect(result[0].role).toBe('user'); // Single text messages are sent as simple strings for Anthropic compatibility expect(result[0].content).toBe('Hello!'); }); it('should convert user message with file URL', () => { const result = convertToOpenAIMessages([ { role: 'user', content: [ {type: 'text', text: 'What is this?'}, { type: 'file', data: 'https://example.com/image.jpg', mediaType: 'image/jpeg', }, ], }, ]); expect(result).toHaveLength(1); // Multi-part messages should remain as arrays expect(Array.isArray(result[0].content)).toBe(true); const content = result[0].content as any[]; expect(content).toHaveLength(2); expect(content[1].type).toBe('image_url'); expect(content[1].image_url.url).toBe('https://example.com/image.jpg'); }); it('should convert user message with file Uint8Array to base64', () => { const fileData = new Uint8Array([137, 80, 78, 71]); // PNG header const result = convertToOpenAIMessages([ { role: 'user', content: [ { type: 'file', data: fileData, mediaType: 'image/png', }, ], }, ]); expect(result).toHaveLength(1); // Single file becomes array with one element expect(Array.isArray(result[0].content)).toBe(true); const content = result[0].content as any[]; expect(content).toHaveLength(1); expect(content[0].type).toBe('image_url'); expect(content[0].image_url.url).toMatch(/^data:image\/png;base64,/); }); it('should convert assistant message with text', () => { const result = convertToOpenAIMessages([ { role: 'assistant', content: [{type: 'text', text: 'Hello! How can I help?'}], }, ]); expect(result).toHaveLength(1); expect(result[0]).toEqual({ role: 'assistant', content: 'Hello! How can I help?', tool_calls: undefined, }); }); it('should convert assistant message with tool calls', () => { const result = convertToOpenAIMessages([ { role: 'assistant', content: [ { type: 'tool-call', toolCallId: 'call_123', toolName: 'getWeather', input: {location: 'San Francisco'}, }, ], }, ]); expect(result).toHaveLength(1); expect(result[0].role).toBe('assistant'); expect(result[0].content).toBe(''); // Empty string when only tool calls expect(result[0].tool_calls).toHaveLength(1); expect(result[0].tool_calls![0]).toEqual({ id: 'call_123', type: 'function', function: { name: 'getWeather', arguments: '{"location":"San Francisco"}', }, }); }); it('should convert assistant message with text and tool calls', () => { const result = convertToOpenAIMessages([ { role: 'assistant', content: [ {type: 'text', text: 'Let me check the weather.'}, { type: 'tool-call', toolCallId: 'call_123', toolName: 'getWeather', input: {location: 'Paris'}, }, ], }, ]); expect(result).toHaveLength(1); expect(result[0].role).toBe('assistant'); expect(result[0].content).toBe('Let me check the weather.'); expect(result[0].tool_calls).toHaveLength(1); }); it('should convert tool message', () => { const result = convertToOpenAIMessages([ { role: 'tool', content: [ { type: 'tool-result', toolCallId: 'call_123', toolName: 'getWeather', output: {type: 'json', value: {temperature: 72, condition: 'Sunny'}}, }, ], }, ]); expect(result).toHaveLength(1); expect(result[0]).toEqual({ role: 'tool', tool_call_id: 'call_123', name: 'getWeather', content: '{"temperature":72,"condition":"Sunny"}', }); }); it('should handle string tool call args', () => { const result = convertToOpenAIMessages([ { role: 'assistant', content: [ { type: 'tool-call', toolCallId: 'call_123', toolName: 'test', input: '{"key":"value"}', }, ], }, ]); expect(result[0].tool_calls![0].function.arguments).toBe('{"key":"value"}'); }); it('should handle string tool result', () => { const result = convertToOpenAIMessages([ { role: 'tool', content: [ { type: 'tool-result', toolCallId: 'call_123', toolName: 'test', output: {type: 'text', value: 'Simple string result'}, }, ], }, ]); expect(result[0].content).toBe('Simple string result'); }); it('should convert multiple messages', () => { const result = convertToOpenAIMessages([ {role: 'system', content: 'You are helpful.'}, {role: 'user', content: [{type: 'text', text: 'Hello'}]}, {role: 'assistant', content: [{type: 'text', text: 'Hi!'}]}, ]); expect(result).toHaveLength(3); expect(result[0].role).toBe('system'); expect(result[1].role).toBe('user'); expect(result[2].role).toBe('assistant'); }); it('should throw error for unsupported message role', () => { expect(() => { convertToOpenAIMessages([ // @ts-expect-error - Testing invalid role {role: 'invalid', content: 'test'}, ]); }).toThrow('Unsupported message role'); }); it('should throw error for unsupported part type', () => { expect(() => { convertToOpenAIMessages([ { role: 'user', // @ts-expect-error - Testing invalid part type content: [{type: 'unsupported', data: 'test'}], }, ]); }).toThrow('Unsupported user message part type'); }); }); describe('mapOpenAIFinishReason', () => { it('should map "stop" to "stop"', () => { expect(mapOpenAIFinishReason('stop')).toBe('stop'); }); it('should map "length" to "length"', () => { expect(mapOpenAIFinishReason('length')).toBe('length'); }); it('should map "content_filter" to "content-filter"', () => { expect(mapOpenAIFinishReason('content_filter')).toBe('content-filter'); }); it('should map "tool_calls" to "tool-calls"', () => { expect(mapOpenAIFinishReason('tool_calls')).toBe('tool-calls'); }); it('should map "function_call" to "tool-calls"', () => { expect(mapOpenAIFinishReason('function_call')).toBe('tool-calls'); }); it('should map null to "unknown"', () => { expect(mapOpenAIFinishReason(null)).toBe('unknown'); }); it('should map undefined to "unknown"', () => { expect(mapOpenAIFinishReason(undefined)).toBe('unknown'); }); it('should map unknown reason to "unknown"', () => { expect(mapOpenAIFinishReason('some_other_reason')).toBe('unknown'); }); }); describe('normalizeModelId', () => { describe('Anthropic models', () => { it('should remove date suffix (YYYYMMDD format)', () => { // Note: The date is removed AND version dashes are converted to dots expect(normalizeModelId('anthropic/claude-3-5-sonnet-20241022')).toBe( 'anthropic/claude-3.5-sonnet' ); expect(normalizeModelId('anthropic/claude-sonnet-4-20250101')).toBe( 'anthropic/claude-sonnet-4' ); expect(normalizeModelId('anthropic/claude-opus-4-20241231')).toBe( 'anthropic/claude-opus-4' ); }); it('should remove -latest suffix', () => { expect(normalizeModelId('anthropic/claude-sonnet-4-latest')).toBe( 'anthropic/claude-sonnet-4' ); expect(normalizeModelId('anthropic/claude-opus-4-latest')).toBe( 'anthropic/claude-opus-4' ); }); it('should convert version dashes to dots (claude-3-5 → claude-3.5)', () => { expect(normalizeModelId('anthropic/claude-3-5-sonnet')).toBe( 'anthropic/claude-3.5-sonnet' ); expect(normalizeModelId('anthropic/claude-3-7-sonnet')).toBe( 'anthropic/claude-3.7-sonnet' ); }); it('should handle version numbers without model names (sonnet-4-5 → sonnet-4.5)', () => { expect(normalizeModelId('anthropic/sonnet-4-5')).toBe( 'anthropic/sonnet-4.5' ); expect(normalizeModelId('anthropic/opus-4-1')).toBe( 'anthropic/opus-4.1' ); }); it('should handle combined date suffix and version conversion', () => { expect(normalizeModelId('anthropic/claude-3-5-sonnet-20241022')).toBe( 'anthropic/claude-3.5-sonnet' ); expect(normalizeModelId('anthropic/claude-3-7-sonnet-20250115')).toBe( 'anthropic/claude-3.7-sonnet' ); }); it('should handle -latest suffix with version conversion', () => { expect(normalizeModelId('anthropic/claude-3-5-sonnet-latest')).toBe( 'anthropic/claude-3.5-sonnet' ); expect(normalizeModelId('anthropic/sonnet-4-5-latest')).toBe( 'anthropic/sonnet-4.5' ); }); it('should handle models without dates or versions', () => { expect(normalizeModelId('anthropic/claude-sonnet')).toBe( 'anthropic/claude-sonnet' ); expect(normalizeModelId('anthropic/claude-opus')).toBe( 'anthropic/claude-opus' ); }); it('should handle case-insensitive provider names', () => { expect(normalizeModelId('Anthropic/claude-3-5-sonnet-20241022')).toBe( 'Anthropic/claude-3.5-sonnet' ); expect(normalizeModelId('ANTHROPIC/claude-3-5-sonnet-20241022')).toBe( 'ANTHROPIC/claude-3.5-sonnet' ); }); }); describe('OpenAI models', () => { it('should remove date suffix in YYYY-MM-DD format', () => { expect(normalizeModelId('openai/gpt-4-turbo-2024-04-09')).toBe( 'openai/gpt-4-turbo' ); expect(normalizeModelId('openai/gpt-4-2024-12-31')).toBe( 'openai/gpt-4' ); }); it('should keep gpt-4o-2024-05-13 as an exception', () => { expect(normalizeModelId('openai/gpt-4o-2024-05-13')).toBe( 'openai/gpt-4o-2024-05-13' ); }); it('should handle models without dates', () => { expect(normalizeModelId('openai/gpt-5')).toBe('openai/gpt-5'); expect(normalizeModelId('openai/gpt-4.1')).toBe('openai/gpt-4.1'); expect(normalizeModelId('openai/o3')).toBe('openai/o3'); }); it('should handle case-insensitive provider names', () => { expect(normalizeModelId('OpenAI/gpt-4-2024-12-31')).toBe( 'OpenAI/gpt-4' ); expect(normalizeModelId('OPENAI/gpt-4-turbo-2024-04-09')).toBe( 'OPENAI/gpt-4-turbo' ); }); it('should not affect YYYYMMDD format (only YYYY-MM-DD)', () => { // OpenAI only removes YYYY-MM-DD format, not YYYYMMDD expect(normalizeModelId('openai/gpt-4-20241231')).toBe( 'openai/gpt-4-20241231' ); }); }); describe('Google/Gemini models', () => { it('should keep models as-is', () => { expect(normalizeModelId('google/gemini-2.5-pro')).toBe( 'google/gemini-2.5-pro' ); expect(normalizeModelId('google/gemini-2.0-flash')).toBe( 'google/gemini-2.0-flash' ); expect(normalizeModelId('google/gemini-1.5-pro')).toBe( 'google/gemini-1.5-pro' ); }); it('should not remove any suffixes', () => { expect(normalizeModelId('google/gemini-2.5-pro-20250101')).toBe( 'google/gemini-2.5-pro-20250101' ); expect(normalizeModelId('google/gemini-2.5-pro-latest')).toBe( 'google/gemini-2.5-pro-latest' ); }); }); describe('Other providers', () => { it('should keep unknown provider models as-is', () => { expect(normalizeModelId('bedrock/claude-3-5-sonnet')).toBe( 'bedrock/claude-3-5-sonnet' ); expect(normalizeModelId('azure/gpt-4-2024-12-31')).toBe( 'azure/gpt-4-2024-12-31' ); expect(normalizeModelId('custom/my-model-1-2-3')).toBe( 'custom/my-model-1-2-3' ); }); }); describe('Edge cases', () => { it('should handle model IDs without provider prefix', () => { // If no slash, return as-is expect(normalizeModelId('gpt-5')).toBe('gpt-5'); expect(normalizeModelId('claude-sonnet-4')).toBe('claude-sonnet-4'); }); it('should handle model IDs with multiple slashes', () => { // If more than one slash, return as-is expect(normalizeModelId('provider/category/model')).toBe( 'provider/category/model' ); }); it('should handle empty strings', () => { expect(normalizeModelId('')).toBe(''); }); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/stripe/agent-toolkit'

If you have feedback or need assistance with the MCP directory API, please join our Discord server