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; let testCategoryId = 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'); // Get a test category ID const categoriesRes = await axios.get(`${BASE_URL}/categories`); if (categoriesRes.data.data && categoriesRes.data.data.categories && categoriesRes.data.data.categories.length > 0) { testCategoryId = categoriesRes.data.data.categories[0].id; console.log(`✓ Found test category: ${testCategoryId}`); } else { console.log('⚠ No test categories available (some tests will be skipped)'); } console.log('\n============================================================'); console.log('GUEST SETTINGS API TESTS'); console.log('============================================================\n'); } catch (error) { console.error('Setup failed:', error.response?.data || error.message); process.exit(1); } } // Test functions async function testGetDefaultSettings() { try { const response = await axios.get(`${BASE_URL}/admin/guest-settings`, { headers: { Authorization: `Bearer ${adminToken}` } }); const passed = response.status === 200 && response.data.success === true && response.data.data !== undefined && response.data.data.maxQuizzes !== undefined && response.data.data.expiryHours !== undefined && Array.isArray(response.data.data.publicCategories) && typeof response.data.data.featureRestrictions === 'object'; logTest('Get guest settings (default or existing)', passed); return response.data.data; } catch (error) { logTest('Get guest settings (default or existing)', false, error.response?.data?.message || error.message); return null; } } async function testSettingsStructure(settings) { if (!settings) { logTest('Settings structure validation', false, 'No settings data available'); return; } try { const hasMaxQuizzes = typeof settings.maxQuizzes === 'number'; const hasExpiryHours = typeof settings.expiryHours === 'number'; const hasPublicCategories = Array.isArray(settings.publicCategories); const hasFeatureRestrictions = typeof settings.featureRestrictions === 'object' && settings.featureRestrictions !== null && typeof settings.featureRestrictions.allowBookmarks === 'boolean' && typeof settings.featureRestrictions.allowReview === 'boolean' && typeof settings.featureRestrictions.allowPracticeMode === 'boolean' && typeof settings.featureRestrictions.allowTimedMode === 'boolean' && typeof settings.featureRestrictions.allowExamMode === 'boolean'; const passed = hasMaxQuizzes && hasExpiryHours && hasPublicCategories && hasFeatureRestrictions; logTest('Settings structure validation', passed); } catch (error) { logTest('Settings structure validation', false, error.message); } } async function testUpdateMaxQuizzes() { try { const response = await axios.put(`${BASE_URL}/admin/guest-settings`, { maxQuizzes: 5 }, { headers: { Authorization: `Bearer ${adminToken}` } } ); const passed = response.status === 200 && response.data.success === true && response.data.data.maxQuizzes === 5; logTest('Update max quizzes', passed); } catch (error) { logTest('Update max quizzes', false, error.response?.data?.message || error.message); } } async function testUpdateExpiryHours() { try { const response = await axios.put(`${BASE_URL}/admin/guest-settings`, { expiryHours: 48 }, { headers: { Authorization: `Bearer ${adminToken}` } } ); const passed = response.status === 200 && response.data.success === true && response.data.data.expiryHours === 48; logTest('Update expiry hours', passed); } catch (error) { logTest('Update expiry hours', false, error.response?.data?.message || error.message); } } async function testUpdatePublicCategories() { if (!testCategoryId) { logTest('Update public categories (skipped - no categories)', true); return; } try { const response = await axios.put(`${BASE_URL}/admin/guest-settings`, { publicCategories: [testCategoryId] }, { headers: { Authorization: `Bearer ${adminToken}` } } ); const passed = response.status === 200 && response.data.success === true && Array.isArray(response.data.data.publicCategories) && response.data.data.publicCategories.includes(testCategoryId); logTest('Update public categories', passed); } catch (error) { logTest('Update public categories', false, error.response?.data?.message || error.message); } } async function testUpdateFeatureRestrictions() { try { const response = await axios.put(`${BASE_URL}/admin/guest-settings`, { featureRestrictions: { allowBookmarks: true, allowTimedMode: true } }, { headers: { Authorization: `Bearer ${adminToken}` } } ); const passed = response.status === 200 && response.data.success === true && response.data.data.featureRestrictions.allowBookmarks === true && response.data.data.featureRestrictions.allowTimedMode === true; logTest('Update feature restrictions', passed); } catch (error) { logTest('Update feature restrictions', false, error.response?.data?.message || error.message); } } async function testUpdateMultipleFields() { try { const response = await axios.put(`${BASE_URL}/admin/guest-settings`, { maxQuizzes: 10, expiryHours: 72, featureRestrictions: { allowExamMode: true } }, { headers: { Authorization: `Bearer ${adminToken}` } } ); const passed = response.status === 200 && response.data.success === true && response.data.data.maxQuizzes === 10 && response.data.data.expiryHours === 72 && response.data.data.featureRestrictions.allowExamMode === true; logTest('Update multiple fields at once', passed); } catch (error) { logTest('Update multiple fields at once', false, error.response?.data?.message || error.message); } } async function testInvalidMaxQuizzes() { try { await axios.put(`${BASE_URL}/admin/guest-settings`, { maxQuizzes: 100 }, { headers: { Authorization: `Bearer ${adminToken}` } } ); logTest('Invalid max quizzes rejected (>50)', false, 'Should reject max quizzes > 50'); } catch (error) { const passed = error.response?.status === 400; logTest('Invalid max quizzes rejected (>50)', passed, !passed ? `Expected 400, got ${error.response?.status}` : null); } } async function testInvalidExpiryHours() { try { await axios.put(`${BASE_URL}/admin/guest-settings`, { expiryHours: 200 }, { headers: { Authorization: `Bearer ${adminToken}` } } ); logTest('Invalid expiry hours rejected (>168)', false, 'Should reject expiry hours > 168'); } catch (error) { const passed = error.response?.status === 400; logTest('Invalid expiry hours rejected (>168)', passed, !passed ? `Expected 400, got ${error.response?.status}` : null); } } async function testInvalidCategoryUUID() { try { await axios.put(`${BASE_URL}/admin/guest-settings`, { publicCategories: ['invalid-uuid'] }, { headers: { Authorization: `Bearer ${adminToken}` } } ); logTest('Invalid category UUID rejected', false, 'Should reject invalid UUID'); } catch (error) { const passed = error.response?.status === 400; logTest('Invalid category UUID rejected', passed, !passed ? `Expected 400, got ${error.response?.status}` : null); } } async function testNonExistentCategory() { try { await axios.put(`${BASE_URL}/admin/guest-settings`, { publicCategories: ['00000000-0000-0000-0000-000000000000'] }, { headers: { Authorization: `Bearer ${adminToken}` } } ); logTest('Non-existent category rejected', false, 'Should reject non-existent category'); } catch (error) { const passed = error.response?.status === 404; logTest('Non-existent category rejected', passed, !passed ? `Expected 404, got ${error.response?.status}` : null); } } async function testInvalidFeatureRestriction() { try { await axios.put(`${BASE_URL}/admin/guest-settings`, { featureRestrictions: { invalidField: true } }, { headers: { Authorization: `Bearer ${adminToken}` } } ); logTest('Invalid feature restriction field rejected', false, 'Should reject invalid field'); } catch (error) { const passed = error.response?.status === 400; logTest('Invalid feature restriction field rejected', passed, !passed ? `Expected 400, got ${error.response?.status}` : null); } } async function testNonBooleanFeatureRestriction() { try { await axios.put(`${BASE_URL}/admin/guest-settings`, { featureRestrictions: { allowBookmarks: 'yes' } }, { headers: { Authorization: `Bearer ${adminToken}` } } ); logTest('Non-boolean feature restriction rejected', false, 'Should reject non-boolean value'); } catch (error) { const passed = error.response?.status === 400; logTest('Non-boolean feature restriction rejected', passed, !passed ? `Expected 400, got ${error.response?.status}` : null); } } async function testNonAdminGetBlocked() { try { await axios.get(`${BASE_URL}/admin/guest-settings`, { headers: { Authorization: `Bearer ${regularToken}` } }); logTest('Non-admin GET blocked', false, 'Regular user should not have access'); } catch (error) { const passed = error.response?.status === 403; logTest('Non-admin GET blocked', passed, !passed ? `Expected 403, got ${error.response?.status}` : null); } } async function testNonAdminUpdateBlocked() { try { await axios.put(`${BASE_URL}/admin/guest-settings`, { maxQuizzes: 5 }, { headers: { Authorization: `Bearer ${regularToken}` } } ); logTest('Non-admin UPDATE blocked', false, 'Regular user should not have access'); } catch (error) { const passed = error.response?.status === 403; logTest('Non-admin UPDATE blocked', passed, !passed ? `Expected 403, got ${error.response?.status}` : null); } } async function testUnauthenticatedGet() { try { await axios.get(`${BASE_URL}/admin/guest-settings`); logTest('Unauthenticated GET blocked', false, 'Should require authentication'); } catch (error) { const passed = error.response?.status === 401; logTest('Unauthenticated GET blocked', passed, !passed ? `Expected 401, got ${error.response?.status}` : null); } } async function testUnauthenticatedUpdate() { try { await axios.put(`${BASE_URL}/admin/guest-settings`, { maxQuizzes: 5 }); logTest('Unauthenticated UPDATE blocked', false, 'Should require authentication'); } catch (error) { const passed = error.response?.status === 401; logTest('Unauthenticated UPDATE blocked', passed, !passed ? `Expected 401, got ${error.response?.status}` : null); } } // Main test runner async function runTests() { await setup(); console.log('Running tests...\n'); // Basic functionality tests const settings = await testGetDefaultSettings(); await new Promise(resolve => setTimeout(resolve, 100)); await testSettingsStructure(settings); await new Promise(resolve => setTimeout(resolve, 100)); // Update tests await testUpdateMaxQuizzes(); await new Promise(resolve => setTimeout(resolve, 100)); await testUpdateExpiryHours(); await new Promise(resolve => setTimeout(resolve, 100)); await testUpdatePublicCategories(); await new Promise(resolve => setTimeout(resolve, 100)); await testUpdateFeatureRestrictions(); await new Promise(resolve => setTimeout(resolve, 100)); await testUpdateMultipleFields(); await new Promise(resolve => setTimeout(resolve, 100)); // Validation tests await testInvalidMaxQuizzes(); await new Promise(resolve => setTimeout(resolve, 100)); await testInvalidExpiryHours(); await new Promise(resolve => setTimeout(resolve, 100)); await testInvalidCategoryUUID(); await new Promise(resolve => setTimeout(resolve, 100)); await testNonExistentCategory(); await new Promise(resolve => setTimeout(resolve, 100)); await testInvalidFeatureRestriction(); await new Promise(resolve => setTimeout(resolve, 100)); await testNonBooleanFeatureRestriction(); await new Promise(resolve => setTimeout(resolve, 100)); // Authorization tests await testNonAdminGetBlocked(); await new Promise(resolve => setTimeout(resolve, 100)); await testNonAdminUpdateBlocked(); await new Promise(resolve => setTimeout(resolve, 100)); await testUnauthenticatedGet(); await new Promise(resolve => setTimeout(resolve, 100)); await testUnauthenticatedUpdate(); // 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); });