Files
Tasks/backend/test-category-model.js
2025-11-11 00:25:50 +02:00

190 lines
7.9 KiB
JavaScript

// Category Model Tests
const { sequelize, Category } = require('./models');
async function runTests() {
try {
console.log('🧪 Running Category Model Tests\n');
console.log('=====================================\n');
// Test 1: Create a category
console.log('Test 1: Create a category with auto-generated slug');
const category1 = await Category.create({
name: 'JavaScript Fundamentals',
description: 'Basic JavaScript concepts and syntax',
icon: 'js-icon',
color: '#F7DF1E',
isActive: true,
guestAccessible: true,
displayOrder: 1
});
console.log('✅ Category created with ID:', category1.id);
console.log(' Generated slug:', category1.slug);
console.log(' Expected slug: javascript-fundamentals');
console.log(' Match:', category1.slug === 'javascript-fundamentals' ? '✅' : '❌');
// Test 2: Slug generation with special characters
console.log('\nTest 2: Slug generation handles special characters');
const category2 = await Category.create({
name: 'C++ & Object-Oriented Programming!',
description: 'OOP concepts in C++',
color: '#00599C',
displayOrder: 2
});
console.log('✅ Category created with name:', category2.name);
console.log(' Generated slug:', category2.slug);
console.log(' Expected slug: c-object-oriented-programming');
console.log(' Match:', category2.slug === 'c-object-oriented-programming' ? '✅' : '❌');
// Test 3: Custom slug
console.log('\nTest 3: Create category with custom slug');
const category3 = await Category.create({
name: 'Python Programming',
slug: 'python-basics',
description: 'Python fundamentals',
color: '#3776AB',
displayOrder: 3
});
console.log('✅ Category created with custom slug:', category3.slug);
console.log(' Slug matches custom:', category3.slug === 'python-basics' ? '✅' : '❌');
// Test 4: Find active categories
console.log('\nTest 4: Find all active categories');
const activeCategories = await Category.findActiveCategories();
console.log('✅ Found', activeCategories.length, 'active categories');
console.log(' Expected: 3');
console.log(' Match:', activeCategories.length === 3 ? '✅' : '❌');
// Test 5: Find by slug
console.log('\nTest 5: Find category by slug');
const foundCategory = await Category.findBySlug('javascript-fundamentals');
console.log('✅ Found category:', foundCategory ? foundCategory.name : 'null');
console.log(' Expected: JavaScript Fundamentals');
console.log(' Match:', foundCategory?.name === 'JavaScript Fundamentals' ? '✅' : '❌');
// Test 6: Guest accessible categories
console.log('\nTest 6: Find guest-accessible categories');
const guestCategories = await Category.getGuestAccessibleCategories();
console.log('✅ Found', guestCategories.length, 'guest-accessible categories');
console.log(' Expected: 1 (only JavaScript Fundamentals)');
console.log(' Match:', guestCategories.length === 1 ? '✅' : '❌');
// Test 7: Increment question count
console.log('\nTest 7: Increment question count');
const beforeCount = category1.questionCount;
await category1.incrementQuestionCount();
await category1.reload();
console.log('✅ Question count incremented');
console.log(' Before:', beforeCount);
console.log(' After:', category1.questionCount);
console.log(' Match:', category1.questionCount === beforeCount + 1 ? '✅' : '❌');
// Test 8: Decrement question count
console.log('\nTest 8: Decrement question count');
const beforeCount2 = category1.questionCount;
await category1.decrementQuestionCount();
await category1.reload();
console.log('✅ Question count decremented');
console.log(' Before:', beforeCount2);
console.log(' After:', category1.questionCount);
console.log(' Match:', category1.questionCount === beforeCount2 - 1 ? '✅' : '❌');
// Test 9: Increment quiz count
console.log('\nTest 9: Increment quiz count');
const beforeQuizCount = category1.quizCount;
await category1.incrementQuizCount();
await category1.reload();
console.log('✅ Quiz count incremented');
console.log(' Before:', beforeQuizCount);
console.log(' After:', category1.quizCount);
console.log(' Match:', category1.quizCount === beforeQuizCount + 1 ? '✅' : '❌');
// Test 10: Update category name (slug auto-regenerates)
console.log('\nTest 10: Update category name (slug should regenerate)');
const oldSlug = category3.slug;
category3.name = 'Advanced Python';
await category3.save();
await category3.reload();
console.log('✅ Category name updated');
console.log(' Old slug:', oldSlug);
console.log(' New slug:', category3.slug);
console.log(' Expected new slug: advanced-python');
console.log(' Match:', category3.slug === 'advanced-python' ? '✅' : '❌');
// Test 11: Unique constraint on name
console.log('\nTest 11: Unique constraint on category name');
try {
await Category.create({
name: 'JavaScript Fundamentals', // Duplicate name
description: 'Another JS category'
});
console.log('❌ Should have thrown error for duplicate name');
} catch (error) {
console.log('✅ Unique constraint enforced:', error.name === 'SequelizeUniqueConstraintError' ? '✅' : '❌');
}
// Test 12: Unique constraint on slug
console.log('\nTest 12: Unique constraint on slug');
try {
await Category.create({
name: 'Different Name',
slug: 'javascript-fundamentals' // Duplicate slug
});
console.log('❌ Should have thrown error for duplicate slug');
} catch (error) {
console.log('✅ Unique constraint enforced:', error.name === 'SequelizeUniqueConstraintError' ? '✅' : '❌');
}
// Test 13: Color validation (hex format)
console.log('\nTest 13: Color validation (must be hex format)');
try {
await Category.create({
name: 'Invalid Color Category',
color: 'red' // Invalid - should be #RRGGBB
});
console.log('❌ Should have thrown validation error for invalid color');
} catch (error) {
console.log('✅ Color validation enforced:', error.name === 'SequelizeValidationError' ? '✅' : '❌');
}
// Test 14: Slug validation (lowercase alphanumeric with hyphens)
console.log('\nTest 14: Slug validation (must be lowercase with hyphens only)');
try {
await Category.create({
name: 'Valid Name',
slug: 'Invalid_Slug!' // Invalid - has underscore and exclamation
});
console.log('❌ Should have thrown validation error for invalid slug');
} catch (error) {
console.log('✅ Slug validation enforced:', error.name === 'SequelizeValidationError' ? '✅' : '❌');
}
// Test 15: Get categories with stats
console.log('\nTest 15: Get categories with stats');
const categoriesWithStats = await Category.getCategoriesWithStats();
console.log('✅ Retrieved', categoriesWithStats.length, 'categories with stats');
console.log(' First category stats:');
console.log(' - Name:', categoriesWithStats[0].name);
console.log(' - Question count:', categoriesWithStats[0].questionCount);
console.log(' - Quiz count:', categoriesWithStats[0].quizCount);
console.log(' - Guest accessible:', categoriesWithStats[0].guestAccessible);
// Cleanup
console.log('\n=====================================');
console.log('🧹 Cleaning up test data...');
await Category.destroy({ where: {}, truncate: true });
console.log('✅ Test data deleted\n');
await sequelize.close();
console.log('✅ All Category 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();