Some checks failed
Deploy / deploy (push) Has been cancelled
Full-stack web application for Telegram management - Frontend: Vue 3 + Vben Admin - Backend: NestJS - Features: User management, group broadcast, statistics 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
638 lines
24 KiB
JavaScript
638 lines
24 KiB
JavaScript
const { expect } = require('chai');
|
|
const sinon = require('sinon');
|
|
const TestSetup = require('../setup');
|
|
|
|
// Import all services for integration testing
|
|
const TaskExecutionEngine = require('../../src/service/TaskExecutionEngine');
|
|
const AccountScheduler = require('../../src/service/AccountScheduler');
|
|
const RiskStrategyService = require('../../src/service/RiskStrategyService');
|
|
const BehaviorSimulationService = require('../../src/service/BehaviorSimulationService');
|
|
const ContentVariationService = require('../../src/service/ContentVariationService');
|
|
const AlertNotificationService = require('../../src/service/AlertNotificationService');
|
|
const MessageQueueService = require('../../src/service/MessageQueueService');
|
|
|
|
describe('Task Workflow Integration Tests', function() {
|
|
let testDb;
|
|
let testRedis;
|
|
let taskEngine;
|
|
let accountScheduler;
|
|
let riskService;
|
|
let behaviorService;
|
|
let contentService;
|
|
let alertService;
|
|
let queueService;
|
|
|
|
before(async function() {
|
|
this.timeout(20000);
|
|
|
|
// Setup test environment
|
|
await TestSetup.setupDatabase();
|
|
await TestSetup.setupRedis();
|
|
await TestSetup.createTestData();
|
|
|
|
testDb = TestSetup.getTestDb();
|
|
testRedis = TestSetup.getTestRedis();
|
|
|
|
// Initialize all services
|
|
taskEngine = new TaskExecutionEngine();
|
|
accountScheduler = new AccountScheduler();
|
|
riskService = new RiskStrategyService();
|
|
behaviorService = new BehaviorSimulationService();
|
|
contentService = new ContentVariationService();
|
|
alertService = new AlertNotificationService();
|
|
queueService = new MessageQueueService();
|
|
|
|
// Initialize services
|
|
await taskEngine.initialize();
|
|
await accountScheduler.start();
|
|
await riskService.initialize();
|
|
await behaviorService.initialize();
|
|
await contentService.initialize();
|
|
await alertService.initialize();
|
|
await queueService.initialize();
|
|
});
|
|
|
|
after(async function() {
|
|
// Cleanup all services
|
|
if (taskEngine) await taskEngine.shutdown();
|
|
if (accountScheduler) await accountScheduler.stop();
|
|
if (riskService) await riskService.shutdown();
|
|
if (behaviorService) await behaviorService.shutdown();
|
|
if (contentService) await contentService.shutdown();
|
|
if (alertService) await alertService.shutdown();
|
|
if (queueService) await queueService.shutdown();
|
|
|
|
await TestSetup.cleanup();
|
|
});
|
|
|
|
describe('Complete Task Execution Workflow', function() {
|
|
it('should execute a full task with all integrations', async function() {
|
|
this.timeout(15000);
|
|
|
|
const mockTask = {
|
|
id: 1,
|
|
name: 'Integration Test Task',
|
|
targetInfo: JSON.stringify({
|
|
targets: [
|
|
{ id: 'group1', name: 'Test Group 1', type: 'group' },
|
|
{ id: 'group2', name: 'Test Group 2', type: 'group' }
|
|
]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'Hello! This is a test message for integration testing.',
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'sequential',
|
|
interval: 2000,
|
|
batchSize: 1,
|
|
enableRiskControl: true,
|
|
enableBehaviorSimulation: true,
|
|
enableContentVariation: true
|
|
}),
|
|
status: 'pending'
|
|
};
|
|
|
|
// Mock external Telegram API calls
|
|
const telegramSendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({
|
|
success: true,
|
|
messageId: `msg_${Date.now()}`,
|
|
timestamp: new Date(),
|
|
executionTime: 1200
|
|
});
|
|
|
|
// Execute the complete workflow
|
|
const result = await taskEngine.executeTask(mockTask);
|
|
|
|
// Verify overall execution success
|
|
expect(result).to.have.property('success', true);
|
|
expect(result).to.have.property('taskId', mockTask.id);
|
|
expect(result).to.have.property('totalTargets', 2);
|
|
expect(result).to.have.property('successCount');
|
|
expect(result).to.have.property('failureCount');
|
|
expect(result).to.have.property('executionTime');
|
|
|
|
// Verify all targets were processed
|
|
expect(result.successCount + result.failureCount).to.equal(2);
|
|
|
|
// Verify Telegram API was called for each target
|
|
expect(telegramSendStub.callCount).to.equal(2);
|
|
|
|
telegramSendStub.restore();
|
|
});
|
|
|
|
it('should handle risk-based task modification', async function() {
|
|
this.timeout(10000);
|
|
|
|
const riskTask = {
|
|
id: 2,
|
|
name: 'Risk Control Test Task',
|
|
targetInfo: JSON.stringify({
|
|
targets: [{ id: 'group1', name: 'Test Group', type: 'group' }]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'This message should trigger risk controls.',
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'immediate',
|
|
enableRiskControl: true
|
|
}),
|
|
status: 'pending'
|
|
};
|
|
|
|
// Mock medium risk scenario
|
|
const riskEvalStub = sinon.stub(riskService, 'evaluateOverallRisk').resolves('medium');
|
|
const riskActionStub = sinon.stub(riskService, 'executeRiskAction').resolves({
|
|
action: 'delayed',
|
|
delay: 5000,
|
|
reason: 'Frequency threshold reached',
|
|
success: true
|
|
});
|
|
|
|
const telegramSendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({
|
|
success: true,
|
|
messageId: 'msg_risk_test',
|
|
timestamp: new Date()
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
const result = await taskEngine.executeTask(riskTask);
|
|
const endTime = Date.now();
|
|
|
|
// Verify risk control was applied (should have been delayed)
|
|
expect(result.success).to.be.true;
|
|
expect(endTime - startTime).to.be.at.least(5000); // Should have been delayed
|
|
|
|
// Verify risk evaluation was called
|
|
expect(riskEvalStub.called).to.be.true;
|
|
expect(riskActionStub.called).to.be.true;
|
|
|
|
riskEvalStub.restore();
|
|
riskActionStub.restore();
|
|
telegramSendStub.restore();
|
|
});
|
|
|
|
it('should integrate account switching on health issues', async function() {
|
|
this.timeout(10000);
|
|
|
|
const accountTask = {
|
|
id: 3,
|
|
name: 'Account Health Test Task',
|
|
targetInfo: JSON.stringify({
|
|
targets: [{ id: 'group1', name: 'Test Group', type: 'group' }]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'Testing account health-based switching.',
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'immediate',
|
|
enableRiskControl: true
|
|
}),
|
|
status: 'pending'
|
|
};
|
|
|
|
// Mock unhealthy account selection and switching
|
|
const selectStub = sinon.stub(accountScheduler, 'selectOptimalAccount');
|
|
selectStub.onFirstCall().resolves({
|
|
accountId: 3,
|
|
healthScore: 30, // Low health
|
|
status: 'warning',
|
|
tier: 'normal'
|
|
});
|
|
selectStub.onSecondCall().resolves({
|
|
accountId: 1,
|
|
healthScore: 85, // High health
|
|
status: 'active',
|
|
tier: 'normal'
|
|
});
|
|
|
|
const riskEvalStub = sinon.stub(riskService, 'evaluateOverallRisk').resolves('high');
|
|
const riskActionStub = sinon.stub(riskService, 'executeRiskAction').resolves({
|
|
action: 'switched',
|
|
originalAccount: 3,
|
|
newAccount: { accountId: 1, healthScore: 85 },
|
|
reason: 'Account health too low',
|
|
success: true
|
|
});
|
|
|
|
const telegramSendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({
|
|
success: true,
|
|
messageId: 'msg_health_test',
|
|
timestamp: new Date()
|
|
});
|
|
|
|
const result = await taskEngine.executeTask(accountTask);
|
|
|
|
// Verify task succeeded with account switching
|
|
expect(result.success).to.be.true;
|
|
expect(riskActionStub.called).to.be.true;
|
|
|
|
selectStub.restore();
|
|
riskEvalStub.restore();
|
|
riskActionStub.restore();
|
|
telegramSendStub.restore();
|
|
});
|
|
});
|
|
|
|
describe('Message Queue Integration', function() {
|
|
it('should process tasks through message queue', async function() {
|
|
this.timeout(10000);
|
|
|
|
const queuedTask = {
|
|
id: 'queue_integration_test',
|
|
taskData: {
|
|
id: 4,
|
|
name: 'Queued Task Test',
|
|
targetInfo: JSON.stringify({
|
|
targets: [{ id: 'group1', name: 'Test Group', type: 'group' }]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'This message was processed through the queue.',
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'queued',
|
|
priority: 'normal'
|
|
})
|
|
},
|
|
priority: 'normal',
|
|
attempts: 0
|
|
};
|
|
|
|
// Mock queue processing methods on task engine
|
|
const processQueuedStub = sinon.stub(taskEngine, 'processQueuedTask').resolves({
|
|
success: true,
|
|
jobId: queuedTask.id,
|
|
processedAt: new Date(),
|
|
executionTime: 1500
|
|
});
|
|
|
|
// Add task to queue
|
|
const jobId = await queueService.addJob('task_execution', queuedTask, {
|
|
priority: queuedTask.priority
|
|
});
|
|
|
|
expect(jobId).to.not.be.null;
|
|
|
|
// Simulate queue processing
|
|
const result = await taskEngine.processQueuedTask(queuedTask);
|
|
|
|
expect(result).to.have.property('success', true);
|
|
expect(result).to.have.property('jobId', queuedTask.id);
|
|
|
|
processQueuedStub.restore();
|
|
});
|
|
|
|
it('should handle queue failures with retry mechanism', async function() {
|
|
this.timeout(10000);
|
|
|
|
const failingTask = {
|
|
id: 'failing_queue_test',
|
|
taskData: {
|
|
id: 5,
|
|
targetInfo: JSON.stringify({
|
|
targets: [{ id: 'group1', type: 'group' }]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'This task will fail initially.',
|
|
type: 'text'
|
|
})
|
|
},
|
|
attempts: 0,
|
|
maxRetries: 2
|
|
};
|
|
|
|
let callCount = 0;
|
|
const processStub = sinon.stub(taskEngine, 'processQueuedTaskWithRetry').callsFake(async () => {
|
|
callCount++;
|
|
if (callCount === 1) {
|
|
throw new Error('First attempt failed');
|
|
}
|
|
return {
|
|
success: true,
|
|
jobId: failingTask.id,
|
|
attempts: callCount,
|
|
processedAt: new Date()
|
|
};
|
|
});
|
|
|
|
const result = await taskEngine.processQueuedTaskWithRetry(failingTask);
|
|
|
|
expect(result.success).to.be.true;
|
|
expect(callCount).to.equal(2); // Failed once, succeeded on retry
|
|
|
|
processStub.restore();
|
|
});
|
|
});
|
|
|
|
describe('Real-time Monitoring Integration', function() {
|
|
it('should emit monitoring events during task execution', async function() {
|
|
this.timeout(10000);
|
|
|
|
const monitoringTask = {
|
|
id: 6,
|
|
name: 'Monitoring Integration Test',
|
|
targetInfo: JSON.stringify({
|
|
targets: [{ id: 'group1', name: 'Test Group', type: 'group' }]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'This task tests monitoring integration.',
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'sequential',
|
|
enableMonitoring: true
|
|
}),
|
|
status: 'pending'
|
|
};
|
|
|
|
// Track monitoring events
|
|
const monitoringEvents = [];
|
|
|
|
taskEngine.on('taskStarted', (data) => {
|
|
monitoringEvents.push({ event: 'taskStarted', data });
|
|
});
|
|
|
|
taskEngine.on('taskProgress', (data) => {
|
|
monitoringEvents.push({ event: 'taskProgress', data });
|
|
});
|
|
|
|
taskEngine.on('taskCompleted', (data) => {
|
|
monitoringEvents.push({ event: 'taskCompleted', data });
|
|
});
|
|
|
|
const telegramSendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({
|
|
success: true,
|
|
messageId: 'msg_monitoring_test',
|
|
timestamp: new Date()
|
|
});
|
|
|
|
const result = await taskEngine.executeTask(monitoringTask);
|
|
|
|
// Verify task execution
|
|
expect(result.success).to.be.true;
|
|
|
|
// Verify monitoring events were emitted
|
|
expect(monitoringEvents.length).to.be.at.least(2); // At least start and complete
|
|
|
|
const startEvent = monitoringEvents.find(e => e.event === 'taskStarted');
|
|
const completeEvent = monitoringEvents.find(e => e.event === 'taskCompleted');
|
|
|
|
expect(startEvent).to.not.be.undefined;
|
|
expect(completeEvent).to.not.be.undefined;
|
|
|
|
telegramSendStub.restore();
|
|
});
|
|
|
|
it('should send alerts on critical issues', async function() {
|
|
this.timeout(10000);
|
|
|
|
const alertTask = {
|
|
id: 7,
|
|
name: 'Alert Integration Test',
|
|
targetInfo: JSON.stringify({
|
|
targets: [{ id: 'group1', name: 'Test Group', type: 'group' }]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'This task should trigger alerts.',
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'immediate'
|
|
}),
|
|
status: 'pending'
|
|
};
|
|
|
|
// Mock critical risk that should trigger alerts
|
|
const riskEvalStub = sinon.stub(riskService, 'evaluateOverallRisk').resolves('critical');
|
|
const riskActionStub = sinon.stub(riskService, 'executeRiskAction').resolves({
|
|
action: 'blocked',
|
|
reason: 'Critical security risk detected',
|
|
success: false
|
|
});
|
|
|
|
// Mock alert sending
|
|
const alertStub = sinon.stub(alertService, 'sendAlert').resolves({
|
|
sent: true,
|
|
channels: ['websocket'],
|
|
timestamp: new Date()
|
|
});
|
|
|
|
const result = await taskEngine.executeTask(alertTask);
|
|
|
|
// Verify task was blocked
|
|
expect(result.success).to.be.false;
|
|
|
|
// Verify alert was sent
|
|
expect(alertStub.called).to.be.true;
|
|
|
|
riskEvalStub.restore();
|
|
riskActionStub.restore();
|
|
alertStub.restore();
|
|
});
|
|
});
|
|
|
|
describe('Content and Behavior Integration', function() {
|
|
it('should apply content variation and behavior simulation', async function() {
|
|
this.timeout(10000);
|
|
|
|
const contentTask = {
|
|
id: 8,
|
|
name: 'Content Behavior Integration Test',
|
|
targetInfo: JSON.stringify({
|
|
targets: [
|
|
{ id: 'group1', name: 'Test Group 1', type: 'group' },
|
|
{ id: 'group2', name: 'Test Group 2', type: 'group' }
|
|
]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'Hello world! This is a test message that should be varied.',
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'sequential',
|
|
interval: 1000,
|
|
enableContentVariation: true,
|
|
enableBehaviorSimulation: true
|
|
}),
|
|
status: 'pending'
|
|
};
|
|
|
|
// Mock content variation
|
|
const variationStub = sinon.stub(contentService, 'generateVariation');
|
|
variationStub.onFirstCall().resolves({
|
|
content: 'Hi world! This is a test message that should be varied.',
|
|
variationsApplied: ['greeting_variation']
|
|
});
|
|
variationStub.onSecondCall().resolves({
|
|
content: 'Hello there! This is a test message that should be varied.',
|
|
variationsApplied: ['greeting_variation', 'casual_tone']
|
|
});
|
|
|
|
// Mock behavior simulation
|
|
const behaviorStub = sinon.stub(behaviorService, 'simulateHumanBehavior').resolves({
|
|
typingTime: 1500,
|
|
readingTime: 800,
|
|
delay: 300,
|
|
patterns: ['natural_typing', 'reading_pause']
|
|
});
|
|
|
|
const telegramSendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({
|
|
success: true,
|
|
messageId: 'msg_content_test',
|
|
timestamp: new Date()
|
|
});
|
|
|
|
const result = await taskEngine.executeTask(contentTask);
|
|
|
|
// Verify task execution
|
|
expect(result.success).to.be.true;
|
|
expect(result.totalTargets).to.equal(2);
|
|
|
|
// Verify content variation was applied
|
|
expect(variationStub.callCount).to.equal(2);
|
|
|
|
// Verify behavior simulation was applied
|
|
expect(behaviorStub.callCount).to.equal(2);
|
|
|
|
variationStub.restore();
|
|
behaviorStub.restore();
|
|
telegramSendStub.restore();
|
|
});
|
|
});
|
|
|
|
describe('Error Propagation and Recovery', function() {
|
|
it('should handle cascading service failures gracefully', async function() {
|
|
this.timeout(10000);
|
|
|
|
const errorTask = {
|
|
id: 9,
|
|
name: 'Error Handling Test',
|
|
targetInfo: JSON.stringify({
|
|
targets: [{ id: 'group1', name: 'Test Group', type: 'group' }]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'This task tests error handling.',
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'immediate'
|
|
}),
|
|
status: 'pending'
|
|
};
|
|
|
|
// Mock service failures
|
|
const schedulerErrorStub = sinon.stub(accountScheduler, 'selectOptimalAccount')
|
|
.rejects(new Error('Account scheduler database connection failed'));
|
|
|
|
const result = await taskEngine.executeTask(errorTask);
|
|
|
|
// Verify graceful error handling
|
|
expect(result.success).to.be.false;
|
|
expect(result).to.have.property('error');
|
|
expect(result.error).to.include('account');
|
|
|
|
schedulerErrorStub.restore();
|
|
});
|
|
|
|
it('should recover from temporary service failures', async function() {
|
|
this.timeout(10000);
|
|
|
|
const recoveryTask = {
|
|
id: 10,
|
|
name: 'Recovery Test',
|
|
targetInfo: JSON.stringify({
|
|
targets: [{ id: 'group1', name: 'Test Group', type: 'group' }]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: 'This task tests recovery mechanisms.',
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'immediate',
|
|
retryOnFailure: true,
|
|
maxRetries: 2
|
|
}),
|
|
status: 'pending'
|
|
};
|
|
|
|
// Mock temporary failure followed by success
|
|
let callCount = 0;
|
|
const telegramSendStub = sinon.stub(taskEngine, 'sendTelegramMessage').callsFake(() => {
|
|
callCount++;
|
|
if (callCount === 1) {
|
|
return Promise.reject(new Error('Temporary network failure'));
|
|
}
|
|
return Promise.resolve({
|
|
success: true,
|
|
messageId: 'msg_recovery_test',
|
|
timestamp: new Date()
|
|
});
|
|
});
|
|
|
|
const result = await taskEngine.executeTask(recoveryTask);
|
|
|
|
// Verify recovery was successful
|
|
expect(result.success).to.be.true;
|
|
expect(callCount).to.equal(2); // Failed once, succeeded on retry
|
|
|
|
telegramSendStub.restore();
|
|
});
|
|
});
|
|
|
|
describe('Performance Under Load', function() {
|
|
it('should handle multiple concurrent tasks', async function() {
|
|
this.timeout(20000);
|
|
|
|
const concurrentTasks = [];
|
|
|
|
// Create 5 concurrent tasks
|
|
for (let i = 0; i < 5; i++) {
|
|
concurrentTasks.push({
|
|
id: 100 + i,
|
|
name: `Concurrent Task ${i + 1}`,
|
|
targetInfo: JSON.stringify({
|
|
targets: [{ id: `group${i + 1}`, name: `Test Group ${i + 1}`, type: 'group' }]
|
|
}),
|
|
messageContent: JSON.stringify({
|
|
content: `Concurrent test message ${i + 1}`,
|
|
type: 'text'
|
|
}),
|
|
sendingStrategy: JSON.stringify({
|
|
type: 'immediate'
|
|
}),
|
|
status: 'pending'
|
|
});
|
|
}
|
|
|
|
// Mock Telegram API
|
|
const telegramSendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({
|
|
success: true,
|
|
messageId: 'concurrent_msg',
|
|
timestamp: new Date()
|
|
});
|
|
|
|
const startTime = Date.now();
|
|
|
|
// Execute all tasks concurrently
|
|
const promises = concurrentTasks.map(task => taskEngine.executeTask(task));
|
|
const results = await Promise.all(promises);
|
|
|
|
const endTime = Date.now();
|
|
const totalTime = endTime - startTime;
|
|
|
|
// Verify all tasks completed successfully
|
|
results.forEach((result, index) => {
|
|
expect(result.success).to.be.true;
|
|
expect(result.taskId).to.equal(100 + index);
|
|
});
|
|
|
|
// Verify reasonable performance (should complete within 10 seconds)
|
|
expect(totalTime).to.be.at.most(10000);
|
|
|
|
telegramSendStub.restore();
|
|
});
|
|
});
|
|
}); |