import { jest } from '@jest/globals'; // Mock dependencies jest.unstable_mockModule('../../../database/database.js', () => ({ listObjects: jest.fn(), getObject: jest.fn(), editObject: jest.fn(), editObjects: jest.fn(), newObject: jest.fn(), deleteObject: jest.fn(), listObjectsByProperties: jest.fn(), getModelStats: jest.fn(), getModelHistory: jest.fn(), checkStates: jest.fn(), })); jest.unstable_mockModule('../../../database/schemas/finance/invoice.schema.js', () => ({ invoiceModel: { modelName: 'Invoice' }, })); jest.unstable_mockModule('../../../database/schemas/inventory/orderitem.schema.js', () => ({ orderItemModel: { modelName: 'OrderItem' }, })); jest.unstable_mockModule('../../../database/schemas/inventory/shipment.schema.js', () => ({ shipmentModel: { modelName: 'Shipment' }, })); jest.unstable_mockModule('log4js', () => ({ default: { getLogger: () => ({ level: 'info', debug: jest.fn(), error: jest.fn(), warn: jest.fn(), trace: jest.fn(), }), }, })); // Import handlers after mocking const { listInvoicesRouteHandler, getInvoiceRouteHandler, newInvoiceRouteHandler, editInvoiceRouteHandler, deleteInvoiceRouteHandler, postInvoiceRouteHandler, } = await import('../invoices.js'); const { listObjects, getObject, editObject, newObject, deleteObject, checkStates } = await import( '../../../database/database.js' ); const { invoiceModel } = await import('../../../database/schemas/finance/invoice.schema.js'); describe('Invoice Service Route Handlers', () => { let req, res; beforeEach(() => { req = { params: {}, query: {}, body: {}, user: { id: 'test-user-id' }, }; res = { send: jest.fn(), status: jest.fn().mockReturnThis(), }; jest.clearAllMocks(); }); describe('listInvoicesRouteHandler', () => { it('should list invoices', async () => { const mockResult = [{ _id: '1', invoiceNumber: 'INV-001' }]; listObjects.mockResolvedValue(mockResult); await listInvoicesRouteHandler(req, res); expect(listObjects).toHaveBeenCalled(); expect(res.send).toHaveBeenCalledWith(mockResult); }); }); describe('getInvoiceRouteHandler', () => { it('should get an invoice by ID', async () => { req.params.id = '123'; const mockResult = { _id: '123', invoiceNumber: 'INV-001' }; getObject.mockResolvedValue(mockResult); await getInvoiceRouteHandler(req, res); expect(getObject).toHaveBeenCalledWith(expect.objectContaining({ id: '123' })); expect(res.send).toHaveBeenCalledWith(mockResult); }); }); describe('newInvoiceRouteHandler', () => { it('should create a new invoice with order items and shipments', async () => { req.body = { order: 'order123', orderType: 'sales' }; // Mock listObjects for orderItems and shipments listObjects.mockResolvedValueOnce([{ _id: 'oi1', totalAmount: 100, invoicedAmount: 0 }]); // orderItems listObjects.mockResolvedValueOnce([{ _id: 's1', amount: 20, invoicedAmount: 0 }]); // shipments const mockInvoice = { _id: 'inv456' }; newObject.mockResolvedValue(mockInvoice); await newInvoiceRouteHandler(req, res); expect(newObject).toHaveBeenCalled(); expect(res.send).toHaveBeenCalledWith(mockInvoice); }); }); describe('postInvoiceRouteHandler', () => { it('should post a draft invoice and update order items/shipments', async () => { req.params.id = '507f1f77bcf86cd799439011'; checkStates.mockResolvedValue(true); const mockInvoice = { _id: '507f1f77bcf86cd799439011', invoiceOrderItems: [{ orderItem: { _id: 'oi1' }, invoiceAmount: 50, invoiceQuantity: 1 }], invoiceShipments: [{ shipment: { _id: 's1' }, invoiceAmount: 10 }], }; getObject.mockResolvedValueOnce(mockInvoice); // Mock getObject for individual orderItems and shipments getObject.mockResolvedValueOnce({ _id: 'oi1', invoicedAmount: 0, invoicedQuantity: 0 }); getObject.mockResolvedValueOnce({ _id: 's1', invoicedAmount: 0 }); editObject.mockResolvedValue({ _id: '507f1f77bcf86cd799439011', state: { type: 'sent' } }); await postInvoiceRouteHandler(req, res); expect(checkStates).toHaveBeenCalledWith(expect.objectContaining({ states: ['draft'] })); expect(editObject).toHaveBeenCalled(); // Should be called for items, shipments, and the invoice itself expect(res.send).toHaveBeenCalled(); }); it('should fail if invoice is not in draft state', async () => { req.params.id = '507f1f77bcf86cd799439011'; checkStates.mockResolvedValue(false); await postInvoiceRouteHandler(req, res); expect(res.status).toHaveBeenCalledWith(400); expect(res.send).toHaveBeenCalledWith( expect.objectContaining({ error: 'Invoice is not in draft state.' }) ); }); }); });