Files
wwjcloud-nest-v1/wwjcloud/test/database/index-manager.service.spec.ts
2025-09-11 22:06:19 +08:00

265 lines
7.7 KiB
TypeScript

import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository, DataSource } from 'typeorm';
import { IndexManagerService } from '../../src/core/database/indexManagerService';
/**
* IndexManagerService 单元测试
* 测试数据库索引管理服务的核心功能
*/
describe('IndexManagerService', () => {
let service: IndexManagerService;
let dataSource: DataSource;
let mockQueryRunner: any;
beforeEach(async () => {
// 创建模拟的查询运行器
mockQueryRunner = {
query: jest.fn(),
release: jest.fn(),
};
// 创建模拟的数据源
const mockDataSource = {
createQueryRunner: jest.fn().mockReturnValue(mockQueryRunner),
query: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
IndexManagerService,
{
provide: DataSource,
useValue: mockDataSource,
},
],
}).compile();
service = module.get<IndexManagerService>(IndexManagerService);
dataSource = module.get<DataSource>(DataSource);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('checkIndexExists', () => {
it('should return true when index exists', async () => {
// 模拟索引存在的查询结果
mockQueryRunner.query.mockResolvedValue([{ count: 1 }]);
const result = await service.checkIndexExists('test_table', 'test_index');
expect(result).toBe(true);
expect(mockQueryRunner.query).toHaveBeenCalledWith(
expect.stringContaining('SHOW INDEX FROM test_table'),
);
});
it('should return false when index does not exist', async () => {
// 模拟索引不存在的查询结果
mockQueryRunner.query.mockResolvedValue([]);
const result = await service.checkIndexExists('test_table', 'test_index');
expect(result).toBe(false);
});
it('should handle database errors gracefully', async () => {
// 模拟数据库错误
mockQueryRunner.query.mockRejectedValue(new Error('Database error'));
const result = await service.checkIndexExists('test_table', 'test_index');
expect(result).toBe(false);
});
});
describe('createIndex', () => {
it('should create single column index successfully', async () => {
mockQueryRunner.query.mockResolvedValue(undefined);
await service.createIndex('test_table', 'test_index', ['column1']);
expect(mockQueryRunner.query).toHaveBeenCalledWith(
'CREATE INDEX test_index ON test_table (column1)',
);
});
it('should create composite index successfully', async () => {
mockQueryRunner.query.mockResolvedValue(undefined);
await service.createIndex('test_table', 'test_composite_index', [
'column1',
'column2',
]);
expect(mockQueryRunner.query).toHaveBeenCalledWith(
'CREATE INDEX test_composite_index ON test_table (column1, column2)',
);
});
it('should handle index creation errors gracefully', async () => {
mockQueryRunner.query.mockRejectedValue(
new Error('Index creation failed'),
);
// 应该不抛出异常,而是记录日志
await expect(
service.createIndex('test_table', 'test_index', ['column1']),
).resolves.not.toThrow();
});
});
describe('getTableIndexes', () => {
it('should return table indexes', async () => {
const mockIndexes = [
{
Table: 'test_table',
Non_unique: 0,
Key_name: 'PRIMARY',
Seq_in_index: 1,
Column_name: 'id',
Collation: 'A',
Cardinality: 1000,
Sub_part: null,
Packed: null,
Null: '',
Index_type: 'BTREE',
Comment: '',
},
];
mockQueryRunner.query.mockResolvedValue(mockIndexes);
const result = await service.getTableIndexes('test_table');
expect(result).toEqual(mockIndexes);
expect(mockQueryRunner.query).toHaveBeenCalledWith(
'SHOW INDEX FROM test_table',
);
});
it('should handle errors when getting table indexes', async () => {
mockQueryRunner.query.mockRejectedValue(new Error('Query failed'));
const result = await service.getTableIndexes('test_table');
expect(result).toEqual([]);
});
});
describe('analyzeTable', () => {
it('should analyze table successfully', async () => {
mockQueryRunner.query.mockResolvedValue(undefined);
await service.analyzeTable('test_table');
expect(mockQueryRunner.query).toHaveBeenCalledWith(
'ANALYZE TABLE test_table',
);
});
it('should handle analyze table errors gracefully', async () => {
mockQueryRunner.query.mockRejectedValue(new Error('Analyze failed'));
await expect(service.analyzeTable('test_table')).resolves.not.toThrow();
});
});
describe('getIndexUsageStats', () => {
it('should return index usage statistics', async () => {
const mockStats = [
{
table_schema: 'test_db',
table_name: 'test_table',
index_name: 'test_index',
count_read: 100,
sum_timer_read: 1000000,
},
];
mockQueryRunner.query.mockResolvedValue(mockStats);
const result = await service.getIndexUsageStats();
expect(result).toEqual(mockStats);
expect(mockQueryRunner.query).toHaveBeenCalledWith(
expect.stringContaining(
'performance_schema.table_io_waits_summary_by_index_usage',
),
);
});
it('should handle errors when getting index usage stats', async () => {
mockQueryRunner.query.mockRejectedValue(new Error('Query failed'));
const result = await service.getIndexUsageStats();
expect(result).toEqual([]);
});
});
describe('checkAndCreateIndexes', () => {
it('should check and create all required indexes', async () => {
// 模拟所有索引都不存在
mockQueryRunner.query.mockResolvedValue([]);
const createIndexSpy = jest
.spyOn(service, 'createIndex')
.mockResolvedValue(undefined);
const checkIndexSpy = jest
.spyOn(service, 'checkIndexExists')
.mockResolvedValue(false);
await service.checkAndCreateIndexes();
// 验证检查了所有必要的索引
expect(checkIndexSpy).toHaveBeenCalledTimes(expect.any(Number));
expect(createIndexSpy).toHaveBeenCalledTimes(expect.any(Number));
createIndexSpy.mockRestore();
checkIndexSpy.mockRestore();
});
it('should skip creating existing indexes', async () => {
const createIndexSpy = jest
.spyOn(service, 'createIndex')
.mockResolvedValue(undefined);
const checkIndexSpy = jest
.spyOn(service, 'checkIndexExists')
.mockResolvedValue(true);
await service.checkAndCreateIndexes();
// 如果所有索引都存在,则不应该创建任何索引
expect(createIndexSpy).not.toHaveBeenCalled();
createIndexSpy.mockRestore();
checkIndexSpy.mockRestore();
});
});
describe('analyzeHotTables', () => {
it('should analyze all hot tables', async () => {
const analyzeTableSpy = jest
.spyOn(service, 'analyzeTable')
.mockResolvedValue(undefined);
await service.analyzeHotTables();
// 验证分析了所有热点表
expect(analyzeTableSpy).toHaveBeenCalledWith('member');
expect(analyzeTableSpy).toHaveBeenCalledWith('member_account_log');
expect(analyzeTableSpy).toHaveBeenCalledWith('pay');
expect(analyzeTableSpy).toHaveBeenCalledWith('pay_refund');
analyzeTableSpy.mockRestore();
});
});
});