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

413 lines
13 KiB
JavaScript

const axios = require('axios');
const BASE_URL = 'http://localhost:3000/api';
// Test configuration
const testConfig = {
adminUser: {
email: 'admin@example.com',
password: 'Admin123!@#',
username: 'adminuser'
},
regularUser: {
email: 'stattest@example.com',
password: 'Test123!@#',
username: 'stattest'
}
};
// 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 {
// Register/Login admin user
try {
await axios.post(`${BASE_URL}/auth/register`, {
email: testConfig.adminUser.email,
password: testConfig.adminUser.password,
username: testConfig.adminUser.username
});
} catch (error) {
if (error.response?.status === 409) {
console.log('Admin user already registered');
}
}
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');
// Manually set admin role in database if needed
// This would typically be done through a database migration or admin tool
// For testing, you may need to manually update the user role to 'admin' in the database
// Register/Login regular user
try {
await axios.post(`${BASE_URL}/auth/register`, {
email: testConfig.regularUser.email,
password: testConfig.regularUser.password,
username: testConfig.regularUser.username
});
} catch (error) {
if (error.response?.status === 409) {
console.log('Regular user already registered');
}
}
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('ADMIN STATISTICS API TESTS');
console.log('============================================================\n');
console.log('NOTE: Admin user must have role="admin" in database');
console.log('If tests fail due to authorization, update user role manually:\n');
console.log(`UPDATE users SET role='admin' WHERE email='${testConfig.adminUser.email}';`);
console.log('\n============================================================\n');
} catch (error) {
console.error('Setup failed:', error.response?.data || error.message);
process.exit(1);
}
}
// Test functions
async function testGetStatistics() {
try {
const response = await axios.get(`${BASE_URL}/admin/statistics`, {
headers: { Authorization: `Bearer ${adminToken}` }
});
const passed = response.status === 200 &&
response.data.success === true &&
response.data.data !== undefined;
logTest('Get statistics successfully', passed);
return response.data.data;
} catch (error) {
logTest('Get statistics successfully', false, error.response?.data?.message || error.message);
return null;
}
}
async function testStatisticsStructure(stats) {
if (!stats) {
logTest('Statistics structure validation', false, 'No statistics data available');
return;
}
try {
// Check users section
const hasUsers = stats.users &&
typeof stats.users.total === 'number' &&
typeof stats.users.active === 'number' &&
typeof stats.users.inactiveLast7Days === 'number';
// Check quizzes section
const hasQuizzes = stats.quizzes &&
typeof stats.quizzes.totalSessions === 'number' &&
typeof stats.quizzes.averageScore === 'number' &&
typeof stats.quizzes.averageScorePercentage === 'number' &&
typeof stats.quizzes.passRate === 'number' &&
typeof stats.quizzes.passedQuizzes === 'number' &&
typeof stats.quizzes.failedQuizzes === 'number';
// Check content section
const hasContent = stats.content &&
typeof stats.content.totalCategories === 'number' &&
typeof stats.content.totalQuestions === 'number' &&
stats.content.questionsByDifficulty &&
typeof stats.content.questionsByDifficulty.easy === 'number' &&
typeof stats.content.questionsByDifficulty.medium === 'number' &&
typeof stats.content.questionsByDifficulty.hard === 'number';
// Check popular categories
const hasPopularCategories = Array.isArray(stats.popularCategories);
// Check user growth
const hasUserGrowth = Array.isArray(stats.userGrowth);
// Check quiz activity
const hasQuizActivity = Array.isArray(stats.quizActivity);
const passed = hasUsers && hasQuizzes && hasContent &&
hasPopularCategories && hasUserGrowth && hasQuizActivity;
logTest('Statistics structure validation', passed);
} catch (error) {
logTest('Statistics structure validation', false, error.message);
}
}
async function testUsersSection(stats) {
if (!stats) {
logTest('Users section fields', false, 'No statistics data available');
return;
}
try {
const users = stats.users;
const passed = users.total >= 0 &&
users.active >= 0 &&
users.inactiveLast7Days >= 0 &&
users.active + users.inactiveLast7Days === users.total;
logTest('Users section fields', passed);
} catch (error) {
logTest('Users section fields', false, error.message);
}
}
async function testQuizzesSection(stats) {
if (!stats) {
logTest('Quizzes section fields', false, 'No statistics data available');
return;
}
try {
const quizzes = stats.quizzes;
const passed = quizzes.totalSessions >= 0 &&
quizzes.averageScore >= 0 &&
quizzes.averageScorePercentage >= 0 &&
quizzes.averageScorePercentage <= 100 &&
quizzes.passRate >= 0 &&
quizzes.passRate <= 100 &&
quizzes.passedQuizzes >= 0 &&
quizzes.failedQuizzes >= 0 &&
quizzes.passedQuizzes + quizzes.failedQuizzes === quizzes.totalSessions;
logTest('Quizzes section fields', passed);
} catch (error) {
logTest('Quizzes section fields', false, error.message);
}
}
async function testContentSection(stats) {
if (!stats) {
logTest('Content section fields', false, 'No statistics data available');
return;
}
try {
const content = stats.content;
const difficulty = content.questionsByDifficulty;
const totalQuestionsByDifficulty = difficulty.easy + difficulty.medium + difficulty.hard;
const passed = content.totalCategories >= 0 &&
content.totalQuestions >= 0 &&
totalQuestionsByDifficulty === content.totalQuestions;
logTest('Content section fields', passed);
} catch (error) {
logTest('Content section fields', false, error.message);
}
}
async function testPopularCategories(stats) {
if (!stats) {
logTest('Popular categories structure', false, 'No statistics data available');
return;
}
try {
const categories = stats.popularCategories;
if (categories.length === 0) {
logTest('Popular categories structure', true);
return;
}
const firstCategory = categories[0];
const passed = firstCategory.id !== undefined &&
firstCategory.name !== undefined &&
firstCategory.slug !== undefined &&
typeof firstCategory.quizCount === 'number' &&
typeof firstCategory.averageScore === 'number' &&
categories.length <= 5; // Max 5 categories
logTest('Popular categories structure', passed);
} catch (error) {
logTest('Popular categories structure', false, error.message);
}
}
async function testUserGrowth(stats) {
if (!stats) {
logTest('User growth data structure', false, 'No statistics data available');
return;
}
try {
const growth = stats.userGrowth;
if (growth.length === 0) {
logTest('User growth data structure', true);
return;
}
const firstEntry = growth[0];
const passed = firstEntry.date !== undefined &&
typeof firstEntry.newUsers === 'number' &&
growth.length <= 30; // Max 30 days
logTest('User growth data structure', passed);
} catch (error) {
logTest('User growth data structure', false, error.message);
}
}
async function testQuizActivity(stats) {
if (!stats) {
logTest('Quiz activity data structure', false, 'No statistics data available');
return;
}
try {
const activity = stats.quizActivity;
if (activity.length === 0) {
logTest('Quiz activity data structure', true);
return;
}
const firstEntry = activity[0];
const passed = firstEntry.date !== undefined &&
typeof firstEntry.quizzesCompleted === 'number' &&
activity.length <= 30; // Max 30 days
logTest('Quiz activity data structure', passed);
} catch (error) {
logTest('Quiz activity data structure', false, error.message);
}
}
async function testNonAdminBlocked() {
try {
await axios.get(`${BASE_URL}/admin/statistics`, {
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/statistics`);
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);
}
}
async function testInvalidToken() {
try {
await axios.get(`${BASE_URL}/admin/statistics`, {
headers: { Authorization: 'Bearer invalid-token-123' }
});
logTest('Invalid token rejected', false, 'Invalid token should be rejected');
} catch (error) {
const passed = error.response?.status === 401;
logTest('Invalid token rejected', 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 stats = await testGetStatistics();
await new Promise(resolve => setTimeout(resolve, 100));
// Structure validation tests
await testStatisticsStructure(stats);
await new Promise(resolve => setTimeout(resolve, 100));
await testUsersSection(stats);
await new Promise(resolve => setTimeout(resolve, 100));
await testQuizzesSection(stats);
await new Promise(resolve => setTimeout(resolve, 100));
await testContentSection(stats);
await new Promise(resolve => setTimeout(resolve, 100));
await testPopularCategories(stats);
await new Promise(resolve => setTimeout(resolve, 100));
await testUserGrowth(stats);
await new Promise(resolve => setTimeout(resolve, 100));
await testQuizActivity(stats);
await new Promise(resolve => setTimeout(resolve, 100));
// Authorization tests
await testNonAdminBlocked();
await new Promise(resolve => setTimeout(resolve, 100));
await testUnauthenticated();
await new Promise(resolve => setTimeout(resolve, 100));
await testInvalidToken();
// 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);
});