const Product = require('../models/Product'); const database = require('../models/database'); describe('Product Model', () => { beforeAll(async () => { // Initialize database for testing database.initialize(); }); afterAll(() => { // Close database connection database.close(); }); beforeEach(() => { // Clear items table before each test const db = database.getDatabase(); db.exec('DELETE FROM items'); }); describe('Validation', () => { test('should validate required fields', () => { const product = new Product(); const validation = product.validate(); expect(validation.isValid).toBe(false); expect(validation.errors).toContain('Product name is required'); }); test('should validate product name length', () => { const product = new Product({ name: 'a'.repeat(256) }); const validation = product.validate(); expect(validation.isValid).toBe(false); expect(validation.errors).toContain('Product name must be less than 255 characters'); }); test('should validate quantity is non-negative number', () => { const product = new Product({ name: 'Test Product', quantity: -1 }); const validation = product.validate(); expect(validation.isValid).toBe(false); expect(validation.errors).toContain('Quantity must be a non-negative number'); }); test('should validate min_stock_level is non-negative number', () => { const product = new Product({ name: 'Test Product', min_stock_level: -1 }); const validation = product.validate(); expect(validation.isValid).toBe(false); expect(validation.errors).toContain('Minimum stock level must be a non-negative number'); }); test('should validate category length', () => { const product = new Product({ name: 'Test Product', category: 'a'.repeat(101) }); const validation = product.validate(); expect(validation.isValid).toBe(false); expect(validation.errors).toContain('Category must be less than 100 characters'); }); test('should validate unit length', () => { const product = new Product({ name: 'Test Product', unit: 'a'.repeat(21) }); const validation = product.validate(); expect(validation.isValid).toBe(false); expect(validation.errors).toContain('Unit must be less than 20 characters'); }); test('should pass validation with valid data', () => { const product = new Product({ name: 'Test Product', description: 'A test product', category: 'Electronics', quantity: 10, unit: 'pieces', barcode: '123456789', location: 'A1-B2', min_stock_level: 5 }); const validation = product.validate(); expect(validation.isValid).toBe(true); expect(validation.errors).toHaveLength(0); }); }); describe('Database Operations', () => { test('should save new product to database', async () => { const productData = { name: 'Test Product', description: 'A test product', category: 'Electronics', quantity: 10, unit: 'pieces', barcode: '123456789', location: 'A1-B2', min_stock_level: 5 }; const product = new Product(productData); await product.save(); expect(product.id).toBeDefined(); expect(product.id).toBeGreaterThan(0); }); test('should update existing product', async () => { // First create a product const product = new Product({ name: 'Test Product', quantity: 10 }); await product.save(); // Update the product product.name = 'Updated Product'; product.quantity = 20; await product.save(); // Verify the update const updatedProduct = await Product.findById(product.id); expect(updatedProduct.name).toBe('Updated Product'); expect(updatedProduct.quantity).toBe(20); }); test('should find product by ID', async () => { const product = new Product({ name: 'Test Product', barcode: '123456789' }); await product.save(); const foundProduct = await Product.findById(product.id); expect(foundProduct).toBeDefined(); expect(foundProduct.name).toBe('Test Product'); expect(foundProduct.barcode).toBe('123456789'); }); test('should find product by barcode', async () => { const product = new Product({ name: 'Test Product', barcode: '123456789' }); await product.save(); const foundProduct = await Product.findByBarcode('123456789'); expect(foundProduct).toBeDefined(); expect(foundProduct.name).toBe('Test Product'); }); test('should return null when product not found', async () => { const foundProduct = await Product.findById(999); expect(foundProduct).toBeNull(); }); test('should find all products', async () => { // Create multiple products const product1 = new Product({ name: 'Product 1', category: 'Electronics' }); const product2 = new Product({ name: 'Product 2', category: 'Books' }); await product1.save(); await product2.save(); const allProducts = await Product.findAll(); expect(allProducts).toHaveLength(2); }); test('should filter products by category', async () => { const product1 = new Product({ name: 'Product 1', category: 'Electronics' }); const product2 = new Product({ name: 'Product 2', category: 'Books' }); await product1.save(); await product2.save(); const electronicsProducts = await Product.findAll({ category: 'Electronics' }); expect(electronicsProducts).toHaveLength(1); expect(electronicsProducts[0].name).toBe('Product 1'); }); test('should delete product by ID', async () => { const product = new Product({ name: 'Test Product' }); await product.save(); const deleted = await Product.deleteById(product.id); expect(deleted).toBe(true); const foundProduct = await Product.findById(product.id); expect(foundProduct).toBeNull(); }); test('should handle unique barcode constraint', async () => { const product1 = new Product({ name: 'Product 1', barcode: '123456789' }); await product1.save(); const product2 = new Product({ name: 'Product 2', barcode: '123456789' }); await expect(product2.save()).rejects.toThrow('A product with this barcode already exists'); }); test('should throw validation error when saving invalid product', async () => { const product = new Product({ quantity: -1 }); // Invalid: no name and negative quantity await expect(product.save()).rejects.toThrow('Validation failed'); }); }); describe('JSON Serialization', () => { test('should convert product to JSON', () => { const productData = { id: 1, name: 'Test Product', description: 'A test product', category: 'Electronics', quantity: 10, unit: 'pieces', barcode: '123456789', qr_code: 'QR123', location: 'A1-B2', min_stock_level: 5, created_at: '2023-01-01 00:00:00', updated_at: '2023-01-01 00:00:00' }; const product = new Product(productData); const json = product.toJSON(); expect(json).toEqual(productData); }); }); });