Initial commit: Inventory Barcode System
This commit is contained in:
237
__tests__/Product.test.js
Normal file
237
__tests__/Product.test.js
Normal file
@ -0,0 +1,237 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user