Files
telegram-management-system/marketing-agent/tests/integration/api/campaigns.test.js
你的用户名 237c7802e5
Some checks failed
Deploy / deploy (push) Has been cancelled
Initial commit: Telegram Management System
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>
2025-11-04 15:37:50 +08:00

370 lines
12 KiB
JavaScript

import request from 'supertest';
import app from '../../../services/api-gateway/src/app.js';
import { connectDatabase, closeDatabase, clearDatabase } from '../../helpers/database.js';
import { createCampaign, createTelegramUser } from '../../helpers/factories.js';
describe('Campaigns API Integration Tests', () => {
let authToken;
beforeAll(async () => {
await connectDatabase();
// Login to get auth token
const loginResponse = await request(app)
.post('/api/v1/auth/login')
.send({
username: 'admin',
password: 'password123'
});
authToken = loginResponse.body.data.accessToken;
});
afterEach(async () => {
await clearDatabase();
});
afterAll(async () => {
await closeDatabase();
});
describe('GET /api/v1/orchestrator/campaigns', () => {
it('should list campaigns', async () => {
const response = await request(app)
.get('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data).toHaveProperty('campaigns');
expect(Array.isArray(response.body.data.campaigns)).toBe(true);
expect(response.body.data).toHaveProperty('pagination');
});
it('should filter campaigns by status', async () => {
const response = await request(app)
.get('/api/v1/orchestrator/campaigns?status=active')
.set('Authorization', `Bearer ${authToken}`);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
response.body.data.campaigns.forEach(campaign => {
expect(campaign.status).toBe('active');
});
});
it('should paginate campaigns', async () => {
const response = await request(app)
.get('/api/v1/orchestrator/campaigns?page=1&limit=5')
.set('Authorization', `Bearer ${authToken}`);
expect(response.status).toBe(200);
expect(response.body.data.campaigns.length).toBeLessThanOrEqual(5);
expect(response.body.data.pagination.page).toBe(1);
expect(response.body.data.pagination.limit).toBe(5);
});
it('should require authentication', async () => {
const response = await request(app)
.get('/api/v1/orchestrator/campaigns');
expect(response.status).toBe(401);
expect(response.body.success).toBe(false);
});
});
describe('POST /api/v1/orchestrator/campaigns', () => {
it('should create a new campaign', async () => {
const campaignData = createCampaign();
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);
expect(response.body.data.campaign).toHaveProperty('id');
expect(response.body.data.campaign.name).toBe(campaignData.name);
expect(response.body.data.campaign.type).toBe(campaignData.type);
});
it('should validate required fields', async () => {
const response = await request(app)
.post('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send({
// Missing required fields
description: 'Test campaign'
});
expect(response.status).toBe(400);
expect(response.body.success).toBe(false);
expect(response.body.error).toContain('required');
});
it('should validate campaign type', async () => {
const campaignData = createCampaign({ type: 'invalid_type' });
const response = await request(app)
.post('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send(campaignData);
expect(response.status).toBe(400);
expect(response.body.success).toBe(false);
expect(response.body.error).toContain('type');
});
});
describe('GET /api/v1/orchestrator/campaigns/:id', () => {
let campaignId;
beforeEach(async () => {
// Create a campaign
const campaignData = createCampaign();
const createResponse = await request(app)
.post('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send(campaignData);
campaignId = createResponse.body.data.campaign.id;
});
it('should get campaign by ID', async () => {
const response = await request(app)
.get(`/api/v1/orchestrator/campaigns/${campaignId}`)
.set('Authorization', `Bearer ${authToken}`);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data.campaign.id).toBe(campaignId);
});
it('should return 404 for non-existent campaign', async () => {
const response = await request(app)
.get('/api/v1/orchestrator/campaigns/nonexistent123')
.set('Authorization', `Bearer ${authToken}`);
expect(response.status).toBe(404);
expect(response.body.success).toBe(false);
expect(response.body.error).toContain('not found');
});
});
describe('PUT /api/v1/orchestrator/campaigns/:id', () => {
let campaignId;
beforeEach(async () => {
// Create a campaign
const campaignData = createCampaign();
const createResponse = await request(app)
.post('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send(campaignData);
campaignId = createResponse.body.data.campaign.id;
});
it('should update campaign', async () => {
const updates = {
name: 'Updated Campaign Name',
description: 'Updated description'
};
const response = await request(app)
.put(`/api/v1/orchestrator/campaigns/${campaignId}`)
.set('Authorization', `Bearer ${authToken}`)
.send(updates);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data.campaign.name).toBe(updates.name);
expect(response.body.data.campaign.description).toBe(updates.description);
});
it('should not update campaign type', async () => {
const response = await request(app)
.put(`/api/v1/orchestrator/campaigns/${campaignId}`)
.set('Authorization', `Bearer ${authToken}`)
.send({ type: 'different_type' });
expect(response.status).toBe(400);
expect(response.body.success).toBe(false);
expect(response.body.error).toContain('type');
});
});
describe('POST /api/v1/orchestrator/campaigns/:id/execute', () => {
let campaignId;
beforeEach(async () => {
// Create users for targeting
const users = await Promise.all([
createTelegramUser(),
createTelegramUser(),
createTelegramUser()
]);
// Create a campaign with users
const campaignData = createCampaign({
status: 'active',
targeting: {
includedUsers: users.map(u => u.telegramId)
}
});
const createResponse = await request(app)
.post('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send(campaignData);
campaignId = createResponse.body.data.campaign.id;
});
it('should execute campaign', async () => {
const response = await request(app)
.post(`/api/v1/orchestrator/campaigns/${campaignId}/execute`)
.set('Authorization', `Bearer ${authToken}`)
.send({ test: false });
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data).toHaveProperty('executionId');
expect(response.body.data.status).toBe('running');
});
it('should execute campaign in test mode', async () => {
const response = await request(app)
.post(`/api/v1/orchestrator/campaigns/${campaignId}/execute`)
.set('Authorization', `Bearer ${authToken}`)
.send({
test: true,
testUsers: ['testUser123']
});
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data).toHaveProperty('executionId');
});
it('should not execute draft campaign', async () => {
// Create draft campaign
const draftCampaign = createCampaign({ status: 'draft' });
const createResponse = await request(app)
.post('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send(draftCampaign);
const draftId = createResponse.body.data.campaign.id;
const response = await request(app)
.post(`/api/v1/orchestrator/campaigns/${draftId}/execute`)
.set('Authorization', `Bearer ${authToken}`)
.send({ test: false });
expect(response.status).toBe(400);
expect(response.body.success).toBe(false);
expect(response.body.error).toContain('not active');
});
});
describe('GET /api/v1/orchestrator/campaigns/:id/statistics', () => {
let campaignId;
beforeEach(async () => {
// Create and execute a campaign
const campaignData = createCampaign({
status: 'active',
statistics: {
messagesSent: 100,
delivered: 95,
read: 80,
clicked: 20,
conversions: 10
}
});
const createResponse = await request(app)
.post('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send(campaignData);
campaignId = createResponse.body.data.campaign.id;
});
it('should get campaign statistics', async () => {
const response = await request(app)
.get(`/api/v1/orchestrator/campaigns/${campaignId}/statistics`)
.set('Authorization', `Bearer ${authToken}`);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data.statistics).toHaveProperty('overview');
expect(response.body.data.statistics.overview).toHaveProperty('deliveryRate');
expect(response.body.data.statistics.overview).toHaveProperty('readRate');
expect(response.body.data.statistics.overview).toHaveProperty('conversionRate');
});
it('should filter statistics by date range', async () => {
const response = await request(app)
.get(`/api/v1/orchestrator/campaigns/${campaignId}/statistics?dateRange=last7days`)
.set('Authorization', `Bearer ${authToken}`);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.data.statistics).toHaveProperty('timeline');
});
});
describe('DELETE /api/v1/orchestrator/campaigns/:id', () => {
let campaignId;
beforeEach(async () => {
// Create a campaign
const campaignData = createCampaign({ status: 'draft' });
const createResponse = await request(app)
.post('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send(campaignData);
campaignId = createResponse.body.data.campaign.id;
});
it('should delete campaign', async () => {
const response = await request(app)
.delete(`/api/v1/orchestrator/campaigns/${campaignId}`)
.set('Authorization', `Bearer ${authToken}`);
expect(response.status).toBe(200);
expect(response.body.success).toBe(true);
expect(response.body.message).toContain('deleted');
// Verify deletion
const getResponse = await request(app)
.get(`/api/v1/orchestrator/campaigns/${campaignId}`)
.set('Authorization', `Bearer ${authToken}`);
expect(getResponse.status).toBe(404);
});
it('should not delete active campaign', async () => {
// Create active campaign
const activeCampaign = createCampaign({ status: 'active' });
const createResponse = await request(app)
.post('/api/v1/orchestrator/campaigns')
.set('Authorization', `Bearer ${authToken}`)
.send(activeCampaign);
const activeId = createResponse.body.data.campaign.id;
const response = await request(app)
.delete(`/api/v1/orchestrator/campaigns/${activeId}`)
.set('Authorization', `Bearer ${authToken}`);
expect(response.status).toBe(400);
expect(response.body.success).toBe(false);
expect(response.body.error).toContain('Cannot delete active campaign');
});
});
});