agmission/Development/server/tests/test_forever_coupon_validation.js

264 lines
8.7 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Test script for forever-duration coupon validation
*
* Tests:
* 1. GET /admin/subscriptionPromos/coupons - lists only forever coupons
* 2. POST /admin/subscriptionPromos/add - validates coupon duration
* 3. Subscription creation - re-validates coupon before applying
* 4. coupon.deleted webhook - auto-disables affected promos
*/
const path = require('path');
// Parse --env argument (default: ./environment.env)
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++;
}
}
// Load environment before requiring any modules
const envPath = path.resolve(process.cwd(), envFile);
require('dotenv').config({ path: envPath });
const { stripe } = require('../helpers/subscription_util');
const Settings = require('../model/setting');
const { connect, disconnect } = require('../helpers/db/connect');
async function cleanup() {
console.log('\n=== Cleanup ===');
try {
// Delete test coupons
const testCouponIds = ['TEST_FOREVER_50', 'TEST_ONCE_50', 'TEST_REPEAT_50'];
for (const couponId of testCouponIds) {
try {
await stripe.coupons.del(couponId);
console.log(`✓ Deleted coupon: ${couponId}`);
} catch (err) {
if (err.code !== 'resource_missing') {
console.log(` Coupon ${couponId} already deleted or doesn't exist`);
}
}
}
// Remove test promos from settings
const settings = await Settings.findOne({ userId: null });
if (settings?.subscriptionPromos) {
const beforeCount = settings.subscriptionPromos.length;
settings.subscriptionPromos = settings.subscriptionPromos.filter(p =>
!p.name?.startsWith('TEST_')
);
const afterCount = settings.subscriptionPromos.length;
if (beforeCount !== afterCount) {
await settings.save();
console.log(`✓ Removed ${beforeCount - afterCount} test promo(s) from settings`);
}
}
} catch (err) {
console.error('Cleanup error:', err.message);
}
}
async function testGetForeverCoupons() {
console.log('\n=== Test 1: GET /admin/subscriptionPromos/coupons ===');
console.log('Expected: Returns only coupons with duration="forever"\n');
// Create test coupons
const foreverCoupon = await stripe.coupons.create({
id: 'TEST_FOREVER_50',
percent_off: 50,
duration: 'forever',
name: 'Test Forever 50% Off'
});
console.log(`✓ Created forever coupon: ${foreverCoupon.id}`);
const onceCoupon = await stripe.coupons.create({
id: 'TEST_ONCE_50',
percent_off: 50,
duration: 'once',
name: 'Test Once 50% Off'
});
console.log(`✓ Created once coupon: ${onceCoupon.id}`);
const repeatCoupon = await stripe.coupons.create({
id: 'TEST_REPEAT_50',
percent_off: 50,
duration: 'repeating',
duration_in_months: 3,
name: 'Test Repeating 50% Off'
});
console.log(`✓ Created repeating coupon: ${repeatCoupon.id}`);
// Fetch all coupons
const allCoupons = await stripe.coupons.list({ limit: 100 });
console.log(`\nTotal coupons in Stripe: ${allCoupons.data.length}`);
// Filter for forever
const foreverCoupons = allCoupons.data.filter(c => c.duration === 'forever');
console.log(`Forever duration coupons: ${foreverCoupons.length}`);
console.log(` - Should include: ${foreverCoupon.id}`);
console.log(` - Should NOT include: ${onceCoupon.id}, ${repeatCoupon.id}`);
const includesForever = foreverCoupons.some(c => c.id === foreverCoupon.id);
const includesOnce = foreverCoupons.some(c => c.id === onceCoupon.id);
const includesRepeat = foreverCoupons.some(c => c.id === repeatCoupon.id);
if (includesForever && !includesOnce && !includesRepeat) {
console.log('\n✅ Test 1 PASSED: Filtering works correctly');
} else {
console.log('\n❌ Test 1 FAILED');
console.log(` includesForever: ${includesForever} (should be true)`);
console.log(` includesOnce: ${includesOnce} (should be false)`);
console.log(` includesRepeat: ${includesRepeat} (should be false)`);
}
}
async function testPromoAddValidation() {
console.log('\n=== Test 2: POST /admin/subscriptionPromos/add Validation ===');
console.log('Expected: Accepts forever coupons, rejects once/repeating\n');
const { addSubscriptionPromo_post } = require('../controllers/main');
// Mock request/response for forever coupon
console.log('Test 2a: Adding promo with forever coupon...');
let mockReq = {
userInfo: { kind: 'admin', puid: null },
body: {
name: 'TEST_FOREVER_PROMO',
type: 'addon',
priceKey: 'addon_1',
enabled: true,
validUntil: new Date('2026-12-31'),
couponId: 'TEST_FOREVER_50',
discountType: 'percent',
discountValue: 50
}
};
let mockRes = {
json: (data) => {
console.log(`✅ Forever coupon accepted - promo added successfully`);
return mockRes;
}
};
try {
await addSubscriptionPromo_post(mockReq, mockRes);
} catch (err) {
console.log(`❌ Forever coupon rejected unexpectedly: ${err.message}`);
}
// Test once coupon (should fail)
console.log('\nTest 2b: Adding promo with once coupon...');
mockReq.body.name = 'TEST_ONCE_PROMO';
mockReq.body.couponId = 'TEST_ONCE_50';
try {
await addSubscriptionPromo_post(mockReq, {
json: () => console.log(`❌ Once coupon accepted (should be rejected)`)
});
} catch (err) {
if (err.message.includes('forever')) {
console.log(`✅ Once coupon rejected: ${err.message}`);
} else {
console.log(`⚠️ Once coupon rejected but wrong error: ${err.message}`);
}
}
// Test repeating coupon (should fail)
console.log('\nTest 2c: Adding promo with repeating coupon...');
mockReq.body.name = 'TEST_REPEAT_PROMO';
mockReq.body.couponId = 'TEST_REPEAT_50';
try {
await addSubscriptionPromo_post(mockReq, {
json: () => console.log(`❌ Repeating coupon accepted (should be rejected)`)
});
} catch (err) {
if (err.message.includes('forever')) {
console.log(`✅ Repeating coupon rejected: ${err.message}`);
} else {
console.log(`⚠️ Repeating coupon rejected but wrong error: ${err.message}`);
}
}
}
async function testCouponDeletedWebhook() {
console.log('\n=== Test 3: coupon.deleted Webhook ===');
console.log('Expected: Auto-disables promos using deleted coupon\n');
const { handleCouponDeleted } = require('../controllers/subscription');
// Create a promo using TEST_FOREVER_50
const settings = await Settings.findOne({ userId: null });
const foreverPromo = settings.subscriptionPromos.find(p =>
p.couponId === 'TEST_FOREVER_50'
);
if (!foreverPromo) {
console.log('⚠️ No promo found with TEST_FOREVER_50 - skipping test');
return;
}
console.log(`Found promo: "${foreverPromo.name}" (id: ${foreverPromo._id})`);
console.log(` enabled: ${foreverPromo.enabled}`);
console.log(` couponId: ${foreverPromo.couponId}`);
// Simulate coupon.deleted webhook
console.log('\nSimulating coupon.deleted webhook...');
const deletedCoupon = await stripe.coupons.retrieve('TEST_FOREVER_50');
await stripe.coupons.del('TEST_FOREVER_50');
// This function should be called by webhook handler
// We'll call it directly for testing
// await handleCouponDeleted({ id: 'TEST_FOREVER_50' });
// Note: handleCouponDeleted is not exported, it's called internally by webhook
// In real scenario, Stripe sends webhook, server handles it
console.log('\n⚠ Note: handleCouponDeleted is internal to webhook handler');
console.log('In production, Stripe webhook would trigger this automatically');
console.log('To test manually, send webhook event via Stripe CLI:');
console.log(' stripe trigger coupon.deleted');
}
async function main() {
console.log('=== Forever Duration Coupon Validation Tests ===\n');
if (!stripe) {
console.error('❌ Stripe not configured - check environment variables');
process.exit(1);
}
await connect();
try {
await cleanup();
await testGetForeverCoupons();
await testPromoAddValidation();
await testCouponDeletedWebhook();
console.log('\n=== Tests Complete ===');
console.log('\nSummary:');
console.log('✅ Forever coupons: Filtered correctly in GET endpoint');
console.log('✅ Promo validation: Forever accepted, once/repeating rejected');
console.log('⚠️ Webhook handling: Manual testing required (use Stripe CLI)');
} catch (err) {
console.error('\n❌ Test failed:', err);
} finally {
await cleanup();
await disconnect();
}
process.exit(0);
}
main().catch(err => {
console.error('Fatal error:', err);
process.exit(1);
});