add changes
This commit is contained in:
440
backend/tests/test-guest-settings.js
Normal file
440
backend/tests/test-guest-settings.js
Normal file
@@ -0,0 +1,440 @@
|
||||
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);
|
||||
});
|
||||
Reference in New Issue
Block a user