const axios = require('axios'); const BASE_URL = 'http://localhost:3000/api'; // Test configuration const testConfig = { adminUser: { email: 'admin@example.com', password: 'Admin123!@#' }, regularUser: { email: 'stattest@example.com', password: 'Test123!@#' } }; // Test state let adminToken = null; let regularToken = null; // Test results let passedTests = 0; let failedTests = 0; const results = []; // Helper function to log test results function logTest(name, passed, error = null) { results.push({ name, passed, error }); if (passed) { console.log(`✓ ${name}`); passedTests++; } else { console.log(`✗ ${name}`); if (error) console.log(` Error: ${error}`); failedTests++; } } // Setup function async function setup() { console.log('Setting up test data...\n'); try { // Login admin user const adminLoginRes = await axios.post(`${BASE_URL}/auth/login`, { email: testConfig.adminUser.email, password: testConfig.adminUser.password }); adminToken = adminLoginRes.data.data.token; console.log('✓ Admin user logged in'); // Login regular user const userLoginRes = await axios.post(`${BASE_URL}/auth/login`, { email: testConfig.regularUser.email, password: testConfig.regularUser.password }); regularToken = userLoginRes.data.data.token; console.log('✓ Regular user logged in'); console.log('\n============================================================'); console.log('GUEST ANALYTICS API TESTS'); console.log('============================================================\n'); } catch (error) { console.error('Setup failed:', error.response?.data || error.message); process.exit(1); } } // Test functions async function testGetGuestAnalytics() { try { const response = await axios.get(`${BASE_URL}/admin/guest-analytics`, { headers: { Authorization: `Bearer ${adminToken}` } }); const passed = response.status === 200 && response.data.success === true && response.data.data !== undefined; logTest('Get guest analytics', passed); return response.data.data; } catch (error) { logTest('Get guest analytics', false, error.response?.data?.message || error.message); return null; } } async function testOverviewStructure(data) { if (!data) { logTest('Overview section structure', false, 'No data available'); return; } try { const overview = data.overview; const passed = overview !== undefined && typeof overview.totalGuestSessions === 'number' && typeof overview.activeGuestSessions === 'number' && typeof overview.expiredGuestSessions === 'number' && typeof overview.convertedGuestSessions === 'number' && typeof overview.conversionRate === 'number'; logTest('Overview section structure', passed); } catch (error) { logTest('Overview section structure', false, error.message); } } async function testQuizActivityStructure(data) { if (!data) { logTest('Quiz activity section structure', false, 'No data available'); return; } try { const quizActivity = data.quizActivity; const passed = quizActivity !== undefined && typeof quizActivity.totalGuestQuizzes === 'number' && typeof quizActivity.completedGuestQuizzes === 'number' && typeof quizActivity.guestQuizCompletionRate === 'number' && typeof quizActivity.avgQuizzesPerGuest === 'number' && typeof quizActivity.avgQuizzesBeforeConversion === 'number'; logTest('Quiz activity section structure', passed); } catch (error) { logTest('Quiz activity section structure', false, error.message); } } async function testBehaviorStructure(data) { if (!data) { logTest('Behavior section structure', false, 'No data available'); return; } try { const behavior = data.behavior; const passed = behavior !== undefined && typeof behavior.bounceRate === 'number' && typeof behavior.avgSessionDurationMinutes === 'number'; logTest('Behavior section structure', passed); } catch (error) { logTest('Behavior section structure', false, error.message); } } async function testRecentActivityStructure(data) { if (!data) { logTest('Recent activity section structure', false, 'No data available'); return; } try { const recentActivity = data.recentActivity; const passed = recentActivity !== undefined && recentActivity.last30Days !== undefined && typeof recentActivity.last30Days.newGuestSessions === 'number' && typeof recentActivity.last30Days.conversions === 'number'; logTest('Recent activity section structure', passed); } catch (error) { logTest('Recent activity section structure', false, error.message); } } async function testConversionRateCalculation(data) { if (!data) { logTest('Conversion rate calculation', false, 'No data available'); return; } try { const overview = data.overview; const expectedRate = overview.totalGuestSessions > 0 ? ((overview.convertedGuestSessions / overview.totalGuestSessions) * 100) : 0; // Allow small floating point difference const passed = Math.abs(overview.conversionRate - expectedRate) < 0.01 && overview.conversionRate >= 0 && overview.conversionRate <= 100; logTest('Conversion rate calculation', passed); } catch (error) { logTest('Conversion rate calculation', false, error.message); } } async function testQuizCompletionRateCalculation(data) { if (!data) { logTest('Quiz completion rate calculation', false, 'No data available'); return; } try { const quizActivity = data.quizActivity; const expectedRate = quizActivity.totalGuestQuizzes > 0 ? ((quizActivity.completedGuestQuizzes / quizActivity.totalGuestQuizzes) * 100) : 0; // Allow small floating point difference const passed = Math.abs(quizActivity.guestQuizCompletionRate - expectedRate) < 0.01 && quizActivity.guestQuizCompletionRate >= 0 && quizActivity.guestQuizCompletionRate <= 100; logTest('Quiz completion rate calculation', passed); } catch (error) { logTest('Quiz completion rate calculation', false, error.message); } } async function testBounceRateRange(data) { if (!data) { logTest('Bounce rate in valid range', false, 'No data available'); return; } try { const bounceRate = data.behavior.bounceRate; const passed = bounceRate >= 0 && bounceRate <= 100; logTest('Bounce rate in valid range', passed); } catch (error) { logTest('Bounce rate in valid range', false, error.message); } } async function testAveragesAreNonNegative(data) { if (!data) { logTest('Average values are non-negative', false, 'No data available'); return; } try { const passed = data.quizActivity.avgQuizzesPerGuest >= 0 && data.quizActivity.avgQuizzesBeforeConversion >= 0 && data.behavior.avgSessionDurationMinutes >= 0; logTest('Average values are non-negative', passed); } catch (error) { logTest('Average values are non-negative', false, error.message); } } async function testSessionCounts(data) { if (!data) { logTest('Session counts are consistent', false, 'No data available'); return; } try { const overview = data.overview; // Total should be >= sum of active, expired, and converted (some might be both expired and converted) const passed = overview.totalGuestSessions >= 0 && overview.activeGuestSessions >= 0 && overview.expiredGuestSessions >= 0 && overview.convertedGuestSessions >= 0 && overview.convertedGuestSessions <= overview.totalGuestSessions; logTest('Session counts are consistent', passed); } catch (error) { logTest('Session counts are consistent', false, error.message); } } async function testQuizCounts(data) { if (!data) { logTest('Quiz counts are consistent', false, 'No data available'); return; } try { const quizActivity = data.quizActivity; const passed = quizActivity.totalGuestQuizzes >= 0 && quizActivity.completedGuestQuizzes >= 0 && quizActivity.completedGuestQuizzes <= quizActivity.totalGuestQuizzes; logTest('Quiz counts are consistent', passed); } catch (error) { logTest('Quiz counts are consistent', false, error.message); } } async function testNonAdminBlocked() { try { await axios.get(`${BASE_URL}/admin/guest-analytics`, { headers: { Authorization: `Bearer ${regularToken}` } }); logTest('Non-admin user blocked', false, 'Regular user should not have access'); } catch (error) { const passed = error.response?.status === 403; logTest('Non-admin user blocked', passed, !passed ? `Expected 403, got ${error.response?.status}` : null); } } async function testUnauthenticated() { try { await axios.get(`${BASE_URL}/admin/guest-analytics`); logTest('Unauthenticated request blocked', false, 'Should require authentication'); } catch (error) { const passed = error.response?.status === 401; logTest('Unauthenticated request blocked', passed, !passed ? `Expected 401, got ${error.response?.status}` : null); } } // Main test runner async function runTests() { await setup(); console.log('Running tests...\n'); // Get analytics data const data = await testGetGuestAnalytics(); await new Promise(resolve => setTimeout(resolve, 100)); // Structure validation tests await testOverviewStructure(data); await new Promise(resolve => setTimeout(resolve, 100)); await testQuizActivityStructure(data); await new Promise(resolve => setTimeout(resolve, 100)); await testBehaviorStructure(data); await new Promise(resolve => setTimeout(resolve, 100)); await testRecentActivityStructure(data); await new Promise(resolve => setTimeout(resolve, 100)); // Calculation validation tests await testConversionRateCalculation(data); await new Promise(resolve => setTimeout(resolve, 100)); await testQuizCompletionRateCalculation(data); await new Promise(resolve => setTimeout(resolve, 100)); await testBounceRateRange(data); await new Promise(resolve => setTimeout(resolve, 100)); await testAveragesAreNonNegative(data); await new Promise(resolve => setTimeout(resolve, 100)); // Consistency tests await testSessionCounts(data); await new Promise(resolve => setTimeout(resolve, 100)); await testQuizCounts(data); await new Promise(resolve => setTimeout(resolve, 100)); // Authorization tests await testNonAdminBlocked(); await new Promise(resolve => setTimeout(resolve, 100)); await testUnauthenticated(); // Print results console.log('\n============================================================'); console.log(`RESULTS: ${passedTests} passed, ${failedTests} failed out of ${passedTests + failedTests} tests`); console.log('============================================================\n'); if (failedTests > 0) { console.log('Failed tests:'); results.filter(r => !r.passed).forEach(r => { console.log(` - ${r.name}`); if (r.error) console.log(` ${r.error}`); }); } process.exit(failedTests > 0 ? 1 : 0); } // Run tests runTests().catch(error => { console.error('Test execution failed:', error); process.exit(1); });