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>
449 lines
9.8 KiB
Markdown
449 lines
9.8 KiB
Markdown
# Testing Documentation
|
|
|
|
Comprehensive testing guide for the Telegram Marketing Agent System.
|
|
|
|
## Table of Contents
|
|
|
|
- [Testing Strategy](#testing-strategy)
|
|
- [Test Types](#test-types)
|
|
- [Running Tests](#running-tests)
|
|
- [Writing Tests](#writing-tests)
|
|
- [CI/CD Integration](#cicd-integration)
|
|
- [Coverage Goals](#coverage-goals)
|
|
- [Best Practices](#best-practices)
|
|
|
|
## Testing Strategy
|
|
|
|
Our testing strategy follows the Testing Pyramid approach:
|
|
|
|
```
|
|
/\
|
|
/E2E\ (5-10%)
|
|
/------\
|
|
/Integration\ (20-30%)
|
|
/------------\
|
|
/ Unit Tests \ (60-70%)
|
|
/-----------------\
|
|
```
|
|
|
|
### Test Categories
|
|
|
|
1. **Unit Tests**: Test individual functions, methods, and components in isolation
|
|
2. **Integration Tests**: Test interactions between components and services
|
|
3. **End-to-End Tests**: Test complete user workflows and scenarios
|
|
|
|
## Test Types
|
|
|
|
### Unit Tests
|
|
|
|
Located in `tests/unit/`, these tests cover:
|
|
- Service methods
|
|
- Utility functions
|
|
- Middleware logic
|
|
- Model validations
|
|
- Helper functions
|
|
|
|
Example structure:
|
|
```
|
|
tests/unit/
|
|
├── services/
|
|
│ ├── api-gateway/
|
|
│ │ ├── middleware/
|
|
│ │ │ ├── auth.test.js
|
|
│ │ │ └── rateLimiter.test.js
|
|
│ │ └── utils/
|
|
│ └── orchestrator/
|
|
│ └── campaignService.test.js
|
|
└── utils/
|
|
```
|
|
|
|
### Integration Tests
|
|
|
|
Located in `tests/integration/`, these tests cover:
|
|
- API endpoint functionality
|
|
- Database operations
|
|
- Service interactions
|
|
- External API mocking
|
|
|
|
Example structure:
|
|
```
|
|
tests/integration/
|
|
├── api/
|
|
│ ├── auth.test.js
|
|
│ ├── campaigns.test.js
|
|
│ └── users.test.js
|
|
└── services/
|
|
```
|
|
|
|
### End-to-End Tests
|
|
|
|
Located in `tests/e2e/`, these tests cover:
|
|
- Complete user workflows
|
|
- Multi-service interactions
|
|
- Real-world scenarios
|
|
|
|
Example structure:
|
|
```
|
|
tests/e2e/
|
|
├── campaigns/
|
|
│ └── campaignWorkflow.test.js
|
|
└── users/
|
|
└── userOnboarding.test.js
|
|
```
|
|
|
|
## Running Tests
|
|
|
|
### Prerequisites
|
|
|
|
```bash
|
|
# Install dependencies
|
|
npm install
|
|
|
|
# For each service
|
|
cd services/<service-name>
|
|
npm install
|
|
```
|
|
|
|
### All Tests
|
|
```bash
|
|
npm test
|
|
```
|
|
|
|
### Unit Tests Only
|
|
```bash
|
|
npm run test:unit
|
|
```
|
|
|
|
### Integration Tests Only
|
|
```bash
|
|
npm run test:integration
|
|
```
|
|
|
|
### E2E Tests Only
|
|
```bash
|
|
npm run test:e2e
|
|
```
|
|
|
|
### Watch Mode (for development)
|
|
```bash
|
|
npm run test:watch
|
|
```
|
|
|
|
### Coverage Report
|
|
```bash
|
|
npm run test:coverage
|
|
```
|
|
|
|
### Specific Test File
|
|
```bash
|
|
npx jest tests/unit/services/orchestrator/campaignService.test.js
|
|
```
|
|
|
|
### Test Pattern
|
|
```bash
|
|
npx jest --testNamePattern="should create campaign"
|
|
```
|
|
|
|
## Writing Tests
|
|
|
|
### Unit Test Example
|
|
|
|
```javascript
|
|
import { jest } from '@jest/globals';
|
|
import CampaignService from '../../../../services/orchestrator/src/services/campaignService.js';
|
|
import { createCampaign } from '../../../helpers/factories.js';
|
|
|
|
describe('CampaignService', () => {
|
|
let campaignService;
|
|
let mockDependency;
|
|
|
|
beforeEach(() => {
|
|
// Setup mocks
|
|
mockDependency = {
|
|
method: jest.fn()
|
|
};
|
|
|
|
campaignService = new CampaignService(mockDependency);
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('createCampaign', () => {
|
|
it('should create a new campaign', async () => {
|
|
// Arrange
|
|
const campaignData = createCampaign();
|
|
mockDependency.method.mockResolvedValue({ success: true });
|
|
|
|
// Act
|
|
const result = await campaignService.createCampaign(campaignData);
|
|
|
|
// Assert
|
|
expect(result).toHaveProperty('id');
|
|
expect(mockDependency.method).toHaveBeenCalledWith(expect.any(Object));
|
|
});
|
|
|
|
it('should handle errors gracefully', async () => {
|
|
// Arrange
|
|
mockDependency.method.mockRejectedValue(new Error('Database error'));
|
|
|
|
// Act & Assert
|
|
await expect(campaignService.createCampaign({}))
|
|
.rejects.toThrow('Database error');
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
### Integration Test Example
|
|
|
|
```javascript
|
|
import request from 'supertest';
|
|
import app from '../../../services/api-gateway/src/app.js';
|
|
import { connectDatabase, closeDatabase, clearDatabase } from '../../helpers/database.js';
|
|
|
|
describe('Campaigns API', () => {
|
|
let authToken;
|
|
|
|
beforeAll(async () => {
|
|
await connectDatabase();
|
|
authToken = await getAuthToken();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await clearDatabase();
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await closeDatabase();
|
|
});
|
|
|
|
describe('POST /api/v1/campaigns', () => {
|
|
it('should create campaign', async () => {
|
|
const response = await request(app)
|
|
.post('/api/v1/campaigns')
|
|
.set('Authorization', `Bearer ${authToken}`)
|
|
.send({
|
|
name: 'Test Campaign',
|
|
type: 'message'
|
|
});
|
|
|
|
expect(response.status).toBe(201);
|
|
expect(response.body.success).toBe(true);
|
|
expect(response.body.data.campaign).toHaveProperty('id');
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
### E2E Test Example
|
|
|
|
```javascript
|
|
describe('Campaign Workflow', () => {
|
|
it('should complete full campaign lifecycle', async () => {
|
|
// 1. Create template
|
|
const template = await createTemplate();
|
|
|
|
// 2. Import users
|
|
const users = await importUsers();
|
|
|
|
// 3. Create segment
|
|
const segment = await createSegment();
|
|
|
|
// 4. Create campaign
|
|
const campaign = await createCampaign({
|
|
templateId: template.id,
|
|
segmentId: segment.id
|
|
});
|
|
|
|
// 5. Execute campaign
|
|
const execution = await executeCampaign(campaign.id);
|
|
|
|
// 6. Verify results
|
|
expect(execution.status).toBe('completed');
|
|
expect(execution.messagesSent).toBe(users.length);
|
|
});
|
|
});
|
|
```
|
|
|
|
## Test Helpers and Utilities
|
|
|
|
### Database Helpers
|
|
```javascript
|
|
import { connectDatabase, closeDatabase, clearDatabase } from './helpers/database.js';
|
|
```
|
|
|
|
### Factory Functions
|
|
```javascript
|
|
import {
|
|
createUser,
|
|
createCampaign,
|
|
createTemplate
|
|
} from './helpers/factories.js';
|
|
```
|
|
|
|
### Authentication Helpers
|
|
```javascript
|
|
import { generateAuthToken, createAuthenticatedRequest } from './helpers/auth.js';
|
|
```
|
|
|
|
## CI/CD Integration
|
|
|
|
Tests run automatically on:
|
|
- Pull requests
|
|
- Commits to main/develop branches
|
|
- Before deployments
|
|
|
|
### GitHub Actions Workflow
|
|
|
|
See `.github/workflows/test.yml` for the complete CI configuration.
|
|
|
|
Key features:
|
|
- Matrix testing (Node.js 18.x, 20.x)
|
|
- Multiple database versions
|
|
- Parallel test execution
|
|
- Coverage reporting
|
|
- Test result artifacts
|
|
|
|
### Pre-commit Hooks
|
|
|
|
```bash
|
|
# Install husky
|
|
npm prepare
|
|
|
|
# Pre-commit hook runs:
|
|
- Linting
|
|
- Unit tests for changed files
|
|
- Commit message validation
|
|
```
|
|
|
|
## Coverage Goals
|
|
|
|
### Overall Coverage Targets
|
|
- **Statements**: 80%
|
|
- **Branches**: 70%
|
|
- **Functions**: 70%
|
|
- **Lines**: 80%
|
|
|
|
### Service-Specific Targets
|
|
- **API Gateway**: 85% (critical path)
|
|
- **Orchestrator**: 80%
|
|
- **Analytics**: 75%
|
|
- **User Management**: 80%
|
|
- **Scheduler**: 75%
|
|
|
|
### Viewing Coverage
|
|
|
|
```bash
|
|
# Generate HTML coverage report
|
|
npm run test:coverage
|
|
|
|
# Open in browser
|
|
open coverage/lcov-report/index.html
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### General Guidelines
|
|
|
|
1. **Test Naming**: Use descriptive test names that explain what is being tested
|
|
```javascript
|
|
// Good
|
|
it('should return 404 when campaign does not exist')
|
|
|
|
// Bad
|
|
it('test campaign')
|
|
```
|
|
|
|
2. **Arrange-Act-Assert**: Structure tests clearly
|
|
```javascript
|
|
it('should calculate discount correctly', () => {
|
|
// Arrange
|
|
const price = 100;
|
|
const discountRate = 0.2;
|
|
|
|
// Act
|
|
const result = calculateDiscount(price, discountRate);
|
|
|
|
// Assert
|
|
expect(result).toBe(80);
|
|
});
|
|
```
|
|
|
|
3. **Isolation**: Each test should be independent
|
|
- Use `beforeEach` and `afterEach` for setup/cleanup
|
|
- Don't rely on test execution order
|
|
- Clear mocks between tests
|
|
|
|
4. **Mocking**: Mock external dependencies
|
|
```javascript
|
|
jest.mock('axios');
|
|
axios.get.mockResolvedValue({ data: mockData });
|
|
```
|
|
|
|
5. **Async Testing**: Handle promises properly
|
|
```javascript
|
|
// Good
|
|
it('should handle async operation', async () => {
|
|
await expect(asyncFunction()).resolves.toBe(expected);
|
|
});
|
|
|
|
// Also good
|
|
it('should handle async operation', () => {
|
|
return expect(asyncFunction()).resolves.toBe(expected);
|
|
});
|
|
```
|
|
|
|
6. **Error Testing**: Test error cases thoroughly
|
|
```javascript
|
|
it('should throw error for invalid input', async () => {
|
|
await expect(functionUnderTest(null))
|
|
.rejects.toThrow('Input cannot be null');
|
|
});
|
|
```
|
|
|
|
### Performance Considerations
|
|
|
|
1. **Use Test Databases**: MongoDB Memory Server for unit tests
|
|
2. **Parallel Execution**: Run independent tests in parallel
|
|
3. **Selective Testing**: Use `--watch` mode during development
|
|
4. **Mock Heavy Operations**: Mock file I/O, network calls
|
|
|
|
### Security Testing
|
|
|
|
1. **Authentication**: Test all auth scenarios
|
|
2. **Authorization**: Verify role-based access
|
|
3. **Input Validation**: Test with malicious inputs
|
|
4. **Rate Limiting**: Verify limits are enforced
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
|
|
1. **Timeout Errors**
|
|
```javascript
|
|
// Increase timeout for specific test
|
|
it('should handle long operation', async () => {
|
|
// test code
|
|
}, 10000); // 10 second timeout
|
|
```
|
|
|
|
2. **Database Connection Issues**
|
|
- Ensure MongoDB/Redis are running
|
|
- Check connection strings in test environment
|
|
- Clear test database between runs
|
|
|
|
3. **Flaky Tests**
|
|
- Add proper waits for async operations
|
|
- Mock time-dependent functions
|
|
- Use stable test data
|
|
|
|
4. **Memory Leaks**
|
|
- Close all connections in `afterAll`
|
|
- Clear large data structures
|
|
- Use `--detectLeaks` flag
|
|
|
|
## Additional Resources
|
|
|
|
- [Jest Documentation](https://jestjs.io/docs/getting-started)
|
|
- [Supertest Documentation](https://github.com/visionmedia/supertest)
|
|
- [MongoDB Memory Server](https://github.com/nodkz/mongodb-memory-server)
|
|
- [Testing Best Practices](https://github.com/goldbergyoni/javascript-testing-best-practices) |