Files
Tasks/backend/test-guest-settings.js
2025-11-12 23:06:27 +02:00

441 lines
15 KiB
JavaScript

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);
});