const { expect } = require('chai'); const TestSetup = require('../setup'); const AccountScheduler = require('../../src/service/AccountScheduler'); describe('AccountScheduler Service', function() { let accountScheduler; let testDb; before(async function() { this.timeout(10000); // Setup test database and data await TestSetup.setupDatabase(); await TestSetup.setupRedis(); await TestSetup.createTestData(); testDb = TestSetup.getTestDb(); accountScheduler = new AccountScheduler(); }); after(async function() { await TestSetup.cleanup(); }); describe('Initialization', function() { it('should initialize with default configuration', function() { expect(accountScheduler).to.be.instanceOf(AccountScheduler); expect(accountScheduler.schedulingStrategy).to.equal('health_priority'); expect(accountScheduler.isRunning).to.be.false; }); it('should start and stop service correctly', async function() { await accountScheduler.start(); expect(accountScheduler.isRunning).to.be.true; await accountScheduler.stop(); expect(accountScheduler.isRunning).to.be.false; }); }); describe('Account Selection', function() { beforeEach(async function() { await accountScheduler.start(); }); afterEach(async function() { await accountScheduler.stop(); }); it('should select optimal account based on health priority', async function() { const taskRequirements = { tier: 'normal', messageCount: 5, urgency: 'medium' }; const account = await accountScheduler.selectOptimalAccount(taskRequirements); expect(account).to.not.be.null; expect(account).to.have.property('accountId'); expect(account).to.have.property('healthScore'); expect(account.status).to.equal('active'); }); it('should exclude limited and banned accounts', async function() { const taskRequirements = { excludeStatuses: ['limited', 'banned'], messageCount: 3 }; const account = await accountScheduler.selectOptimalAccount(taskRequirements); if (account) { expect(['active', 'warning']).to.include(account.status); } }); it('should respect account limits', async function() { const taskRequirements = { messageCount: 100, // Very high count checkLimits: true }; const account = await accountScheduler.selectOptimalAccount(taskRequirements); if (account) { expect(account.todaySentCount + taskRequirements.messageCount).to.be.at.most(account.dailyLimit); } }); }); describe('Load Balancing', function() { beforeEach(async function() { await accountScheduler.start(); }); afterEach(async function() { await accountScheduler.stop(); }); it('should distribute tasks across multiple accounts', async function() { const selections = []; const taskRequirements = { messageCount: 1 }; // Select accounts multiple times for (let i = 0; i < 5; i++) { const account = await accountScheduler.selectOptimalAccount(taskRequirements); if (account) { selections.push(account.accountId); } } // Should have some variety in account selection const uniqueAccounts = new Set(selections); expect(uniqueAccounts.size).to.be.greaterThan(0); }); it('should update account usage after task completion', async function() { const account = await accountScheduler.selectOptimalAccount({ messageCount: 5 }); if (account) { const initialUsage = account.todaySentCount; await accountScheduler.updateAccountUsage(account.accountId, { sentCount: 5, success: true, executionTime: 1500 }); // Verify usage was updated const updatedAccount = await accountScheduler.getAccountById(account.accountId); expect(updatedAccount.todaySentCount).to.equal(initialUsage + 5); } }); }); describe('Risk Assessment', function() { beforeEach(async function() { await accountScheduler.start(); }); afterEach(async function() { await accountScheduler.stop(); }); it('should calculate account risk score', async function() { const account = await accountScheduler.selectOptimalAccount({ messageCount: 1 }); if (account) { const riskScore = accountScheduler.calculateAccountRisk(account); expect(riskScore).to.be.a('number'); expect(riskScore).to.be.at.least(0); expect(riskScore).to.be.at.most(100); } }); it('should prefer lower risk accounts', async function() { // Set strategy to risk-based accountScheduler.setSchedulingStrategy('risk_balanced'); const account = await accountScheduler.selectOptimalAccount({ messageCount: 1, riskTolerance: 'low' }); if (account) { expect(account.riskScore).to.be.at.most(50); // Low to medium risk } }); }); describe('Error Handling', function() { it('should handle database connection errors gracefully', async function() { // Mock database error const originalQuery = testDb.query; testDb.query = () => Promise.reject(new Error('Database connection lost')); const account = await accountScheduler.selectOptimalAccount({ messageCount: 1 }); expect(account).to.be.null; // Restore original query method testDb.query = originalQuery; }); it('should handle empty account pool', async function() { // Clear all accounts await testDb.query('DELETE FROM accounts_pool'); const account = await accountScheduler.selectOptimalAccount({ messageCount: 1 }); expect(account).to.be.null; // Restore test data await TestSetup.createTestData(); }); }); describe('Strategy Configuration', function() { beforeEach(async function() { await accountScheduler.start(); }); afterEach(async function() { await accountScheduler.stop(); }); it('should support different scheduling strategies', function() { const strategies = ['round_robin', 'health_priority', 'risk_balanced', 'random']; strategies.forEach(strategy => { accountScheduler.setSchedulingStrategy(strategy); expect(accountScheduler.schedulingStrategy).to.equal(strategy); }); }); it('should validate strategy parameters', function() { expect(() => { accountScheduler.setSchedulingStrategy('invalid_strategy'); }).to.throw(); }); }); describe('Performance Monitoring', function() { beforeEach(async function() { await accountScheduler.start(); }); afterEach(async function() { await accountScheduler.stop(); }); it('should track selection performance metrics', async function() { const startTime = Date.now(); await accountScheduler.selectOptimalAccount({ messageCount: 1 }); const endTime = Date.now(); const duration = endTime - startTime; // Selection should be reasonably fast (under 100ms) expect(duration).to.be.at.most(100); }); it('should provide service statistics', function() { const stats = accountScheduler.getServiceStats(); expect(stats).to.have.property('isRunning'); expect(stats).to.have.property('strategy'); expect(stats).to.have.property('totalSelections'); expect(stats.isRunning).to.be.true; }); }); });