/**
* getProjects 工具的单元测试
*/
import { getProjectsHandler } from '../../tools/getProjects';
import { ZenTaoClient } from '../../client';
import { LRUCache } from '../../utils/cache';
import { IProject } from '../../types';
// Mock ZenTaoClient
jest.mock('../../client');
const MockZenTaoClient = ZenTaoClient as jest.MockedClass<typeof ZenTaoClient>;
describe('getProjectsHandler', () => {
let mockClient: jest.Mocked<ZenTaoClient>;
let mockCache: LRUCache<string, any>;
beforeEach(() => {
jest.clearAllMocks();
// 创建 mock 客户端
mockClient = {
get: jest.fn()
} as any;
// 创建 mock 缓存
mockCache = {
get: jest.fn(),
set: jest.fn(),
size: 0,
maxSize: 1000,
delete: jest.fn(),
clear: jest.fn(),
cleanup: jest.fn(),
getStats: jest.fn(),
resetStats: jest.fn()
} as any;
});
describe('缓存命中', () => {
it('应该从缓存返回项目数据', async () => {
// Arrange
const cachedProjects = [
{
id: 1,
name: 'Test Project 1',
code: 'TP1',
status: 'active',
begin: '2024-01-01',
end: '2024-12-31',
acl: 'open'
}
];
const cachedData = {
projects: cachedProjects,
total: 1,
page: 1,
limit: 50
};
mockCache.get.mockReturnValue(cachedData);
const params = { page: 1, limit: 50 };
// Act
const result = await getProjectsHandler(mockClient, mockCache, params);
// Assert
expect(mockCache.get).toHaveBeenCalledWith('projects:{"page":1,"limit":50}');
expect(mockClient.get).not.toHaveBeenCalled();
expect(result.content[0].text).toContain('从缓存获取的项目列表');
expect(result.structuredContent).toEqual(cachedData);
});
});
describe('缓存未命中', () => {
it('应该从API获取项目数据', async () => {
// Arrange
const apiProjects: IProject[] = [
{
id: 1,
name: 'API Project 1',
code: 'AP1',
status: 'active',
begin: '2024-01-01',
end: '2024-12-31'
},
{
id: 2,
name: 'API Project 2',
code: 'AP2',
status: 'wait',
begin: '2024-02-01',
end: null
}
];
mockCache.get.mockReturnValue(null);
mockClient.get.mockResolvedValue(apiProjects);
const params = { page: 1, limit: 50, status: 'active' };
// Act
const result = await getProjectsHandler(mockClient, mockCache, params);
// Assert
expect(mockCache.get).toHaveBeenCalledWith('projects:{"page":1,"limit":50,"status":"active"}');
expect(mockClient.get).toHaveBeenCalledWith('/api.php/v1/projects', {
page: 1,
limit: 50,
status: 'active'
});
const expectedStructuredData = {
projects: apiProjects,
total: 2,
page: 1,
limit: 50
};
expect(mockCache.set).toHaveBeenCalledWith(
'projects:{"page":1,"limit":50,"status":"active"}',
expectedStructuredData,
10 * 60 * 1000 // 10分钟 TTL
);
expect(result.content[0].text).toContain('获取到 2 个项目');
expect(result.content[0].text).toContain('API Project 1');
expect(result.content[0].text).toContain('API Project 2');
expect(result.structuredContent).toEqual(expectedStructuredData);
});
it('应该正确处理默认参数', async () => {
// Arrange
const apiProjects: IProject[] = [];
mockCache.get.mockReturnValue(null);
mockClient.get.mockResolvedValue(apiProjects);
const params = {}; // 空参数,应该使用默认值
// Act
await getProjectsHandler(mockClient, mockCache, params);
// Assert
expect(mockClient.get).toHaveBeenCalledWith('/api.php/v1/projects', {
page: 1,
limit: 50
});
});
it('应该正确处理 orderBy 参数', async () => {
// Arrange
const apiProjects: IProject[] = [];
mockCache.get.mockReturnValue(null);
mockClient.get.mockResolvedValue(apiProjects);
const params = { orderBy: 'name_desc' };
// Act
await getProjectsHandler(mockClient, mockCache, params);
// Assert
expect(mockClient.get).toHaveBeenCalledWith('/api.php/v1/projects', {
page: 1,
limit: 50,
orderBy: 'name_desc'
});
});
});
describe('错误处理', () => {
it('应该处理API调用失败', async () => {
// Arrange
mockCache.get.mockReturnValue(null);
mockClient.get.mockRejectedValue(new Error('Network error'));
const params = { page: 1, limit: 50 };
// Act & Assert
await expect(getProjectsHandler(mockClient, mockCache, params))
.rejects.toThrow('获取项目列表失败: Network error');
expect(mockCache.get).toHaveBeenCalled();
expect(mockClient.get).toHaveBeenCalled();
expect(mockCache.set).not.toHaveBeenCalled();
});
it('应该处理API返回空数据', async () => {
// Arrange
const apiProjects: IProject[] = [];
mockCache.get.mockReturnValue(null);
mockClient.get.mockResolvedValue(apiProjects);
const params = { page: 1, limit: 50 };
// Act
const result = await getProjectsHandler(mockClient, mockCache, params);
// Assert
expect(result.content[0].text).toContain('获取到 0 个项目');
expect(result.structuredContent.projects).toEqual([]);
expect(result.structuredContent.total).toBe(0);
});
});
describe('数据格式化', () => {
it('应该正确格式化项目信息', async () => {
// Arrange
const apiProjects: IProject[] = [
{
id: 1,
name: 'Test Project',
code: 'TP',
status: 'active',
begin: '2024-01-01',
end: null,
acl: 'private',
team: 'dev-team'
}
];
mockCache.get.mockReturnValue(null);
mockClient.get.mockResolvedValue(apiProjects);
const params = { page: 1, limit: 50 };
// Act
const result = await getProjectsHandler(mockClient, mockCache, params);
// Assert
expect(result.content[0].text).toContain('Test Project (TP) - 状态: active');
expect(result.content[0].text).toContain('ID: 1');
expect(result.content[0].text).toContain('代码: TP');
expect(result.content[0].text).toContain('状态: active');
expect(result.content[0].text).toContain('开始: 2024-01-01');
expect(result.content[0].text).toContain('结束: 未设置');
});
});
});