主要更新: 1. 后端核心底座完成 (M1-M6): - 健康检查、指标监控、分布式锁 - 事件总线、队列系统、事务管理 - 安全守卫、多租户隔离、存储适配器 - 审计日志、配置管理、多语言支持 2. 前端迁移到 Ant Design Vue: - 从 Element Plus 迁移到 Ant Design Vue - 完善 system 模块 (role/menu/dept) - 修复依赖和配置问题 3. 文档完善: - AI 开发工作流文档 - 架构约束和开发规范 - 项目进度跟踪 4. 其他改进: - 修复编译错误和类型问题 - 完善测试用例 - 优化项目结构
232 lines
7.3 KiB
TypeScript
232 lines
7.3 KiB
TypeScript
import { Test, TestingModule } from '@nestjs/testing';
|
|
import { INestApplication } from '@nestjs/common';
|
|
import request from 'supertest';
|
|
import { TestModule } from '../test.module';
|
|
import { TestService } from '../test.service';
|
|
import { UnifiedQueueService } from '../../src/core/queue/unified-queue.service';
|
|
import { DatabaseQueueProvider } from '../../src/core/queue/database-queue.provider';
|
|
import { QueueModule } from '../../src/core/queue/queue.module';
|
|
|
|
describe('Queue System (e2e)', () => {
|
|
let app: INestApplication;
|
|
let testService: TestService;
|
|
let unifiedQueueService: UnifiedQueueService;
|
|
let databaseQueueProvider: DatabaseQueueProvider;
|
|
|
|
beforeAll(async () => {
|
|
const moduleFixture: TestingModule = await Test.createTestingModule({
|
|
imports: [TestModule, QueueModule],
|
|
}).compile();
|
|
|
|
app = moduleFixture.createNestApplication();
|
|
await app.init();
|
|
|
|
testService = moduleFixture.get<TestService>(TestService);
|
|
unifiedQueueService = moduleFixture.get<UnifiedQueueService>(UnifiedQueueService);
|
|
databaseQueueProvider = moduleFixture.get<DatabaseQueueProvider>(DatabaseQueueProvider);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
if (app) {
|
|
await app.close();
|
|
}
|
|
});
|
|
|
|
describe('Test Controller Endpoints', () => {
|
|
it('/test/status (GET) - should return service status', () => {
|
|
return request(app.getHttpServer())
|
|
.get('/test/status')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).toHaveProperty('message');
|
|
expect(res.body).toHaveProperty('timestamp');
|
|
expect(res.body).toHaveProperty('services');
|
|
expect(res.body.services).toHaveProperty('redis');
|
|
expect(res.body.services).toHaveProperty('kafka');
|
|
});
|
|
});
|
|
|
|
it('/test/kafka (POST) - should publish event to Kafka', () => {
|
|
const testData = { test: 'kafka-event', value: 123 };
|
|
return request(app.getHttpServer())
|
|
.post('/test/kafka')
|
|
.send(testData)
|
|
.expect(201)
|
|
.expect((res) => {
|
|
expect(res.body).toHaveProperty('success', true);
|
|
expect(res.body).toHaveProperty('message');
|
|
expect(res.body).toHaveProperty('topic', 'test-topic');
|
|
expect(res.body).toHaveProperty('data', testData);
|
|
});
|
|
});
|
|
|
|
it('/test/redis (POST) - should enqueue job to Redis', () => {
|
|
const testData = { test: 'redis-job', value: 456 };
|
|
return request(app.getHttpServer())
|
|
.post('/test/redis')
|
|
.send(testData)
|
|
.expect(201)
|
|
.expect((res) => {
|
|
expect(res.body).toHaveProperty('success', true);
|
|
expect(res.body).toHaveProperty('message');
|
|
expect(res.body).toHaveProperty('jobId');
|
|
expect(res.body).toHaveProperty('data', testData);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('UnifiedQueueService', () => {
|
|
it('should be defined', () => {
|
|
expect(unifiedQueueService).toBeDefined();
|
|
});
|
|
|
|
it('should add task to queue', async () => {
|
|
const result = await unifiedQueueService.addTask('test-queue', {
|
|
data: { test: 'data' },
|
|
priority: 1,
|
|
delay: 0,
|
|
attempts: 3,
|
|
});
|
|
expect(result).toBeDefined();
|
|
});
|
|
|
|
it('should process task from queue', async () => {
|
|
let processedData: any = null;
|
|
|
|
await unifiedQueueService.processTask('test-queue', async (job: any) => {
|
|
processedData = job.data;
|
|
return { success: true };
|
|
});
|
|
|
|
// Add a task to be processed
|
|
await unifiedQueueService.addTask('test-queue', {
|
|
data: { test: 'process-data' },
|
|
priority: 1,
|
|
});
|
|
|
|
// Wait a bit for processing
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
|
expect(processedData).toBeDefined();
|
|
});
|
|
|
|
it('should publish event', async () => {
|
|
const event = {
|
|
eventType: 'test.event',
|
|
aggregateId: 'test-123',
|
|
aggregateType: 'Test',
|
|
version: '1.0',
|
|
occurredAt: new Date().toISOString(),
|
|
tenantId: 'tenant-1',
|
|
idempotencyKey: 'key-123',
|
|
traceId: 'trace-123',
|
|
data: { test: 'event-data' },
|
|
};
|
|
|
|
await expect(unifiedQueueService.publishEvent(event)).resolves.not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('DatabaseQueueProvider', () => {
|
|
it('should be defined', () => {
|
|
expect(databaseQueueProvider).toBeDefined();
|
|
});
|
|
|
|
it('should add job to database queue', async () => {
|
|
const jobData = {
|
|
type: 'test-job',
|
|
payload: { test: 'database-job' },
|
|
options: {
|
|
priority: 1,
|
|
delay: 0,
|
|
attempts: 3,
|
|
},
|
|
};
|
|
|
|
const result = await databaseQueueProvider.add('test-db-queue', jobData.type, jobData.payload, jobData.options);
|
|
expect(result).toBeDefined();
|
|
});
|
|
|
|
it('should publish event to database', async () => {
|
|
const event = {
|
|
eventType: 'test.database.event',
|
|
aggregateId: 'db-test-123',
|
|
aggregateType: 'DatabaseTest',
|
|
version: '1.0',
|
|
occurredAt: new Date().toISOString(),
|
|
tenantId: 'tenant-1',
|
|
idempotencyKey: 'db-key-123',
|
|
traceId: 'db-trace-123',
|
|
data: { test: 'database-event-data' },
|
|
};
|
|
|
|
await expect(databaseQueueProvider.publish(event)).resolves.not.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('Service Integration', () => {
|
|
it('should have all required services available', () => {
|
|
expect(testService).toBeDefined();
|
|
expect(unifiedQueueService).toBeDefined();
|
|
expect(databaseQueueProvider).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('Integration Tests', () => {
|
|
it('should handle complete queue workflow', async () => {
|
|
// Test the complete workflow: add task -> process task -> publish event
|
|
const taskData = { workflow: 'test', step: 1 };
|
|
|
|
// Add task
|
|
const taskResult = await unifiedQueueService.addTask('workflow-queue', {
|
|
data: taskData,
|
|
priority: 1,
|
|
});
|
|
expect(taskResult).toBeDefined();
|
|
|
|
// Process task and publish event
|
|
await unifiedQueueService.processTask('workflow-queue', async (job: any) => {
|
|
const event = {
|
|
eventType: 'workflow.completed',
|
|
aggregateId: 'workflow-123',
|
|
aggregateType: 'Workflow',
|
|
version: '1.0',
|
|
occurredAt: new Date().toISOString(),
|
|
tenantId: 'tenant-1',
|
|
idempotencyKey: 'workflow-key-123',
|
|
traceId: 'workflow-trace-123',
|
|
data: job.data,
|
|
};
|
|
|
|
await unifiedQueueService.publishEvent(event);
|
|
return { success: true, processed: job.data };
|
|
});
|
|
|
|
// Wait for processing
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
});
|
|
|
|
it('should handle error scenarios gracefully', async () => {
|
|
// Test error handling in task processing
|
|
await unifiedQueueService.processTask('error-queue', async (job: any) => {
|
|
if (job.data.shouldFail) {
|
|
throw new Error('Intentional test error');
|
|
}
|
|
return { success: true };
|
|
});
|
|
|
|
// Add a failing task
|
|
await unifiedQueueService.addTask('error-queue', {
|
|
data: { shouldFail: true },
|
|
priority: 1,
|
|
attempts: 1, // Only try once
|
|
});
|
|
|
|
// Wait for processing attempt
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
|
// The test passes if no unhandled errors are thrown
|
|
expect(true).toBe(true);
|
|
});
|
|
});
|
|
}); |