Initial commit: Telegram Management System
Some checks failed
Deploy / deploy (push) Has been cancelled
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>
This commit is contained in:
@@ -0,0 +1,282 @@
|
||||
import { jest } from '@jest/globals';
|
||||
import CampaignService from '../../../../services/orchestrator/src/services/campaignService.js';
|
||||
import Campaign from '../../../../services/orchestrator/src/models/Campaign.js';
|
||||
import { createCampaign } from '../../../helpers/factories.js';
|
||||
|
||||
// Mock dependencies
|
||||
jest.mock('../../../../services/orchestrator/src/models/Campaign.js');
|
||||
jest.mock('../../../../services/orchestrator/src/services/messagingService.js');
|
||||
jest.mock('../../../../services/orchestrator/src/services/analyticsService.js');
|
||||
|
||||
describe('CampaignService', () => {
|
||||
let campaignService;
|
||||
let mockMessagingService;
|
||||
let mockAnalyticsService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockMessagingService = {
|
||||
sendMessage: jest.fn(),
|
||||
validateMessage: jest.fn()
|
||||
};
|
||||
mockAnalyticsService = {
|
||||
trackEvent: jest.fn(),
|
||||
updateCampaignStats: jest.fn()
|
||||
};
|
||||
|
||||
campaignService = new CampaignService(mockMessagingService, mockAnalyticsService);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('createCampaign', () => {
|
||||
it('should create a new campaign', async () => {
|
||||
const campaignData = createCampaign();
|
||||
const savedCampaign = { ...campaignData, _id: 'camp123', save: jest.fn() };
|
||||
|
||||
Campaign.mockImplementation(() => savedCampaign);
|
||||
savedCampaign.save.mockResolvedValue(savedCampaign);
|
||||
|
||||
const result = await campaignService.createCampaign(campaignData, 'user123');
|
||||
|
||||
expect(Campaign).toHaveBeenCalledWith({
|
||||
...campaignData,
|
||||
createdBy: 'user123'
|
||||
});
|
||||
expect(savedCampaign.save).toHaveBeenCalled();
|
||||
expect(result).toEqual(savedCampaign);
|
||||
});
|
||||
|
||||
it('should validate required fields', async () => {
|
||||
const invalidData = { name: '' };
|
||||
|
||||
await expect(campaignService.createCampaign(invalidData, 'user123'))
|
||||
.rejects.toThrow('Campaign name is required');
|
||||
});
|
||||
|
||||
it('should track campaign creation', async () => {
|
||||
const campaignData = createCampaign();
|
||||
const savedCampaign = { ...campaignData, _id: 'camp123', save: jest.fn() };
|
||||
|
||||
Campaign.mockImplementation(() => savedCampaign);
|
||||
savedCampaign.save.mockResolvedValue(savedCampaign);
|
||||
|
||||
await campaignService.createCampaign(campaignData, 'user123');
|
||||
|
||||
expect(mockAnalyticsService.trackEvent).toHaveBeenCalledWith({
|
||||
event: 'campaign.created',
|
||||
campaignId: 'camp123',
|
||||
userId: 'user123',
|
||||
campaignType: campaignData.type
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeCampaign', () => {
|
||||
it('should execute campaign successfully', async () => {
|
||||
const campaign = {
|
||||
_id: 'camp123',
|
||||
status: 'active',
|
||||
targeting: {
|
||||
includedUsers: ['user1', 'user2', 'user3']
|
||||
},
|
||||
content: {
|
||||
customMessage: 'Test message'
|
||||
},
|
||||
settings: {
|
||||
rateLimit: {
|
||||
messagesPerSecond: 10
|
||||
}
|
||||
},
|
||||
save: jest.fn()
|
||||
};
|
||||
|
||||
Campaign.findById.mockResolvedValue(campaign);
|
||||
mockMessagingService.sendMessage.mockResolvedValue({ success: true });
|
||||
|
||||
const result = await campaignService.executeCampaign('camp123', {
|
||||
test: false
|
||||
});
|
||||
|
||||
expect(result.campaignId).toBe('camp123');
|
||||
expect(result.status).toBe('completed');
|
||||
expect(result.progress.total).toBe(3);
|
||||
expect(mockMessagingService.sendMessage).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should handle test mode execution', async () => {
|
||||
const campaign = {
|
||||
_id: 'camp123',
|
||||
status: 'active',
|
||||
targeting: {
|
||||
includedUsers: ['user1', 'user2', 'user3']
|
||||
},
|
||||
content: {
|
||||
customMessage: 'Test message'
|
||||
},
|
||||
save: jest.fn()
|
||||
};
|
||||
|
||||
Campaign.findById.mockResolvedValue(campaign);
|
||||
mockMessagingService.sendMessage.mockResolvedValue({ success: true });
|
||||
|
||||
const result = await campaignService.executeCampaign('camp123', {
|
||||
test: true,
|
||||
testUsers: ['testUser1']
|
||||
});
|
||||
|
||||
expect(result.isTest).toBe(true);
|
||||
expect(mockMessagingService.sendMessage).toHaveBeenCalledTimes(1);
|
||||
expect(mockMessagingService.sendMessage).toHaveBeenCalledWith(
|
||||
'testUser1',
|
||||
expect.any(Object)
|
||||
);
|
||||
});
|
||||
|
||||
it('should respect rate limits', async () => {
|
||||
const campaign = {
|
||||
_id: 'camp123',
|
||||
status: 'active',
|
||||
targeting: {
|
||||
includedUsers: Array(100).fill(null).map((_, i) => `user${i}`)
|
||||
},
|
||||
content: {
|
||||
customMessage: 'Test message'
|
||||
},
|
||||
settings: {
|
||||
rateLimit: {
|
||||
messagesPerSecond: 10
|
||||
}
|
||||
},
|
||||
save: jest.fn()
|
||||
};
|
||||
|
||||
Campaign.findById.mockResolvedValue(campaign);
|
||||
mockMessagingService.sendMessage.mockResolvedValue({ success: true });
|
||||
|
||||
const startTime = Date.now();
|
||||
await campaignService.executeCampaign('camp123', { test: false });
|
||||
const endTime = Date.now();
|
||||
|
||||
// With 100 users and 10 messages per second, it should take at least 9 seconds
|
||||
// (allowing for some margin)
|
||||
expect(endTime - startTime).toBeGreaterThanOrEqual(9000);
|
||||
});
|
||||
|
||||
it('should handle campaign not found', async () => {
|
||||
Campaign.findById.mockResolvedValue(null);
|
||||
|
||||
await expect(campaignService.executeCampaign('invalid123'))
|
||||
.rejects.toThrow('Campaign not found');
|
||||
});
|
||||
|
||||
it('should handle inactive campaign', async () => {
|
||||
const campaign = {
|
||||
_id: 'camp123',
|
||||
status: 'draft'
|
||||
};
|
||||
|
||||
Campaign.findById.mockResolvedValue(campaign);
|
||||
|
||||
await expect(campaignService.executeCampaign('camp123'))
|
||||
.rejects.toThrow('Campaign is not active');
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateCampaign', () => {
|
||||
it('should update campaign successfully', async () => {
|
||||
const campaign = {
|
||||
_id: 'camp123',
|
||||
name: 'Old Name',
|
||||
description: 'Old Description',
|
||||
save: jest.fn(),
|
||||
toObject: jest.fn(() => ({ _id: 'camp123', name: 'New Name' }))
|
||||
};
|
||||
|
||||
Campaign.findById.mockResolvedValue(campaign);
|
||||
campaign.save.mockResolvedValue(campaign);
|
||||
|
||||
const updates = {
|
||||
name: 'New Name',
|
||||
description: 'New Description'
|
||||
};
|
||||
|
||||
const result = await campaignService.updateCampaign('camp123', updates);
|
||||
|
||||
expect(campaign.name).toBe('New Name');
|
||||
expect(campaign.description).toBe('New Description');
|
||||
expect(campaign.save).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not allow updating campaign in progress', async () => {
|
||||
const campaign = {
|
||||
_id: 'camp123',
|
||||
status: 'executing'
|
||||
};
|
||||
|
||||
Campaign.findById.mockResolvedValue(campaign);
|
||||
|
||||
await expect(campaignService.updateCampaign('camp123', { name: 'New' }))
|
||||
.rejects.toThrow('Cannot update campaign while it is executing');
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteCampaign', () => {
|
||||
it('should delete campaign successfully', async () => {
|
||||
const campaign = {
|
||||
_id: 'camp123',
|
||||
status: 'draft',
|
||||
deleteOne: jest.fn()
|
||||
};
|
||||
|
||||
Campaign.findById.mockResolvedValue(campaign);
|
||||
|
||||
await campaignService.deleteCampaign('camp123');
|
||||
|
||||
expect(campaign.deleteOne).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not allow deleting active campaign', async () => {
|
||||
const campaign = {
|
||||
_id: 'camp123',
|
||||
status: 'active'
|
||||
};
|
||||
|
||||
Campaign.findById.mockResolvedValue(campaign);
|
||||
|
||||
await expect(campaignService.deleteCampaign('camp123'))
|
||||
.rejects.toThrow('Cannot delete active campaign');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCampaignStatistics', () => {
|
||||
it('should return campaign statistics', async () => {
|
||||
const campaign = {
|
||||
_id: 'camp123',
|
||||
statistics: {
|
||||
messagesSent: 100,
|
||||
delivered: 95,
|
||||
read: 80,
|
||||
clicked: 20,
|
||||
conversions: 10
|
||||
}
|
||||
};
|
||||
|
||||
Campaign.findById.mockResolvedValue(campaign);
|
||||
|
||||
const stats = await campaignService.getCampaignStatistics('camp123');
|
||||
|
||||
expect(stats).toEqual({
|
||||
overview: {
|
||||
messagesSent: 100,
|
||||
delivered: 95,
|
||||
deliveryRate: 95,
|
||||
read: 80,
|
||||
readRate: 84.21,
|
||||
clicked: 20,
|
||||
clickRate: 25,
|
||||
conversions: 10,
|
||||
conversionRate: 12.5
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user