@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.
342 lines (304 loc) • 9.96 kB
text/typescript
import { expect } from 'vitest';
import { AIChatModelCard } from '@/types/aiModel';
import { mergeArrayById } from './merge';
describe('mergeArrayById', () => {
it('should merge data', () => {
const data = mergeArrayById(
[
{
contextWindowTokens: 128_000,
description:
'o1-mini是一款针对编程、数学和科学应用场景而设计的快速、经济高效的推理模型。该模型具有128K上下文和2023年10月的知识截止日期。',
displayName: 'OpenAI o1-mini',
enabled: true,
id: 'o1-mini',
maxOutput: 65_536,
pricing: {
input: 3,
output: 12,
},
releasedAt: '2024-09-12',
type: 'chat',
},
],
[{ id: 'o1-mini', displayName: 'OpenAI o1-mini ABC', type: 'chat' }],
);
expect(data).toEqual([
{
contextWindowTokens: 128_000,
description:
'o1-mini是一款针对编程、数学和科学应用场景而设计的快速、经济高效的推理模型。该模型具有128K上下文和2023年10月的知识截止日期。',
displayName: 'OpenAI o1-mini ABC',
enabled: true,
id: 'o1-mini',
maxOutput: 65_536,
pricing: {
input: 3,
output: 12,
},
releasedAt: '2024-09-12',
type: 'chat',
},
]);
});
it('should merge data with objects', () => {
const data = mergeArrayById(
[
{
contextWindowTokens: 128_000,
description:
'o1-mini是一款针对编程、数学和科学应用场景而设计的快速、经济高效的推理模型。该模型具有128K上下文和2023年10月的知识截止日期。',
displayName: 'OpenAI o1-mini',
enabled: true,
id: 'o3-mini',
abilities: {
functionCall: true,
},
maxOutput: 65_536,
pricing: {
input: 3,
output: 12,
},
releasedAt: '2024-09-12',
type: 'chat',
},
],
[
{
id: 'o3-mini',
contextWindowTokens: null,
displayName: 'OpenAI o1-mini ABC',
type: 'chat',
abilities: {},
enabled: false,
},
],
);
expect(data).toEqual([
{
contextWindowTokens: 128_000,
description:
'o1-mini是一款针对编程、数学和科学应用场景而设计的快速、经济高效的推理模型。该模型具有128K上下文和2023年10月的知识截止日期。',
displayName: 'OpenAI o1-mini ABC',
enabled: false,
id: 'o3-mini',
maxOutput: 65_536,
pricing: {
input: 3,
output: 12,
},
abilities: {
functionCall: true,
},
releasedAt: '2024-09-12',
type: 'chat',
},
]);
});
describe('empty array handling', () => {
it('should return empty array when both inputs are empty', () => {
const result = mergeArrayById([], []);
expect(result).toEqual([]);
});
it('should return all default items when user items is empty', () => {
const defaultItems = [
{ id: '1', name: 'Default 1', value: 100 },
{ id: '2', name: 'Default 2', value: 200 },
];
const result = mergeArrayById(defaultItems, []);
expect(result).toEqual(defaultItems);
});
it('should return all user items when default items is empty', () => {
const userItems = [
{ id: '1', name: 'User 1', value: 300 },
{ id: '2', name: 'User 2', value: 400 },
];
const result = mergeArrayById([], userItems);
expect(result).toEqual(userItems);
});
});
describe('ID matching scenarios', () => {
it('should handle user items with IDs not in default items', () => {
const defaultItems = [{ id: '1', name: 'Default 1', value: 100 }];
const userItems = [
{ id: '1', name: 'User 1', value: 200 },
{ id: '2', name: 'User 2', value: 300 }, // New ID
];
const result = mergeArrayById(defaultItems, userItems);
expect(result).toHaveLength(2);
expect(result).toContainEqual({ id: '1', name: 'User 1', value: 200 });
expect(result).toContainEqual({ id: '2', name: 'User 2', value: 300 });
});
it('should merge multiple items correctly', () => {
const defaultItems = [
{ id: '1', name: 'Default 1', value: 100, meta: { key: 'value' } },
{ id: '2', name: 'Default 2', value: 200, meta: { key: 'value' } },
];
const userItems = [
{ id: '2', name: 'User 2', value: 300 },
{ id: '1', name: 'User 1', value: 400 },
];
const result = mergeArrayById(defaultItems, userItems);
expect(result).toHaveLength(2);
expect(result).toContainEqual({
id: '1',
name: 'User 1',
value: 400,
meta: { key: 'value' },
});
expect(result).toContainEqual({
id: '2',
name: 'User 2',
value: 300,
meta: { key: 'value' },
});
});
});
describe('special value handling', () => {
it('should handle undefined values by keeping default values', () => {
const defaultItems = [{ id: '1', name: 'Default', value: 100, meta: { key: 'value' } }];
const userItems = [{ id: '1', name: undefined, value: 200, meta: undefined }];
const result = mergeArrayById(defaultItems, userItems as any);
expect(result).toEqual([{ id: '1', name: 'Default', value: 200, meta: { key: 'value' } }]);
});
it('should handle nested objects correctly', () => {
const defaultItems = [
{
id: '1',
config: {
deep: {
value: 100,
keep: true,
},
surface: 'default',
},
},
];
const userItems = [
{
id: '1',
config: {
deep: {
value: 200,
},
surface: 'changed',
},
},
];
const result = mergeArrayById(defaultItems, userItems);
expect(result[0].config).toEqual({
deep: {
value: 200,
keep: true,
},
surface: 'changed',
});
});
});
describe('edge cases', () => {
it('should handle objects missing id property', () => {
const defaultItems = [{ id: '1', name: 'Default' }];
const userItems = [{ name: 'Invalid' }];
expect(mergeArrayById(defaultItems, userItems as any)).toEqual([
{ name: 'Invalid' },
{ id: '1', name: 'Default' },
]);
});
it('should preserve the source objects (no mutation)', () => {
const defaultItems = [{ id: '1', name: 'Default', meta: { key: 'value' } }];
const userItems = [{ id: '1', name: 'User' }];
const defaultItemsClone = JSON.parse(JSON.stringify(defaultItems));
const userItemsClone = JSON.parse(JSON.stringify(userItems));
mergeArrayById(defaultItems, userItems);
expect(defaultItems).toEqual(defaultItemsClone);
expect(userItems).toEqual(userItemsClone);
});
it('should handle duplicate IDs in user items by using the last occurrence', () => {
const defaultItems = [{ id: '1', name: 'Default', value: 100 }];
const userItems = [
{ id: '1', name: 'User 1', value: 200 },
{ id: '1', name: 'User 2', value: 300 }, // Duplicate ID
];
const result = mergeArrayById(defaultItems, userItems);
expect(result).toHaveLength(1);
expect(result[0]).toEqual({
id: '1',
name: 'User 2',
value: 300,
});
});
});
it('should merge data with not empty objects', () => {
const data = mergeArrayById(
[
{
abilities: {
reasoning: true,
functionCalling: true,
},
config: {
deploymentName: 'o1',
},
contextWindowTokens: 200000,
description:
'o1是OpenAI新的推理模型,支持图文输入并输出文本,适用于需要广泛通用知识的复杂任务。该模型具有200K上下文和2023年10月的知识截止日期。',
displayName: 'OpenAI o1',
enabled: true,
id: 'o1',
maxOutput: 100000,
pricing: {
input: 15,
output: 60,
},
releasedAt: '2024-12-17',
type: 'chat',
source: 'builtin',
},
],
[
{
abilities: {
reasoning: true,
},
config: {
deploymentName: 'ddd',
},
contextWindowTokens: 200000,
description:
'o1是OpenAI新的推理模型,支持图文输入并输出文本,适用于需要广泛通用知识的复杂任务。该模型具有200K上下文和2023年10月的知识截止日期。',
displayName: 'OpenAI o1',
enabled: true,
id: 'o1',
maxOutput: 100000,
releasedAt: '2024-12-17',
type: 'chat',
},
],
);
expect(data).toEqual([
{
abilities: {
functionCalling: true,
reasoning: true,
},
config: {
deploymentName: 'ddd',
},
contextWindowTokens: 200000,
description:
'o1是OpenAI新的推理模型,支持图文输入并输出文本,适用于需要广泛通用知识的复杂任务。该模型具有200K上下文和2023年10月的知识截止日期。',
displayName: 'OpenAI o1',
enabled: true,
id: 'o1',
pricing: {
input: 15,
output: 60,
},
source: 'builtin',
maxOutput: 100000,
releasedAt: '2024-12-17',
type: 'chat',
},
]);
});
});