agmission/Development/server/tests/test_promo_enhancements.js

290 lines
8.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Test Promo Enhancements
*
* Tests the new promo features:
* - Priority-based matching
* - Eligibility checking (new_only, renew_only)
* - Repeating coupon support
* - Subscription history cache
*
* Usage:
* node tests/test_promo_enhancements.js [--env=./environment.env]
*/
const path = require('path');
// Parse --env argument
const args = process.argv.slice(2);
let envFile = './environment.env';
for (let i = 0; i < args.length; i++) {
if (args[i] === '--env' && args[i + 1]) {
envFile = args[i + 1];
i++;
} else if (args[i].startsWith('--env=')) {
envFile = args[i].split('=')[1];
}
}
// Load environment
const envPath = path.resolve(process.cwd(), envFile);
require('dotenv').config({ path: envPath });
const mongoose = require('mongoose');
const connect = require('../helpers/db/connect');
const Settings = require('../model/setting');
const SubscriptionHistory = require('../model/subscription_history');const { PromoEligibility } = require('../helpers/constants');const { stripe } = require('../helpers/subscription_util');
console.log('\n=== Promo Enhancement Tests ===\n');
async function testPromoSchema() {
console.log('1. Testing Promo Schema...');
try {
const settings = await Settings.findOne({ userId: null });
const testPromo = {
type: 'package',
priceKey: 'ess_1',
enabled: true,
validUntil: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000),
couponId: 'test_coupon_abc',
priority: 10,
chainable: false,
durationInMonths: 12,
eligibility: PromoEligibility.NEW_ONLY,
name: 'Test First Year Promo',
nameKey: 'TEST_PROMO',
discountType: 'percent',
discountValue: 30,
usageCount: 0
};
console.log(' ✓ Test promo object structure valid');
console.log(` - priority: ${testPromo.priority}`);
console.log(` - eligibility: ${testPromo.eligibility}`);
console.log(` - durationInMonths: ${testPromo.durationInMonths}`);
console.log(` - chainable: ${testPromo.chainable}`);
return true;
} catch (err) {
console.error(' ✗ Schema test failed:', err.message);
return false;
}
}
async function testSubscriptionHistoryCache() {
console.log('\n2. Testing Subscription History Cache...');
try {
// Create test history record
const testHistory = {
custId: 'cus_test_123',
type: 'package',
priceKey: 'ess_1',
firstSubscribedAt: new Date('2025-01-01'),
lastSubscribedAt: new Date(),
totalSubscriptions: 2,
currentSubscriptionId: 'sub_test_active',
lastSubscriptionStatus: 'active',
lastSyncedAt: new Date()
};
await SubscriptionHistory.findOneAndUpdate(
{ custId: testHistory.custId, type: testHistory.type, priceKey: testHistory.priceKey },
testHistory,
{ upsert: true, new: true }
);
console.log(' ✓ History cache record created');
// Query history
const found = await SubscriptionHistory.findOne({
custId: 'cus_test_123',
type: 'package',
priceKey: 'ess_1'
}).lean();
if (found) {
console.log(` ✓ History cache query successful`);
console.log(` - firstSubscribedAt: ${found.firstSubscribedAt.toISOString()}`);
console.log(` - totalSubscriptions: ${found.totalSubscriptions}`);
console.log(` - currentSubscriptionId: ${found.currentSubscriptionId}`);
console.log(` - lastSubscriptionStatus: ${found.lastSubscriptionStatus}`);
} else {
throw new Error('History record not found');
}
// Cleanup
await SubscriptionHistory.deleteOne({ custId: 'cus_test_123' });
console.log(' ✓ Cleanup complete');
return true;
} catch (err) {
console.error(' ✗ History cache test failed:', err.message);
return false;
}
}
async function testPriorityMatching() {
console.log('\n3. Testing Priority-Based Matching...');
try {
const promos = [
{
type: 'package',
priceKey: 'ess_1',
priority: 5,
name: 'Standard Promo',
enabled: true,
validUntil: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)
},
{
type: 'package',
priceKey: 'ess_1',
priority: 10,
name: 'Premium Promo',
enabled: true,
validUntil: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000)
}
];
// Sort by priority (descending)
promos.sort((a, b) => (b.priority || 0) - (a.priority || 0));
if (promos[0].name === 'Premium Promo') {
console.log(' ✓ Priority sorting works correctly');
console.log(` - Winner: ${promos[0].name} (priority: ${promos[0].priority})`);
} else {
throw new Error('Priority sorting failed');
}
return true;
} catch (err) {
console.error(' ✗ Priority matching test failed:', err.message);
return false;
}
}
async function testEligibilityLogic() {
console.log('\n4. Testing Eligibility Logic...');
try {
// Simulate eligibility checks
const scenarios = [
{ eligibility: PromoEligibility.ALL, hasHistory: true, expected: true },
{ eligibility: PromoEligibility.ALL, hasHistory: false, expected: true },
{ eligibility: PromoEligibility.NEW_ONLY, hasHistory: false, expected: true },
{ eligibility: PromoEligibility.NEW_ONLY, hasHistory: true, expected: false },
{ eligibility: PromoEligibility.RENEW_ONLY, hasHistory: true, expected: true },
{ eligibility: PromoEligibility.RENEW_ONLY, hasHistory: false, expected: false }
];
let passed = 0;
for (const scenario of scenarios) {
let isEligible;
if (scenario.eligibility === PromoEligibility.ALL) {
isEligible = true;
} else if (scenario.eligibility === PromoEligibility.NEW_ONLY) {
isEligible = !scenario.hasHistory;
} else if (scenario.eligibility === PromoEligibility.RENEW_ONLY) {
isEligible = scenario.hasHistory;
}
if (isEligible === scenario.expected) {
passed++;
} else {
console.error(` ✗ Failed: ${scenario.eligibility} + hasHistory=${scenario.hasHistory} → expected ${scenario.expected}, got ${isEligible}`);
}
}
console.log(` ✓ Eligibility logic: ${passed}/${scenarios.length} scenarios passed`);
return passed === scenarios.length;
} catch (err) {
console.error(' ✗ Eligibility test failed:', err.message);
return false;
}
}
async function testCouponDurationSupport() {
console.log('\n5. Testing Coupon Duration Support...');
try {
if (!stripe) {
console.log(' ⊘ Stripe not configured - skipping coupon validation test');
return true;
}
// Test validation logic
const validDurations = ['forever', 'repeating'];
const invalidDurations = ['once'];
console.log(` ✓ Valid durations: ${validDurations.join(', ')}`);
console.log(` ✓ Invalid durations: ${invalidDurations.join(', ')}`);
// Simulate duration check
for (const duration of validDurations) {
const isValid = ['forever', 'repeating'].includes(duration);
if (!isValid) {
throw new Error(`Duration '${duration}' should be valid but failed check`);
}
}
for (const duration of invalidDurations) {
const isValid = ['forever', 'repeating'].includes(duration);
if (isValid) {
throw new Error(`Duration '${duration}' should be invalid but passed check`);
}
}
console.log(' ✓ Duration validation logic correct');
return true;
} catch (err) {
console.error(' ✗ Coupon duration test failed:', err.message);
return false;
}
}
async function main() {
try {
await connect(false);
console.log('Connected to MongoDB\n');
const results = [];
results.push(await testPromoSchema());
results.push(await testSubscriptionHistoryCache());
results.push(await testPriorityMatching());
results.push(await testEligibilityLogic());
results.push(await testCouponDurationSupport());
const passed = results.filter(r => r).length;
const total = results.length;
console.log('\n=== Test Summary ===');
console.log(`Passed: ${passed}/${total}`);
console.log(`Failed: ${total - passed}/${total}`);
if (passed === total) {
console.log('\n✓ All tests passed!\n');
} else {
console.log('\n✗ Some tests failed\n');
process.exit(1);
}
} catch (err) {
console.error('\nFATAL ERROR:', err);
process.exit(1);
} finally {
await mongoose.connection.close();
process.exit(0);
}
}
main();