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>
253 lines
8.6 KiB
JavaScript
253 lines
8.6 KiB
JavaScript
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;
|
|
});
|
|
});
|
|
}); |