courseworx/backend/plugins/financial-plugin/tests/currency.test.js
mmabdalla 5477297914 v2.0.2 - Complete Plugin Architecture System and Multi-Currency Implementation
Major Features Added:
- Complete Plugin Architecture System with financial plugin
- Multi-currency support with exchange rates
- Course type system (online, classroom, hybrid)
- Attendance tracking and QR code scanning
- Classroom sessions management
- Course sections and content management
- Professional video player with authentication
- Secure media serving system
- Shopping cart and checkout system
- Financial dashboard and earnings tracking
- Trainee progress tracking
- User notes and assignments system

Backend Infrastructure:
- Plugin loader and registry system
- Multi-currency database models
- Secure media middleware
- Course access middleware
- Financial plugin with payment processing
- Database migrations for new features
- API endpoints for all new functionality

Frontend Components:
- Course management interface
- Content creation and editing
- Section management with drag-and-drop
- Professional video player
- QR scanner for attendance
- Shopping cart and checkout flow
- Financial dashboard
- Plugin management interface
- Trainee details and progress views

This represents a major evolution of CourseWorx from a basic LMS to a comprehensive educational platform with plugin architecture.
2025-09-14 04:20:37 +03:00

344 lines
11 KiB
JavaScript

/**
* Multi-Currency System Tests
*
* This test suite validates the multi-currency functionality
* including currency management, exchange rates, and conversions.
*/
const { Currency, ExchangeRate, CourseCurrency, CurrencyService } = require('../models');
const { sequelize } = require('../../../config/database');
describe('Multi-Currency System', () => {
let testCurrencies = [];
let testExchangeRates = [];
beforeAll(async () => {
// Setup test database connection
await sequelize.authenticate();
});
beforeEach(async () => {
// Create test currencies
testCurrencies = await Currency.bulkCreate([
{
code: 'USD',
name: 'US Dollar',
symbol: '$',
decimalPlaces: 2,
isActive: true,
isBaseCurrency: true
},
{
code: 'EUR',
name: 'Euro',
symbol: '€',
decimalPlaces: 2,
isActive: true,
isBaseCurrency: false
},
{
code: 'EGP',
name: 'Egyptian Pound',
symbol: 'E£',
decimalPlaces: 2,
isActive: true,
isBaseCurrency: false
}
]);
// Create test exchange rates
testExchangeRates = await ExchangeRate.bulkCreate([
{
fromCurrencyId: testCurrencies[0].id, // USD
toCurrencyId: testCurrencies[1].id, // EUR
rate: 0.85,
effectiveDate: new Date(),
isActive: true,
source: 'manual'
},
{
fromCurrencyId: testCurrencies[0].id, // USD
toCurrencyId: testCurrencies[2].id, // EGP
rate: 30.50,
effectiveDate: new Date(),
isActive: true,
source: 'manual'
}
]);
});
afterEach(async () => {
// Clean up test data
await ExchangeRate.destroy({ where: {} });
await CourseCurrency.destroy({ where: {} });
await Currency.destroy({ where: {} });
});
afterAll(async () => {
await sequelize.close();
});
describe('Currency Management', () => {
test('should create currency with proper validation', async () => {
const currency = await Currency.create({
code: 'GBP',
name: 'British Pound Sterling',
symbol: '£',
decimalPlaces: 2,
isActive: true,
isBaseCurrency: false
});
expect(currency.code).toBe('GBP');
expect(currency.name).toBe('British Pound Sterling');
expect(currency.symbol).toBe('£');
expect(currency.decimalPlaces).toBe(2);
expect(currency.isActive).toBe(true);
expect(currency.isBaseCurrency).toBe(false);
});
test('should enforce unique currency codes', async () => {
await expect(
Currency.create({
code: 'USD', // Duplicate code
name: 'Duplicate USD',
symbol: '$',
decimalPlaces: 2,
isActive: true
})
).rejects.toThrow();
});
test('should get active currencies', async () => {
const activeCurrencies = await CurrencyService.getActiveCurrencies();
expect(activeCurrencies.length).toBeGreaterThan(0);
expect(activeCurrencies.every(c => c.isActive)).toBe(true);
});
test('should get currency by code', async () => {
const usd = await CurrencyService.getCurrencyByCode('USD');
expect(usd).toBeTruthy();
expect(usd.code).toBe('USD');
});
test('should get base currency', async () => {
const baseCurrency = await CurrencyService.getBaseCurrency();
expect(baseCurrency).toBeTruthy();
expect(baseCurrency.isBaseCurrency).toBe(true);
});
});
describe('Exchange Rate Management', () => {
test('should create exchange rate', async () => {
const exchangeRate = await ExchangeRate.create({
fromCurrencyId: testCurrencies[1].id, // EUR
toCurrencyId: testCurrencies[2].id, // EGP
rate: 35.88,
effectiveDate: new Date(),
isActive: true,
source: 'manual'
});
expect(exchangeRate.rate).toBe('35.88');
expect(exchangeRate.fromCurrencyId).toBe(testCurrencies[1].id);
expect(exchangeRate.toCurrencyId).toBe(testCurrencies[2].id);
});
test('should get exchange rate between currencies', async () => {
const rate = await CurrencyService.getExchangeRate(
testCurrencies[0].id, // USD
testCurrencies[1].id // EUR
);
expect(rate).toBeTruthy();
expect(parseFloat(rate.rate)).toBe(0.85);
});
test('should return null for non-existent exchange rate', async () => {
const rate = await CurrencyService.getExchangeRate(
testCurrencies[1].id, // EUR
testCurrencies[0].id // USD (reverse direction)
);
expect(rate).toBeNull();
});
});
describe('Currency Conversion', () => {
test('should convert amount between currencies', async () => {
const conversion = await CurrencyService.convertAmount(
100, // 100 USD
testCurrencies[0].id, // USD
testCurrencies[1].id // EUR
);
expect(conversion.originalAmount).toBe(100);
expect(conversion.convertedAmount).toBe(85); // 100 * 0.85
expect(conversion.exchangeRate).toBe(0.85);
});
test('should return same amount for same currency', async () => {
const conversion = await CurrencyService.convertAmount(
100,
testCurrencies[0].id, // USD
testCurrencies[0].id // USD
);
expect(conversion.originalAmount).toBe(100);
expect(conversion.convertedAmount).toBe(100);
expect(conversion.exchangeRate).toBe(1);
});
test('should throw error for non-existent exchange rate', async () => {
await expect(
CurrencyService.convertAmount(
100,
testCurrencies[1].id, // EUR
testCurrencies[0].id // USD (no rate exists)
)
).rejects.toThrow('Exchange rate not found');
});
});
describe('Course Currency Configuration', () => {
test('should create course currency configuration', async () => {
const courseCurrency = await CourseCurrency.create({
courseId: 'test-course-id',
baseCurrencyId: testCurrencies[0].id, // USD
basePrice: 99.99,
allowedPaymentCurrencies: [testCurrencies[1].id, testCurrencies[2].id], // EUR, EGP
isActive: true
});
expect(courseCurrency.courseId).toBe('test-course-id');
expect(courseCurrency.baseCurrencyId).toBe(testCurrencies[0].id);
expect(courseCurrency.basePrice).toBe('99.99');
expect(courseCurrency.allowedPaymentCurrencies).toEqual([
testCurrencies[1].id,
testCurrencies[2].id
]);
});
test('should get course currency configuration', async () => {
const courseCurrency = await CourseCurrency.create({
courseId: 'test-course-id',
baseCurrencyId: testCurrencies[0].id,
basePrice: 99.99,
allowedPaymentCurrencies: [testCurrencies[1].id],
isActive: true
});
const config = await CurrencyService.getCourseCurrencyConfig('test-course-id');
expect(config).toBeTruthy();
expect(config.courseId).toBe('test-course-id');
expect(config.baseCurrencyId).toBe(testCurrencies[0].id);
});
test('should return null for non-existent course configuration', async () => {
const config = await CurrencyService.getCourseCurrencyConfig('non-existent-course');
expect(config).toBeNull();
});
});
describe('Currency Formatting', () => {
test('should format USD currency', () => {
const usd = testCurrencies[0];
const formatted = CurrencyService.formatCurrency(99.99, usd);
expect(formatted).toBe('$99.99');
});
test('should format EUR currency', () => {
const eur = testCurrencies[1];
const formatted = CurrencyService.formatCurrency(85.50, eur);
expect(formatted).toBe('€85.50');
});
test('should format EGP currency', () => {
const egp = testCurrencies[2];
const formatted = CurrencyService.formatCurrency(3050.00, egp);
expect(formatted).toBe('E£3050.00');
});
test('should format without currency object', () => {
const formatted = CurrencyService.formatCurrency(99.99);
expect(formatted).toBe('99.99');
});
});
describe('Exchange Rate History', () => {
test('should track exchange rate changes', async () => {
const { ExchangeRateHistory } = require('../models');
// Update an exchange rate
const updatedRate = await CurrencyService.updateExchangeRate(
testCurrencies[0].id, // USD
testCurrencies[1].id, // EUR
0.90, // New rate
'test-user-id',
'Test rate update'
);
// Check history was created
const history = await ExchangeRateHistory.findOne({
where: {
fromCurrencyId: testCurrencies[0].id,
toCurrencyId: testCurrencies[1].id
}
});
expect(history).toBeTruthy();
expect(parseFloat(history.previousRate)).toBe(0.85);
expect(parseFloat(history.newRate)).toBe(0.90);
expect(history.changeReason).toBe('manual_update');
});
});
describe('Validation', () => {
test('should validate course currency configuration', async () => {
const validation = await CurrencyService.validateCourseCurrencyConfig(
'test-course-id',
testCurrencies[0].id, // Valid base currency
[testCurrencies[1].id, testCurrencies[2].id] // Valid payment currencies
);
expect(validation.isValid).toBe(true);
expect(validation.errors).toHaveLength(0);
});
test('should reject invalid base currency', async () => {
const validation = await CurrencyService.validateCourseCurrencyConfig(
'test-course-id',
'invalid-currency-id',
[testCurrencies[1].id]
);
expect(validation.isValid).toBe(false);
expect(validation.errors).toContain('Base currency is invalid or inactive');
});
test('should reject invalid payment currencies', async () => {
const validation = await CurrencyService.validateCourseCurrencyConfig(
'test-course-id',
testCurrencies[0].id,
['invalid-currency-id']
);
expect(validation.isValid).toBe(false);
expect(validation.errors).toContain('One or more allowed payment currencies are invalid or inactive');
});
});
});
// Integration tests
describe('Multi-Currency Integration', () => {
test('should handle complete course pricing workflow', async () => {
// This would test the complete workflow from course creation
// to payment processing with multiple currencies
// Implementation would depend on the full system integration
});
test('should handle payment processing with currency conversion', async () => {
// This would test payment processing with currency conversion
// Implementation would depend on payment system integration
});
});