#!/usr/bin/env node /** * Test Active Promos Endpoint * * Verifies that /api/activePromos returns V2 enhancement fields: * - priority * - eligibility * - durationInMonths * - chainable * * NOTE (v3.0): This test queries the database directly and does NOT test * the actual HTTP endpoint. For v3.0 authentication and eligibility filtering, * use test_active_promos_eligibility.js instead. */ 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++; } } // Load environment const envPath = path.resolve(process.cwd(), envFile); require('dotenv').config({ path: envPath }); const connect = require('../helpers/db/connect'); const Settings = require('../model/setting'); const moment = require('moment'); console.log('=== Active Promos Endpoint Test ===\n'); async function testActivePromosEndpoint() { try { await connect(false); console.log('1. Fetching active promos from database...'); const settings = await Settings.findOne({ userId: null }).lean(); const now = moment.utc(); const activePromos = (settings?.subscriptionPromos || []) .filter(p => { if (!p.enabled) return false; const validDate = p.validUntil; // Include if: // 1. Has validUntil in the future, OR // 2. Is a repeating coupon (has durationInMonths) without validUntil (self-expiring) if (validDate) { return moment.utc(validDate).isAfter(now); } else { return p.durationInMonths && p.durationInMonths > 0; } }); console.log(` Total promos in database: ${settings?.subscriptionPromos?.length || 0}`); console.log(` Active promos: ${activePromos.length}`); console.log(''); if (activePromos.length === 0) { console.log('⚠️ No active promos found. Create a test promo with:'); console.log(' - enabled: true'); console.log(' - validUntil: future date'); console.log(' - priority, eligibility, durationInMonths, chainable fields'); return 0; } console.log('2. Simulating endpoint response format...'); const responsePromos = activePromos.map(p => ({ type: p.type, priceKey: p.priceKey, validUntil: p.validUntil, name: p.name, nameKey: p.nameKey, descriptionKey: p.descriptionKey, discountType: p.discountType, discountValue: p.discountValue, // V2 Enhancement fields priority: p.priority || 0, eligibility: p.eligibility || 'all', durationInMonths: p.durationInMonths, chainable: p.chainable || false })); console.log(''); console.log('3. Verifying V2 fields are included:'); let allHaveV2Fields = true; responsePromos.forEach((promo, idx) => { console.log(`\n Promo ${idx + 1}: ${promo.name || 'Unnamed'}`); console.log(` - priority: ${promo.priority} ${typeof promo.priority === 'number' ? '✓' : '✗'}`); console.log(` - eligibility: ${promo.eligibility} ${promo.eligibility ? '✓' : '✗'}`); console.log(` - durationInMonths: ${promo.durationInMonths || 'N/A'} ${promo.durationInMonths ? '✓' : '(optional)'}`); console.log(` - chainable: ${promo.chainable} ${typeof promo.chainable === 'boolean' ? '✓' : '✗'}`); if (typeof promo.priority !== 'number' || !promo.eligibility || typeof promo.chainable !== 'boolean') { allHaveV2Fields = false; } }); console.log(''); console.log('4. Security check - couponId should NOT be included:'); const hasCouponId = responsePromos.some(p => p.couponId !== undefined); console.log(` couponId excluded: ${!hasCouponId ? '✓ YES' : '✗ NO (SECURITY ISSUE!)'}`); console.log(''); console.log('=== Test Summary ==='); if (allHaveV2Fields && !hasCouponId) { console.log('✓ All tests passed!'); console.log('Active promos endpoint correctly includes V2 fields without exposing couponId.'); return 0; } else { console.log('✗ Test failed!'); if (!allHaveV2Fields) console.log(' - Not all promos have required V2 fields'); if (hasCouponId) console.log(' - couponId is exposed (security issue)'); return 1; } } catch (err) { console.error('ERROR:', err.message); return 1; } finally { process.exit(0); } } testActivePromosEndpoint().then(code => process.exit(code));