190 lines
7.9 KiB
JavaScript
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();
|