const { expect } = require('chai'); const sinon = require('sinon'); const TestSetup = require('../setup'); const TaskExecutionEngine = require('../../src/service/TaskExecutionEngine'); describe('TaskExecutionEngine', function() { let taskEngine; let testDb; before(async function() { this.timeout(15000); await TestSetup.setupDatabase(); await TestSetup.setupRedis(); await TestSetup.createTestData(); testDb = TestSetup.getTestDb(); taskEngine = new TaskExecutionEngine(); await taskEngine.initialize(); }); after(async function() { if (taskEngine) { await taskEngine.shutdown(); } await TestSetup.cleanup(); }); describe('Initialization', function() { it('should initialize all components', function() { expect(taskEngine.isInitialized).to.be.true; expect(taskEngine.accountScheduler).to.not.be.null; expect(taskEngine.riskService).to.not.be.null; expect(taskEngine.behaviorSimulator).to.not.be.null; expect(taskEngine.contentVariator).to.not.be.null; }); it('should start and stop correctly', async function() { await taskEngine.start(); expect(taskEngine.isRunning).to.be.true; await taskEngine.stop(); expect(taskEngine.isRunning).to.be.false; // Restart for other tests await taskEngine.start(); }); }); describe('Task Execution Workflow', function() { let mockTask; beforeEach(function() { mockTask = { id: 1, name: '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!', type: 'text' }), sendingStrategy: JSON.stringify({ type: 'sequential', interval: 2000, batchSize: 1 }), status: 'pending' }; }); it('should execute task successfully', async function() { this.timeout(10000); // Mock the actual Telegram sending const sendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({ success: true, messageId: 'msg_123', timestamp: new Date() }); const result = await taskEngine.executeTask(mockTask); 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'); sendStub.restore(); }); it('should handle task execution with risk controls', async function() { this.timeout(10000); // Mock risk evaluation that returns medium risk const riskStub = sinon.stub(taskEngine.riskService, 'evaluateOverallRisk').resolves('medium'); const actionStub = sinon.stub(taskEngine.riskService, 'executeRiskAction').resolves({ action: 'delayed', delay: 3000, success: true }); const sendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({ success: true, messageId: 'msg_123' }); const result = await taskEngine.executeTask(mockTask); expect(result.success).to.be.true; expect(riskStub.called).to.be.true; riskStub.restore(); actionStub.restore(); sendStub.restore(); }); it('should handle account switching on high risk', async function() { this.timeout(10000); // Mock high risk scenario requiring account switch const riskStub = sinon.stub(taskEngine.riskService, 'evaluateOverallRisk').resolves('high'); const actionStub = sinon.stub(taskEngine.riskService, 'executeRiskAction').resolves({ action: 'switched', originalAccount: 1, newAccount: { accountId: 2, healthScore: 90 }, success: true }); const sendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({ success: true, messageId: 'msg_124' }); const result = await taskEngine.executeTask(mockTask); expect(result.success).to.be.true; expect(actionStub.called).to.be.true; riskStub.restore(); actionStub.restore(); sendStub.restore(); }); }); describe('Message Processing', function() { it('should apply content variation', async function() { const originalMessage = 'Hello world! This is a test message.'; const context = { account: { accountId: 1 }, target: { id: 'group1' }, variationLevel: 'medium' }; // Mock content variation const variationStub = sinon.stub(taskEngine.contentVariator, 'generateVariation').resolves({ content: 'Hi world! This is a test message.', variationsApplied: ['greeting_variation'] }); const result = await taskEngine.processMessageContent(originalMessage, context); expect(result).to.have.property('content'); expect(result).to.have.property('variationsApplied'); expect(variationStub.called).to.be.true; variationStub.restore(); }); it('should apply behavior simulation', async function() { const context = { account: { accountId: 1, tier: 'normal' }, message: { content: 'Test message', length: 12 } }; // Mock behavior simulation const behaviorStub = sinon.stub(taskEngine.behaviorSimulator, 'simulateHumanBehavior').resolves({ typingTime: 1200, readingTime: 800, delay: 500, patterns: ['natural_typing', 'reading_pause'] }); const result = await taskEngine.simulateBehavior(context); expect(result).to.have.property('typingTime'); expect(result).to.have.property('readingTime'); expect(result).to.have.property('delay'); expect(behaviorStub.called).to.be.true; behaviorStub.restore(); }); }); describe('Account Management Integration', function() { it('should select optimal account for task', async function() { const taskRequirements = { tier: 'normal', messageCount: 5, urgency: 'medium' }; // Mock account selection const scheduleStub = sinon.stub(taskEngine.accountScheduler, 'selectOptimalAccount').resolves({ accountId: 1, healthScore: 85, status: 'active', tier: 'normal' }); const account = await taskEngine.selectAccount(taskRequirements); expect(account).to.not.be.null; expect(account).to.have.property('accountId'); expect(scheduleStub.called).to.be.true; scheduleStub.restore(); }); it('should update account usage after successful send', async function() { const accountId = 1; const usageData = { sentCount: 1, success: true, executionTime: 1500, riskLevel: 'low' }; // Mock usage update const updateStub = sinon.stub(taskEngine.accountScheduler, 'updateAccountUsage').resolves(true); await taskEngine.updateAccountUsage(accountId, usageData); expect(updateStub.calledWith(accountId, usageData)).to.be.true; updateStub.restore(); }); }); describe('Error Handling', function() { it('should handle send failures gracefully', async function() { const mockTask = { id: 1, targetInfo: JSON.stringify({ targets: [{ id: 'group1', type: 'group' }] }), messageContent: JSON.stringify({ content: 'Test message' }), sendingStrategy: JSON.stringify({ type: 'sequential', interval: 1000 }) }; // Mock send failure const sendStub = sinon.stub(taskEngine, 'sendTelegramMessage').rejects( new Error('Rate limit exceeded') ); const result = await taskEngine.executeTask(mockTask); expect(result).to.have.property('success', false); expect(result).to.have.property('failureCount'); expect(result.failureCount).to.be.greaterThan(0); sendStub.restore(); }); it('should handle account unavailability', async function() { // Mock no available accounts const scheduleStub = sinon.stub(taskEngine.accountScheduler, 'selectOptimalAccount').resolves(null); const mockTask = { id: 1, targetInfo: JSON.stringify({ targets: [{ id: 'group1', type: 'group' }] }), messageContent: JSON.stringify({ content: 'Test message' }), sendingStrategy: JSON.stringify({ type: 'sequential' }) }; const result = await taskEngine.executeTask(mockTask); expect(result).to.have.property('success', false); expect(result).to.have.property('error'); expect(result.error).to.include('account'); scheduleStub.restore(); }); it('should handle critical risk blocking', async function() { // Mock critical risk that blocks execution const riskStub = sinon.stub(taskEngine.riskService, 'evaluateOverallRisk').resolves('critical'); const actionStub = sinon.stub(taskEngine.riskService, 'executeRiskAction').resolves({ action: 'blocked', reason: 'Critical security risk detected', success: false }); const mockTask = { id: 1, targetInfo: JSON.stringify({ targets: [{ id: 'group1', type: 'group' }] }), messageContent: JSON.stringify({ content: 'Test message' }), sendingStrategy: JSON.stringify({ type: 'sequential' }) }; const result = await taskEngine.executeTask(mockTask); expect(result).to.have.property('success', false); expect(result).to.have.property('error'); expect(result.error).to.include('blocked'); riskStub.restore(); actionStub.restore(); }); }); describe('Performance Monitoring', function() { it('should track execution metrics', async function() { const mockTask = { id: 1, targetInfo: JSON.stringify({ targets: [{ id: 'group1', type: 'group' }] }), messageContent: JSON.stringify({ content: 'Test message' }), sendingStrategy: JSON.stringify({ type: 'sequential' }) }; // Mock successful send const sendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({ success: true, messageId: 'msg_123', executionTime: 1200 }); const startTime = Date.now(); const result = await taskEngine.executeTask(mockTask); const endTime = Date.now(); expect(result).to.have.property('executionTime'); expect(result.executionTime).to.be.at.most(endTime - startTime + 100); // Allow some margin sendStub.restore(); }); it('should provide service statistics', function() { const stats = taskEngine.getServiceStats(); expect(stats).to.have.property('isRunning'); expect(stats).to.have.property('totalTasksExecuted'); expect(stats).to.have.property('successRate'); expect(stats).to.have.property('avgExecutionTime'); expect(stats).to.have.property('activeConnections'); }); }); describe('Configuration Management', function() { it('should update execution configuration', async function() { const newConfig = { maxConcurrentTasks: 10, defaultTimeout: 30000, retryAttempts: 3, enableBehaviorSimulation: true }; await taskEngine.updateConfiguration(newConfig); const config = taskEngine.getConfiguration(); expect(config.maxConcurrentTasks).to.equal(10); expect(config.defaultTimeout).to.equal(30000); }); it('should validate configuration parameters', function() { const invalidConfig = { maxConcurrentTasks: -1, // Invalid defaultTimeout: 'invalid' // Invalid type }; expect(() => { taskEngine.updateConfiguration(invalidConfig); }).to.throw(); }); }); describe('Integration with Message Queue', function() { it('should process queued tasks', async function() { const queuedTask = { id: 'queue_task_1', taskData: { id: 1, targetInfo: JSON.stringify({ targets: [{ id: 'group1', type: 'group' }] }), messageContent: JSON.stringify({ content: 'Queued message' }), sendingStrategy: JSON.stringify({ type: 'sequential' }) }, priority: 'normal' }; // Mock queue processing const sendStub = sinon.stub(taskEngine, 'sendTelegramMessage').resolves({ success: true, messageId: 'msg_queue_123' }); const result = await taskEngine.processQueuedTask(queuedTask); expect(result).to.have.property('success', true); expect(result).to.have.property('jobId', queuedTask.id); sendStub.restore(); }); it('should handle queue failures with retry', async function() { const queuedTask = { id: 'retry_task_1', taskData: { id: 1, targetInfo: JSON.stringify({ targets: [{ id: 'group1', type: 'group' }] }), messageContent: JSON.stringify({ content: 'Retry message' }) }, attempts: 0, maxRetries: 3 }; // Mock failure on first attempt, success on second let callCount = 0; const sendStub = sinon.stub(taskEngine, 'sendTelegramMessage').callsFake(() => { callCount++; if (callCount === 1) { return Promise.reject(new Error('Temporary failure')); } return Promise.resolve({ success: true, messageId: 'msg_retry_123' }); }); const result = await taskEngine.processQueuedTaskWithRetry(queuedTask); expect(result).to.have.property('success', true); expect(callCount).to.equal(2); // Failed once, succeeded on retry sendStub.restore(); }); }); });