// 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();