777 lines
25 KiB
JavaScript
777 lines
25 KiB
JavaScript
const axios = require('axios');
|
|
|
|
const API_URL = 'http://localhost:3000/api';
|
|
|
|
// Admin credentials (from seeder)
|
|
const adminUser = {
|
|
email: 'admin@quiz.com',
|
|
password: 'Admin@123'
|
|
};
|
|
|
|
// Regular user credentials (with timestamp to avoid conflicts)
|
|
const timestamp = Date.now();
|
|
const regularUser = {
|
|
username: `testuser${timestamp}`,
|
|
email: `testuser${timestamp}@example.com`,
|
|
password: 'Test@123'
|
|
};
|
|
|
|
// ANSI color codes
|
|
const colors = {
|
|
reset: '\x1b[0m',
|
|
green: '\x1b[32m',
|
|
red: '\x1b[31m',
|
|
yellow: '\x1b[33m',
|
|
blue: '\x1b[34m',
|
|
cyan: '\x1b[36m',
|
|
magenta: '\x1b[35m'
|
|
};
|
|
|
|
let adminToken = null;
|
|
let regularUserToken = null;
|
|
let testCategoryId = null;
|
|
let testQuestionId = null;
|
|
|
|
// Test counters
|
|
let totalTests = 0;
|
|
let passedTests = 0;
|
|
let failedTests = 0;
|
|
|
|
/**
|
|
* Helper: Log test result
|
|
*/
|
|
function logTestResult(testName, passed, error = null) {
|
|
totalTests++;
|
|
if (passed) {
|
|
passedTests++;
|
|
console.log(`${colors.green}✓ ${testName}${colors.reset}`);
|
|
} else {
|
|
failedTests++;
|
|
console.log(`${colors.red}✗ ${testName}${colors.reset}`);
|
|
if (error) {
|
|
console.log(` ${colors.red}Error: ${error}${colors.reset}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Login as admin
|
|
*/
|
|
async function loginAdmin() {
|
|
try {
|
|
const response = await axios.post(`${API_URL}/auth/login`, adminUser);
|
|
adminToken = response.data.data.token;
|
|
console.log(`${colors.cyan}✓ Logged in as admin${colors.reset}`);
|
|
return adminToken;
|
|
} catch (error) {
|
|
console.error(`${colors.red}✗ Failed to login as admin:${colors.reset}`, error.response?.data || error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create and login regular user
|
|
*/
|
|
async function createRegularUser() {
|
|
try {
|
|
const registerResponse = await axios.post(`${API_URL}/auth/register`, regularUser);
|
|
regularUserToken = registerResponse.data.data.token;
|
|
console.log(`${colors.cyan}✓ Created and logged in as regular user${colors.reset}`);
|
|
return regularUserToken;
|
|
} catch (error) {
|
|
console.error(`${colors.red}✗ Failed to create regular user:${colors.reset}`, error.response?.data || error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get first active category
|
|
*/
|
|
async function getFirstCategory() {
|
|
try {
|
|
const response = await axios.get(`${API_URL}/categories`, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
if (response.data.data && response.data.data.length > 0) {
|
|
testCategoryId = response.data.data[0].id;
|
|
console.log(`${colors.cyan}✓ Got test category: ${testCategoryId}${colors.reset}`);
|
|
return testCategoryId;
|
|
}
|
|
throw new Error('No categories found');
|
|
} catch (error) {
|
|
console.error(`${colors.red}✗ Failed to get category:${colors.reset}`, error.response?.data || error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a test question
|
|
*/
|
|
async function createTestQuestion() {
|
|
try {
|
|
const questionData = {
|
|
questionText: 'What is the capital of France?',
|
|
questionType: 'multiple',
|
|
options: [
|
|
{ id: 'a', text: 'Paris' },
|
|
{ id: 'b', text: 'London' },
|
|
{ id: 'c', text: 'Berlin' },
|
|
{ id: 'd', text: 'Madrid' }
|
|
],
|
|
correctAnswer: 'a',
|
|
difficulty: 'easy',
|
|
points: 10,
|
|
explanation: 'Paris is the capital and largest city of France.',
|
|
categoryId: testCategoryId,
|
|
tags: ['geography', 'capitals'],
|
|
keywords: ['france', 'paris', 'capital']
|
|
};
|
|
|
|
const response = await axios.post(`${API_URL}/admin/questions`, questionData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
testQuestionId = response.data.data.id;
|
|
console.log(`${colors.cyan}✓ Created test question: ${testQuestionId}${colors.reset}`);
|
|
return testQuestionId;
|
|
} catch (error) {
|
|
console.error(`${colors.red}✗ Failed to create test question:${colors.reset}`);
|
|
console.error('Status:', error.response?.status);
|
|
console.error('Data:', JSON.stringify(error.response?.data, null, 2));
|
|
console.error('Message:', error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// TEST SUITE: UPDATE QUESTION ENDPOINT
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Test 1: Unauthenticated request cannot update question (401)
|
|
*/
|
|
async function test01_UnauthenticatedCannotUpdate() {
|
|
console.log(`\n${colors.blue}Test 1: Unauthenticated request cannot update question${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
questionText: 'Updated question text'
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData);
|
|
|
|
logTestResult('Test 1: Unauthenticated request cannot update question', false, 'Should have returned 401');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 401;
|
|
logTestResult('Test 1: Unauthenticated request cannot update question', passed, passed ? null : `Expected 401, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 2: Regular user cannot update question (403)
|
|
*/
|
|
async function test02_UserCannotUpdate() {
|
|
console.log(`\n${colors.blue}Test 2: Regular user cannot update question${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
questionText: 'Updated question text'
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${regularUserToken}` }
|
|
});
|
|
|
|
logTestResult('Test 2: Regular user cannot update question', false, 'Should have returned 403');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 403;
|
|
logTestResult('Test 2: Regular user cannot update question', passed, passed ? null : `Expected 403, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 3: Admin can update question text
|
|
*/
|
|
async function test03_UpdateQuestionText() {
|
|
console.log(`\n${colors.blue}Test 3: Admin can update question text${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
questionText: 'What is the capital city of France?'
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success, data } = response.data;
|
|
const passed = success &&
|
|
data.questionText === updateData.questionText &&
|
|
data.id === testQuestionId;
|
|
|
|
logTestResult('Test 3: Admin can update question text', passed,
|
|
passed ? null : 'Question text not updated correctly');
|
|
} catch (error) {
|
|
logTestResult('Test 3: Admin can update question text', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 4: Update difficulty level
|
|
*/
|
|
async function test04_UpdateDifficulty() {
|
|
console.log(`\n${colors.blue}Test 4: Update difficulty level${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
difficulty: 'medium'
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success, data } = response.data;
|
|
const passed = success && data.difficulty === 'medium';
|
|
|
|
logTestResult('Test 4: Update difficulty level', passed,
|
|
passed ? null : 'Difficulty not updated correctly');
|
|
} catch (error) {
|
|
logTestResult('Test 4: Update difficulty level', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 5: Update points
|
|
*/
|
|
async function test05_UpdatePoints() {
|
|
console.log(`\n${colors.blue}Test 5: Update points${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
points: 20
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success, data } = response.data;
|
|
const passed = success && data.points === 20;
|
|
|
|
logTestResult('Test 5: Update points', passed,
|
|
passed ? null : 'Points not updated correctly');
|
|
} catch (error) {
|
|
logTestResult('Test 5: Update points', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 6: Update explanation
|
|
*/
|
|
async function test06_UpdateExplanation() {
|
|
console.log(`\n${colors.blue}Test 6: Update explanation${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
explanation: 'Paris has been the capital of France since the 12th century.'
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success, data } = response.data;
|
|
const passed = success && data.explanation === updateData.explanation;
|
|
|
|
logTestResult('Test 6: Update explanation', passed,
|
|
passed ? null : 'Explanation not updated correctly');
|
|
} catch (error) {
|
|
logTestResult('Test 6: Update explanation', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 7: Update tags
|
|
*/
|
|
async function test07_UpdateTags() {
|
|
console.log(`\n${colors.blue}Test 7: Update tags${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
tags: ['geography', 'europe', 'france', 'capitals']
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success, data } = response.data;
|
|
const passed = success &&
|
|
Array.isArray(data.tags) &&
|
|
data.tags.length === 4 &&
|
|
data.tags.includes('europe');
|
|
|
|
logTestResult('Test 7: Update tags', passed,
|
|
passed ? null : 'Tags not updated correctly');
|
|
} catch (error) {
|
|
logTestResult('Test 7: Update tags', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 8: Update multiple choice options
|
|
*/
|
|
async function test08_UpdateOptions() {
|
|
console.log(`\n${colors.blue}Test 8: Update multiple choice options${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
options: [
|
|
{ id: 'a', text: 'Paris' },
|
|
{ id: 'b', text: 'London' },
|
|
{ id: 'c', text: 'Berlin' },
|
|
{ id: 'd', text: 'Madrid' },
|
|
{ id: 'e', text: 'Rome' }
|
|
]
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success, data } = response.data;
|
|
const passed = success &&
|
|
Array.isArray(data.options) &&
|
|
data.options.length === 5 &&
|
|
data.options.some(opt => opt.text === 'Rome');
|
|
|
|
logTestResult('Test 8: Update multiple choice options', passed,
|
|
passed ? null : 'Options not updated correctly');
|
|
} catch (error) {
|
|
logTestResult('Test 8: Update multiple choice options', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 9: Update correct answer
|
|
*/
|
|
async function test09_UpdateCorrectAnswer() {
|
|
console.log(`\n${colors.blue}Test 9: Update correct answer${colors.reset}`);
|
|
|
|
try {
|
|
// First update to add 'Lyon' as an option
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, {
|
|
options: [
|
|
{ id: 'a', text: 'Paris' },
|
|
{ id: 'b', text: 'London' },
|
|
{ id: 'c', text: 'Berlin' },
|
|
{ id: 'd', text: 'Madrid' },
|
|
{ id: 'e', text: 'Lyon' }
|
|
]
|
|
}, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
// Note: correctAnswer is not returned in response for security
|
|
// We just verify the update succeeds
|
|
const updateData = {
|
|
correctAnswer: 'a' // Keep as 'a' (Paris) since it's still valid
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success } = response.data;
|
|
const passed = success;
|
|
|
|
logTestResult('Test 9: Update correct answer', passed,
|
|
passed ? null : 'Update failed');
|
|
} catch (error) {
|
|
logTestResult('Test 9: Update correct answer', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 10: Update isActive status
|
|
*/
|
|
async function test10_UpdateIsActive() {
|
|
console.log(`\n${colors.blue}Test 10: Update isActive status${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
isActive: false
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success, data } = response.data;
|
|
const passed = success && data.isActive === false;
|
|
|
|
// Reactivate for other tests
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, { isActive: true }, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
logTestResult('Test 10: Update isActive status', passed,
|
|
passed ? null : 'isActive not updated correctly');
|
|
} catch (error) {
|
|
logTestResult('Test 10: Update isActive status', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 11: Update multiple fields at once
|
|
*/
|
|
async function test11_UpdateMultipleFields() {
|
|
console.log(`\n${colors.blue}Test 11: Update multiple fields at once${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
questionText: 'What is the capital and largest city of France?',
|
|
difficulty: 'hard',
|
|
points: 30,
|
|
explanation: 'Paris is both the capital and the most populous city of France.',
|
|
tags: ['geography', 'france', 'cities'],
|
|
keywords: ['france', 'paris', 'capital', 'city']
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success, data } = response.data;
|
|
const passed = success &&
|
|
data.questionText === updateData.questionText &&
|
|
data.difficulty === 'hard' &&
|
|
data.points === 30 &&
|
|
data.explanation === updateData.explanation &&
|
|
data.tags.length === 3 &&
|
|
data.keywords.length === 4;
|
|
|
|
logTestResult('Test 11: Update multiple fields at once', passed,
|
|
passed ? null : 'Multiple fields not updated correctly');
|
|
} catch (error) {
|
|
logTestResult('Test 11: Update multiple fields at once', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 12: Invalid question ID (400)
|
|
*/
|
|
async function test12_InvalidQuestionId() {
|
|
console.log(`\n${colors.blue}Test 12: Invalid question ID${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
questionText: 'Updated text'
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/invalid-id`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
logTestResult('Test 12: Invalid question ID', false, 'Should have returned 400');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 400;
|
|
logTestResult('Test 12: Invalid question ID', passed, passed ? null : `Expected 400, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 13: Non-existent question (404)
|
|
*/
|
|
async function test13_NonExistentQuestion() {
|
|
console.log(`\n${colors.blue}Test 13: Non-existent question${colors.reset}`);
|
|
|
|
try {
|
|
const fakeUuid = '00000000-0000-0000-0000-000000000000';
|
|
const updateData = {
|
|
questionText: 'Updated text'
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/${fakeUuid}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
logTestResult('Test 13: Non-existent question', false, 'Should have returned 404');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 404;
|
|
logTestResult('Test 13: Non-existent question', passed, passed ? null : `Expected 404, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 14: Invalid difficulty value (400)
|
|
*/
|
|
async function test14_InvalidDifficulty() {
|
|
console.log(`\n${colors.blue}Test 14: Invalid difficulty value${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
difficulty: 'super-hard'
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
logTestResult('Test 14: Invalid difficulty value', false, 'Should have returned 400');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 400;
|
|
logTestResult('Test 14: Invalid difficulty value', passed, passed ? null : `Expected 400, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 15: Invalid points value (400)
|
|
*/
|
|
async function test15_InvalidPoints() {
|
|
console.log(`\n${colors.blue}Test 15: Invalid points value${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
points: -10
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
logTestResult('Test 15: Invalid points value', false, 'Should have returned 400');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 400;
|
|
logTestResult('Test 15: Invalid points value', passed, passed ? null : `Expected 400, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 16: Empty question text (400)
|
|
*/
|
|
async function test16_EmptyQuestionText() {
|
|
console.log(`\n${colors.blue}Test 16: Empty question text${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
questionText: ''
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
logTestResult('Test 16: Empty question text', false, 'Should have returned 400');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 400;
|
|
logTestResult('Test 16: Empty question text', passed, passed ? null : `Expected 400, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 17: Update with less than 2 options for multiple choice (400)
|
|
*/
|
|
async function test17_InsufficientOptions() {
|
|
console.log(`\n${colors.blue}Test 17: Insufficient options for multiple choice${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
options: [{ id: 'a', text: 'Paris' }]
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
logTestResult('Test 17: Insufficient options for multiple choice', false, 'Should have returned 400');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 400;
|
|
logTestResult('Test 17: Insufficient options for multiple choice', passed, passed ? null : `Expected 400, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 18: Correct answer not in options (400)
|
|
*/
|
|
async function test18_CorrectAnswerNotInOptions() {
|
|
console.log(`\n${colors.blue}Test 18: Correct answer not in options${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
correctAnswer: 'z' // Invalid option ID
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
logTestResult('Test 18: Correct answer not in options', false, 'Should have returned 400');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 400;
|
|
logTestResult('Test 18: Correct answer not in options', passed, passed ? null : `Expected 400, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 19: Update category to non-existent category (404)
|
|
*/
|
|
async function test19_NonExistentCategory() {
|
|
console.log(`\n${colors.blue}Test 19: Update to non-existent category${colors.reset}`);
|
|
|
|
try {
|
|
const fakeUuid = '00000000-0000-0000-0000-000000000000';
|
|
const updateData = {
|
|
categoryId: fakeUuid
|
|
};
|
|
|
|
await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
logTestResult('Test 19: Update to non-existent category', false, 'Should have returned 404');
|
|
} catch (error) {
|
|
const status = error.response?.status;
|
|
const passed = status === 404;
|
|
logTestResult('Test 19: Update to non-existent category', passed, passed ? null : `Expected 404, got ${status}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test 20: Response doesn't include correctAnswer (security)
|
|
*/
|
|
async function test20_NoCorrectAnswerInResponse() {
|
|
console.log(`\n${colors.blue}Test 20: Response doesn't expose correct answer${colors.reset}`);
|
|
|
|
try {
|
|
const updateData = {
|
|
questionText: 'What is the capital of France? (Updated)'
|
|
};
|
|
|
|
const response = await axios.put(`${API_URL}/admin/questions/${testQuestionId}`, updateData, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
|
|
const { success, data } = response.data;
|
|
const passed = success && !data.hasOwnProperty('correctAnswer') && !data.hasOwnProperty('correct_answer');
|
|
|
|
logTestResult('Test 20: Response doesn\'t expose correct answer', passed,
|
|
passed ? null : 'correctAnswer should not be in response');
|
|
} catch (error) {
|
|
logTestResult('Test 20: Response doesn\'t expose correct answer', false, error.response?.data?.message || error.message);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// CLEANUP
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Delete test question
|
|
*/
|
|
async function deleteTestQuestion() {
|
|
try {
|
|
if (testQuestionId) {
|
|
await axios.delete(`${API_URL}/admin/questions/${testQuestionId}`, {
|
|
headers: { 'Authorization': `Bearer ${adminToken}` }
|
|
});
|
|
console.log(`${colors.cyan}✓ Deleted test question${colors.reset}`);
|
|
}
|
|
} catch (error) {
|
|
console.error(`${colors.yellow}⚠ Failed to delete test question:${colors.reset}`, error.response?.data || error.message);
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// MAIN TEST RUNNER
|
|
// ============================================================================
|
|
|
|
async function runAllTests() {
|
|
console.log(`${colors.magenta}
|
|
╔════════════════════════════════════════════════════════════╗
|
|
║ ADMIN UPDATE QUESTION ENDPOINT - TEST SUITE ║
|
|
╚════════════════════════════════════════════════════════════╝
|
|
${colors.reset}`);
|
|
|
|
try {
|
|
// Setup
|
|
console.log(`${colors.cyan}\n--- Setup Phase ---${colors.reset}`);
|
|
await loginAdmin();
|
|
await createRegularUser();
|
|
await getFirstCategory();
|
|
await createTestQuestion();
|
|
|
|
// Run tests
|
|
console.log(`${colors.cyan}\n--- Running Tests ---${colors.reset}`);
|
|
|
|
// Authorization tests
|
|
await test01_UnauthenticatedCannotUpdate();
|
|
await test02_UserCannotUpdate();
|
|
|
|
// Update field tests
|
|
await test03_UpdateQuestionText();
|
|
await test04_UpdateDifficulty();
|
|
await test05_UpdatePoints();
|
|
await test06_UpdateExplanation();
|
|
await test07_UpdateTags();
|
|
await test08_UpdateOptions();
|
|
await test09_UpdateCorrectAnswer();
|
|
await test10_UpdateIsActive();
|
|
await test11_UpdateMultipleFields();
|
|
|
|
// Error handling tests
|
|
await test12_InvalidQuestionId();
|
|
await test13_NonExistentQuestion();
|
|
await test14_InvalidDifficulty();
|
|
await test15_InvalidPoints();
|
|
await test16_EmptyQuestionText();
|
|
await test17_InsufficientOptions();
|
|
await test18_CorrectAnswerNotInOptions();
|
|
await test19_NonExistentCategory();
|
|
|
|
// Security tests
|
|
await test20_NoCorrectAnswerInResponse();
|
|
|
|
// Cleanup
|
|
console.log(`${colors.cyan}\n--- Cleanup Phase ---${colors.reset}`);
|
|
await deleteTestQuestion();
|
|
|
|
// Summary
|
|
console.log(`${colors.magenta}
|
|
╔════════════════════════════════════════════════════════════╗
|
|
║ TEST SUMMARY ║
|
|
╚════════════════════════════════════════════════════════════╝
|
|
${colors.reset}`);
|
|
console.log(`Total Tests: ${totalTests}`);
|
|
console.log(`${colors.green}Passed: ${passedTests}${colors.reset}`);
|
|
console.log(`${colors.red}Failed: ${failedTests}${colors.reset}`);
|
|
console.log(`Success Rate: ${((passedTests / totalTests) * 100).toFixed(2)}%\n`);
|
|
|
|
if (failedTests === 0) {
|
|
console.log(`${colors.green}✓ All tests passed!${colors.reset}\n`);
|
|
process.exit(0);
|
|
} else {
|
|
console.log(`${colors.red}✗ Some tests failed${colors.reset}\n`);
|
|
process.exit(1);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error(`${colors.red}\n✗ Test suite failed:${colors.reset}`, error.message);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Run the tests
|
|
runAllTests();
|