@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
336 lines (278 loc) • 10.9 kB
text/typescript
// @vitest-environment node
import { ModelProvider } from 'model-bank';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { testProvider } from '../../providerTestUtils';
import { LobeGiteeAI, params } from './index';
testProvider({
Runtime: LobeGiteeAI,
chatDebugEnv: 'DEBUG_GITEE_AI_CHAT_COMPLETION',
chatModel: 'deepseek-r1',
defaultBaseURL: 'https://ai.gitee.com/v1',
provider: ModelProvider.GiteeAI,
});
describe('LobeGiteeAI - custom features', () => {
let instance: InstanceType<typeof LobeGiteeAI>;
beforeEach(() => {
instance = new LobeGiteeAI({ apiKey: 'test_api_key' });
vi.spyOn(instance['client'].chat.completions, 'create').mockResolvedValue(
new ReadableStream() as any,
);
});
describe('params configuration', () => {
it('should export params object with correct baseURL', () => {
expect(params.baseURL).toBe('https://ai.gitee.com/v1');
});
it('should export params with correct provider', () => {
expect(params.provider).toBe(ModelProvider.GiteeAI);
});
it('should have debug configuration', () => {
expect(params.debug).toBeDefined();
expect(params.debug.chatCompletion).toBeDefined();
expect(typeof params.debug.chatCompletion).toBe('function');
});
it('should have models function', () => {
expect(params.models).toBeDefined();
expect(typeof params.models).toBe('function');
});
});
describe('debug configuration', () => {
it('should disable debug by default', () => {
delete process.env.DEBUG_GITEE_AI_CHAT_COMPLETION;
const result = params.debug.chatCompletion();
expect(result).toBe(false);
});
it('should enable debug when env is set to 1', () => {
process.env.DEBUG_GITEE_AI_CHAT_COMPLETION = '1';
const result = params.debug.chatCompletion();
expect(result).toBe(true);
});
it('should disable debug when env is set to 0', () => {
process.env.DEBUG_GITEE_AI_CHAT_COMPLETION = '0';
const result = params.debug.chatCompletion();
expect(result).toBe(false);
});
it('should disable debug when env is empty string', () => {
process.env.DEBUG_GITEE_AI_CHAT_COMPLETION = '';
const result = params.debug.chatCompletion();
expect(result).toBe(false);
});
});
describe('models function', () => {
it('should fetch and process models with data property', async () => {
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockResolvedValue({
data: [
{ id: 'deepseek-r1', object: 'model' },
{ id: 'qwen-plus', object: 'model' },
],
}),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
});
it('should handle models list without data property (direct array)', async () => {
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockResolvedValue([
{ id: 'deepseek-r1', object: 'model' },
{ id: 'qwen-plus', object: 'model' },
]),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
});
it('should handle empty models list with data property', async () => {
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockResolvedValue({
data: [],
}),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
expect(models).toHaveLength(0);
});
it('should handle empty models list without data property', async () => {
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockResolvedValue([]),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
expect(models).toHaveLength(0);
});
it('should handle null response', async () => {
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockResolvedValue(null),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
expect(models).toHaveLength(0);
});
it('should handle undefined response', async () => {
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockResolvedValue(undefined),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
expect(models).toHaveLength(0);
});
it('should handle response with non-array data', async () => {
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockResolvedValue({
data: 'not-an-array',
}),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
expect(models).toHaveLength(0);
});
it('should handle network error and return empty array', async () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockRejectedValue(new Error('Network error')),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
expect(models).toHaveLength(0);
expect(consoleWarnSpy).toHaveBeenCalledWith(
'Failed to fetch GiteeAI models. Please ensure your GiteeAI API key is valid:',
expect.any(Error),
);
consoleWarnSpy.mockRestore();
});
it('should handle API authentication error and return empty array', async () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const mockClient = {
apiKey: 'invalid_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockRejectedValue(new Error('401 Unauthorized')),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toEqual([]);
expect(consoleWarnSpy).toHaveBeenCalledWith(
'Failed to fetch GiteeAI models. Please ensure your GiteeAI API key is valid:',
expect.any(Error),
);
consoleWarnSpy.mockRestore();
});
it('should handle API rate limit error and return empty array', async () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockRejectedValue(new Error('429 Too Many Requests')),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toEqual([]);
expect(consoleWarnSpy).toHaveBeenCalledWith(
'Failed to fetch GiteeAI models. Please ensure your GiteeAI API key is valid:',
expect.any(Error),
);
consoleWarnSpy.mockRestore();
});
it('should handle timeout error and return empty array', async () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockRejectedValue(new Error('Request timeout')),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(mockClient.models.list).toHaveBeenCalledTimes(1);
expect(models).toEqual([]);
expect(consoleWarnSpy).toHaveBeenCalledWith(
'Failed to fetch GiteeAI models. Please ensure your GiteeAI API key is valid:',
expect.any(Error),
);
consoleWarnSpy.mockRestore();
});
it('should handle malformed JSON response', async () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockRejectedValue(new Error('Invalid JSON')),
},
} as any;
const models = await params.models!({ client: mockClient });
expect(models).toEqual([]);
expect(consoleWarnSpy).toHaveBeenCalledWith(
'Failed to fetch GiteeAI models. Please ensure your GiteeAI API key is valid:',
expect.any(Error),
);
consoleWarnSpy.mockRestore();
});
it('should pass correct client to processMultiProviderModelList', async () => {
const mockModelList = [
{ id: 'deepseek-r1', object: 'model' },
{ id: 'qwen-plus', object: 'model' },
];
const mockClient = {
apiKey: 'test_api_key',
baseURL: 'https://ai.gitee.com/v1',
models: {
list: vi.fn().mockResolvedValue({ data: mockModelList }),
},
} as any;
const models = await params.models!({ client: mockClient });
// Verify processMultiProviderModelList was called with correct parameters
expect(models).toBeDefined();
expect(Array.isArray(models)).toBe(true);
});
});
});