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:
372
marketing-agent/tests/e2e/campaigns/campaignWorkflow.test.js
Normal file
372
marketing-agent/tests/e2e/campaigns/campaignWorkflow.test.js
Normal file
@@ -0,0 +1,372 @@
|
||||
import request from 'supertest';
|
||||
import app from '../../../services/api-gateway/src/app.js';
|
||||
import { connectDatabase, closeDatabase, clearDatabase } from '../../helpers/database.js';
|
||||
import { createCampaign, createTelegramUser, createTemplate } from '../../helpers/factories.js';
|
||||
|
||||
describe('Campaign Workflow E2E Tests', () => {
|
||||
let authToken;
|
||||
let userId;
|
||||
|
||||
beforeAll(async () => {
|
||||
await connectDatabase();
|
||||
|
||||
// Register and login
|
||||
const registerResponse = await request(app)
|
||||
.post('/api/v1/auth/register')
|
||||
.send({
|
||||
username: 'testuser',
|
||||
email: 'test@example.com',
|
||||
password: 'SecurePass123!',
|
||||
fullName: 'Test User'
|
||||
});
|
||||
|
||||
const loginResponse = await request(app)
|
||||
.post('/api/v1/auth/login')
|
||||
.send({
|
||||
username: 'testuser',
|
||||
password: 'SecurePass123!'
|
||||
});
|
||||
|
||||
authToken = loginResponse.body.data.accessToken;
|
||||
userId = loginResponse.body.data.user.id;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await clearDatabase();
|
||||
await closeDatabase();
|
||||
});
|
||||
|
||||
describe('Complete Campaign Lifecycle', () => {
|
||||
let campaignId;
|
||||
let templateId;
|
||||
let segmentId;
|
||||
let telegramUsers = [];
|
||||
|
||||
it('Step 1: Create message template', async () => {
|
||||
const templateData = createTemplate({
|
||||
name: 'Welcome Message',
|
||||
category: 'onboarding',
|
||||
content: {
|
||||
en: 'Welcome {{firstName}}! Thanks for joining our community.',
|
||||
es: '¡Bienvenido {{firstName}}! Gracias por unirte a nuestra comunidad.'
|
||||
}
|
||||
});
|
||||
|
||||
const response = await request(app)
|
||||
.post('/api/v1/templates')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(templateData);
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.body.success).toBe(true);
|
||||
templateId = response.body.data.template.id;
|
||||
});
|
||||
|
||||
it('Step 2: Import users', async () => {
|
||||
// Create users via API
|
||||
const usersData = [
|
||||
createTelegramUser({ tags: ['new', 'premium'] }),
|
||||
createTelegramUser({ tags: ['new', 'free'] }),
|
||||
createTelegramUser({ tags: ['existing', 'premium'] }),
|
||||
createTelegramUser({ tags: ['new', 'premium'] }),
|
||||
createTelegramUser({ tags: ['new', 'free'] })
|
||||
];
|
||||
|
||||
for (const userData of usersData) {
|
||||
const response = await request(app)
|
||||
.post('/api/v1/users')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(userData);
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
telegramUsers.push(response.body.data.user);
|
||||
}
|
||||
|
||||
expect(telegramUsers.length).toBe(5);
|
||||
});
|
||||
|
||||
it('Step 3: Create user segment', async () => {
|
||||
const segmentData = {
|
||||
name: 'New Premium Users',
|
||||
description: 'Users who are new and have premium subscription',
|
||||
criteria: [
|
||||
{
|
||||
field: 'tags',
|
||||
operator: 'contains',
|
||||
value: 'new',
|
||||
logic: 'AND'
|
||||
},
|
||||
{
|
||||
field: 'tags',
|
||||
operator: 'contains',
|
||||
value: 'premium',
|
||||
logic: 'AND'
|
||||
}
|
||||
],
|
||||
isDynamic: true
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.post('/api/v1/segments')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(segmentData);
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.body.success).toBe(true);
|
||||
segmentId = response.body.data.segment.id;
|
||||
|
||||
// Verify segment users
|
||||
const segmentUsersResponse = await request(app)
|
||||
.get(`/api/v1/segments/${segmentId}/users`)
|
||||
.set('Authorization', `Bearer ${authToken}`);
|
||||
|
||||
expect(segmentUsersResponse.status).toBe(200);
|
||||
expect(segmentUsersResponse.body.data.users.length).toBe(2); // Should match 2 users
|
||||
});
|
||||
|
||||
it('Step 4: Create campaign targeting segment', async () => {
|
||||
const campaignData = {
|
||||
name: 'Welcome Campaign for Premium Users',
|
||||
description: 'Onboarding campaign for new premium subscribers',
|
||||
type: 'message',
|
||||
content: {
|
||||
messageTemplateId: templateId
|
||||
},
|
||||
targeting: {
|
||||
segments: [segmentId]
|
||||
},
|
||||
settings: {
|
||||
rateLimit: {
|
||||
messagesPerSecond: 5,
|
||||
messagesPerUser: 1
|
||||
}
|
||||
},
|
||||
goals: {
|
||||
targetAudience: 2,
|
||||
conversionRate: 20,
|
||||
revenue: 1000
|
||||
}
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.post('/api/v1/orchestrator/campaigns')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(campaignData);
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.body.success).toBe(true);
|
||||
campaignId = response.body.data.campaign.id;
|
||||
});
|
||||
|
||||
it('Step 5: Test campaign execution', async () => {
|
||||
// First, test with a single user
|
||||
const testResponse = await request(app)
|
||||
.post(`/api/v1/orchestrator/campaigns/${campaignId}/execute`)
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send({
|
||||
test: true,
|
||||
testUsers: [telegramUsers[0].telegramId]
|
||||
});
|
||||
|
||||
expect(testResponse.status).toBe(200);
|
||||
expect(testResponse.body.success).toBe(true);
|
||||
expect(testResponse.body.data.status).toBe('running');
|
||||
});
|
||||
|
||||
it('Step 6: Activate and execute campaign', async () => {
|
||||
// Update campaign status to active
|
||||
const activateResponse = await request(app)
|
||||
.put(`/api/v1/orchestrator/campaigns/${campaignId}`)
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send({ status: 'active' });
|
||||
|
||||
expect(activateResponse.status).toBe(200);
|
||||
expect(activateResponse.body.data.campaign.status).toBe('active');
|
||||
|
||||
// Execute campaign
|
||||
const executeResponse = await request(app)
|
||||
.post(`/api/v1/orchestrator/campaigns/${campaignId}/execute`)
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send({ test: false });
|
||||
|
||||
expect(executeResponse.status).toBe(200);
|
||||
expect(executeResponse.body.success).toBe(true);
|
||||
expect(executeResponse.body.data).toHaveProperty('executionId');
|
||||
});
|
||||
|
||||
it('Step 7: Monitor campaign execution', async () => {
|
||||
// Get execution history
|
||||
const executionsResponse = await request(app)
|
||||
.get(`/api/v1/orchestrator/campaigns/${campaignId}/executions`)
|
||||
.set('Authorization', `Bearer ${authToken}`);
|
||||
|
||||
expect(executionsResponse.status).toBe(200);
|
||||
expect(executionsResponse.body.data.executions.length).toBeGreaterThan(0);
|
||||
|
||||
const latestExecution = executionsResponse.body.data.executions[0];
|
||||
expect(latestExecution).toHaveProperty('progress');
|
||||
expect(latestExecution.progress.total).toBe(2); // Should target 2 users from segment
|
||||
});
|
||||
|
||||
it('Step 8: View campaign statistics', async () => {
|
||||
// Wait a bit for execution to complete
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const statsResponse = await request(app)
|
||||
.get(`/api/v1/orchestrator/campaigns/${campaignId}/statistics`)
|
||||
.set('Authorization', `Bearer ${authToken}`);
|
||||
|
||||
expect(statsResponse.status).toBe(200);
|
||||
expect(statsResponse.body.success).toBe(true);
|
||||
expect(statsResponse.body.data.statistics.overview).toHaveProperty('messagesSent');
|
||||
expect(statsResponse.body.data.statistics.overview.messagesSent).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('Step 9: Create scheduled follow-up campaign', async () => {
|
||||
// Create follow-up campaign
|
||||
const followUpCampaign = {
|
||||
name: 'Premium User Engagement',
|
||||
description: 'Weekly engagement for premium users',
|
||||
type: 'message',
|
||||
content: {
|
||||
customMessage: 'Hi {{firstName}}, here are this week\'s premium features!'
|
||||
},
|
||||
targeting: {
|
||||
segments: [segmentId]
|
||||
},
|
||||
status: 'active'
|
||||
};
|
||||
|
||||
const campaignResponse = await request(app)
|
||||
.post('/api/v1/orchestrator/campaigns')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(followUpCampaign);
|
||||
|
||||
const followUpId = campaignResponse.body.data.campaign.id;
|
||||
|
||||
// Schedule it
|
||||
const scheduleData = {
|
||||
campaignId: followUpId,
|
||||
name: 'Weekly Premium Engagement',
|
||||
type: 'recurring',
|
||||
schedule: {
|
||||
startDateTime: new Date(Date.now() + 86400000).toISOString(), // Start tomorrow
|
||||
recurring: {
|
||||
pattern: 'weekly',
|
||||
frequency: {
|
||||
interval: 1,
|
||||
unit: 'week'
|
||||
},
|
||||
daysOfWeek: [1], // Monday
|
||||
time: '10:00',
|
||||
timezone: 'America/New_York'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const scheduleResponse = await request(app)
|
||||
.post('/api/v1/scheduler/scheduled-campaigns')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(scheduleData);
|
||||
|
||||
expect(scheduleResponse.status).toBe(201);
|
||||
expect(scheduleResponse.body.success).toBe(true);
|
||||
expect(scheduleResponse.body.data.schedule.type).toBe('recurring');
|
||||
});
|
||||
|
||||
it('Step 10: Setup webhook for campaign events', async () => {
|
||||
const webhookData = {
|
||||
name: 'Campaign Event Notifier',
|
||||
url: 'https://example.com/webhooks/campaigns',
|
||||
events: ['campaign.completed', 'campaign.failed', 'user.converted'],
|
||||
headers: {
|
||||
'X-Webhook-Secret': 'test-secret-key'
|
||||
},
|
||||
isActive: true
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.post('/api/v1/webhooks')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(webhookData);
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
expect(response.body.success).toBe(true);
|
||||
expect(response.body.data.webhook.events).toContain('campaign.completed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('A/B Testing Workflow', () => {
|
||||
let campaignId;
|
||||
let abTestId;
|
||||
|
||||
it('Should create campaign with A/B test', async () => {
|
||||
// Create base campaign
|
||||
const campaignData = createCampaign({
|
||||
name: 'A/B Test Campaign',
|
||||
status: 'active'
|
||||
});
|
||||
|
||||
const campaignResponse = await request(app)
|
||||
.post('/api/v1/orchestrator/campaigns')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(campaignData);
|
||||
|
||||
campaignId = campaignResponse.body.data.campaign.id;
|
||||
|
||||
// Create A/B test
|
||||
const abTestData = {
|
||||
name: 'Subject Line Test',
|
||||
campaignId: campaignId,
|
||||
variants: [
|
||||
{
|
||||
name: 'Control',
|
||||
weight: 50,
|
||||
content: {
|
||||
customMessage: '🎉 Special offer just for you!'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Variant A',
|
||||
weight: 50,
|
||||
content: {
|
||||
customMessage: '💰 Save 50% today only!'
|
||||
}
|
||||
}
|
||||
],
|
||||
metrics: ['open_rate', 'click_rate', 'conversion_rate'],
|
||||
sampleSize: 20 // 20% of audience
|
||||
};
|
||||
|
||||
const abTestResponse = await request(app)
|
||||
.post('/api/v1/ab-testing/experiments')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(abTestData);
|
||||
|
||||
expect(abTestResponse.status).toBe(201);
|
||||
abTestId = abTestResponse.body.data.experiment.id;
|
||||
|
||||
// Start A/B test
|
||||
const startResponse = await request(app)
|
||||
.post(`/api/v1/ab-testing/experiments/${abTestId}/start`)
|
||||
.set('Authorization', `Bearer ${authToken}`);
|
||||
|
||||
expect(startResponse.status).toBe(200);
|
||||
expect(startResponse.body.success).toBe(true);
|
||||
});
|
||||
|
||||
it('Should analyze A/B test results', async () => {
|
||||
// Wait for some data
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
const resultsResponse = await request(app)
|
||||
.get(`/api/v1/ab-testing/experiments/${abTestId}/results`)
|
||||
.set('Authorization', `Bearer ${authToken}`);
|
||||
|
||||
expect(resultsResponse.status).toBe(200);
|
||||
expect(resultsResponse.body.data.results).toHaveProperty('variants');
|
||||
expect(resultsResponse.body.data.results).toHaveProperty('winner');
|
||||
expect(resultsResponse.body.data.results).toHaveProperty('confidence');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user