290 lines
8.6 KiB
JavaScript
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();
|