add changes

This commit is contained in:
AD2025
2025-12-26 23:56:32 +02:00
parent 410c3d725f
commit e7d26bc981
127 changed files with 36162 additions and 0 deletions

View File

@@ -0,0 +1,265 @@
// Question Model Tests
const { sequelize, Question, Category, User } = require('../models');
async function runTests() {
try {
console.log('🧪 Running Question Model Tests\n');
console.log('=====================================\n');
// Setup: Create test category and user
console.log('Setting up test data...');
const testCategory = await Category.create({
name: 'Test Category',
slug: 'test-category',
description: 'Category for testing',
isActive: true
});
const testUser = await User.create({
username: 'testadmin',
email: 'admin@test.com',
password: 'password123',
role: 'admin'
});
console.log('✅ Test category and user created\n');
// Test 1: Create a multiple choice question
console.log('Test 1: Create a multiple choice question with JSON options');
const question1 = await Question.create({
categoryId: testCategory.id,
createdBy: testUser.id,
questionText: 'What is the capital of France?',
questionType: 'multiple',
options: ['London', 'Berlin', 'Paris', 'Madrid'],
correctAnswer: '2',
explanation: 'Paris is the capital and largest city of France.',
difficulty: 'easy',
points: 10,
keywords: ['geography', 'capital', 'france'],
tags: ['geography', 'europe'],
visibility: 'public',
guestAccessible: true
});
console.log('✅ Multiple choice question created with ID:', question1.id);
console.log(' Options:', question1.options);
console.log(' Keywords:', question1.keywords);
console.log(' Tags:', question1.tags);
console.log(' Match:', Array.isArray(question1.options) ? '✅' : '❌');
// Test 2: Create a true/false question
console.log('\nTest 2: Create a true/false question');
const question2 = await Question.create({
categoryId: testCategory.id,
createdBy: testUser.id,
questionText: 'JavaScript is a compiled language.',
questionType: 'trueFalse',
correctAnswer: 'false',
explanation: 'JavaScript is an interpreted language, not compiled.',
difficulty: 'easy',
visibility: 'registered'
});
console.log('✅ True/False question created with ID:', question2.id);
console.log(' Correct answer:', question2.correctAnswer);
console.log(' Match:', question2.correctAnswer === 'false' ? '✅' : '❌');
// Test 3: Create a written question
console.log('\nTest 3: Create a written question');
const question3 = await Question.create({
categoryId: testCategory.id,
createdBy: testUser.id,
questionText: 'Explain the concept of closure in JavaScript.',
questionType: 'written',
correctAnswer: 'A closure is a function that has access to variables in its outer scope',
explanation: 'Closures allow functions to access variables from an enclosing scope.',
difficulty: 'hard',
points: 30,
visibility: 'registered'
});
console.log('✅ Written question created with ID:', question3.id);
console.log(' Points (auto-set):', question3.points);
console.log(' Match:', question3.points === 30 ? '✅' : '❌');
// Test 4: Find active questions by category
console.log('\nTest 4: Find active questions by category');
const categoryQuestions = await Question.findActiveQuestions({
categoryId: testCategory.id
});
console.log('✅ Found', categoryQuestions.length, 'questions in category');
console.log(' Expected: 3');
console.log(' Match:', categoryQuestions.length === 3 ? '✅' : '❌');
// Test 5: Filter by difficulty
console.log('\nTest 5: Filter questions by difficulty');
const easyQuestions = await Question.findActiveQuestions({
categoryId: testCategory.id,
difficulty: 'easy'
});
console.log('✅ Found', easyQuestions.length, 'easy questions');
console.log(' Expected: 2');
console.log(' Match:', easyQuestions.length === 2 ? '✅' : '❌');
// Test 6: Filter by guest accessibility
console.log('\nTest 6: Filter questions by guest accessibility');
const guestQuestions = await Question.findActiveQuestions({
categoryId: testCategory.id,
guestAccessible: true
});
console.log('✅ Found', guestQuestions.length, 'guest-accessible questions');
console.log(' Expected: 1');
console.log(' Match:', guestQuestions.length === 1 ? '✅' : '❌');
// Test 7: Get random questions
console.log('\nTest 7: Get random questions from category');
const randomQuestions = await Question.getRandomQuestions(testCategory.id, 2);
console.log('✅ Retrieved', randomQuestions.length, 'random questions');
console.log(' Expected: 2');
console.log(' Match:', randomQuestions.length === 2 ? '✅' : '❌');
// Test 8: Increment attempted count
console.log('\nTest 8: Increment attempted count');
const beforeAttempted = question1.timesAttempted;
await question1.incrementAttempted();
await question1.reload();
console.log('✅ Attempted count incremented');
console.log(' Before:', beforeAttempted);
console.log(' After:', question1.timesAttempted);
console.log(' Match:', question1.timesAttempted === beforeAttempted + 1 ? '✅' : '❌');
// Test 9: Increment correct count
console.log('\nTest 9: Increment correct count');
const beforeCorrect = question1.timesCorrect;
await question1.incrementCorrect();
await question1.reload();
console.log('✅ Correct count incremented');
console.log(' Before:', beforeCorrect);
console.log(' After:', question1.timesCorrect);
console.log(' Match:', question1.timesCorrect === beforeCorrect + 1 ? '✅' : '❌');
// Test 10: Calculate accuracy
console.log('\nTest 10: Calculate accuracy');
const accuracy = question1.getAccuracy();
console.log('✅ Accuracy calculated:', accuracy + '%');
console.log(' Times attempted:', question1.timesAttempted);
console.log(' Times correct:', question1.timesCorrect);
console.log(' Expected accuracy: 100%');
console.log(' Match:', accuracy === 100 ? '✅' : '❌');
// Test 11: toSafeJSON hides correct answer
console.log('\nTest 11: toSafeJSON hides correct answer');
const safeJSON = question1.toSafeJSON();
console.log('✅ Safe JSON generated');
console.log(' Has correctAnswer:', 'correctAnswer' in safeJSON ? '❌' : '✅');
console.log(' Has questionText:', 'questionText' in safeJSON ? '✅' : '❌');
// Test 12: Validation - multiple choice needs options
console.log('\nTest 12: Validation - multiple choice needs at least 2 options');
try {
await Question.create({
categoryId: testCategory.id,
questionText: 'Invalid question',
questionType: 'multiple',
options: ['Only one option'],
correctAnswer: '0',
difficulty: 'easy'
});
console.log('❌ Should have thrown validation error');
} catch (error) {
console.log('✅ Validation error caught:', error.message.includes('at least 2 options') ? '✅' : '❌');
}
// Test 13: Validation - trueFalse correct answer
console.log('\nTest 13: Validation - trueFalse must have true/false answer');
try {
await Question.create({
categoryId: testCategory.id,
questionText: 'Invalid true/false',
questionType: 'trueFalse',
correctAnswer: 'maybe',
difficulty: 'easy'
});
console.log('❌ Should have thrown validation error');
} catch (error) {
console.log('✅ Validation error caught:', error.message.includes('true') || error.message.includes('false') ? '✅' : '❌');
}
// Test 14: Points default based on difficulty
console.log('\nTest 14: Points auto-set based on difficulty');
const mediumQuestion = await Question.create({
categoryId: testCategory.id,
questionText: 'What is React?',
questionType: 'multiple',
options: ['Library', 'Framework', 'Language', 'Database'],
correctAnswer: '0',
difficulty: 'medium',
explanation: 'React is a JavaScript library'
});
console.log('✅ Question created with medium difficulty');
console.log(' Points auto-set:', mediumQuestion.points);
console.log(' Expected: 20');
console.log(' Match:', mediumQuestion.points === 20 ? '✅' : '❌');
// Test 15: Association with Category
console.log('\nTest 15: Association with Category');
const questionWithCategory = await Question.findByPk(question1.id, {
include: [{ model: Category, as: 'category' }]
});
console.log('✅ Question loaded with category association');
console.log(' Category name:', questionWithCategory.category.name);
console.log(' Match:', questionWithCategory.category.id === testCategory.id ? '✅' : '❌');
// Test 16: Association with User (creator)
console.log('\nTest 16: Association with User (creator)');
const questionWithCreator = await Question.findByPk(question1.id, {
include: [{ model: User, as: 'creator' }]
});
console.log('✅ Question loaded with creator association');
console.log(' Creator username:', questionWithCreator.creator.username);
console.log(' Match:', questionWithCreator.creator.id === testUser.id ? '✅' : '❌');
// Test 17: Get questions by category with options
console.log('\nTest 17: Get questions by category with filtering options');
const filteredQuestions = await Question.getQuestionsByCategory(testCategory.id, {
difficulty: 'easy',
limit: 2
});
console.log('✅ Retrieved filtered questions');
console.log(' Count:', filteredQuestions.length);
console.log(' Expected: 2');
console.log(' Match:', filteredQuestions.length === 2 ? '✅' : '❌');
// Test 18: Full-text search (if supported)
console.log('\nTest 18: Full-text search');
try {
const searchResults = await Question.searchQuestions('JavaScript', {
limit: 10
});
console.log('✅ Full-text search executed');
console.log(' Results found:', searchResults.length);
console.log(' Contains JavaScript question:', searchResults.length > 0 ? '✅' : '❌');
} catch (error) {
console.log('⚠️ Full-text search requires proper index setup');
}
// Cleanup
console.log('\n=====================================');
console.log('🧹 Cleaning up test data...');
// Delete in correct order (children first, then parents)
await Question.destroy({ where: {}, force: true });
await Category.destroy({ where: {}, force: true });
await User.destroy({ where: {}, force: true });
console.log('✅ Test data deleted\n');
await sequelize.close();
console.log('✅ All Question Model Tests Completed!\n');
process.exit(0);
} catch (error) {
console.error('\n❌ Test failed with error:', error.message);
console.error('Error details:', error);
await sequelize.close();
process.exit(1);
}
}
runTests();