import { describe, expect, it, beforeEach, mock } from 'bun:test' import { FieldValidationError } from '~/helpers/errors' import { MultiFieldValidationError } from '~/helpers/errors/MultiFieldValidationError' import CommandService from '~/modules/purchase_order/commands/service' describe('Purchase Order CommandService', () => { beforeEach(() => { // reset and reapply mocks to keep isolated state for each test mock.restore() mock.module('~/modules/partner/queries/query', () => ({ default: { getById: mock(async (id: string) => { if (id === 'partner-123') return { id: 'partner-123', name: 'Partner' } return null }), }, })) mock.module('~/modules/supplier/queries/query', () => ({ default: { getById: mock(async (id: string) => { if (id === 'supplier-123') return { id: 'supplier-123', name: 'Supplier' } return null }), }, })) mock.module('~/modules/supplier_item/queries/query', () => ({ default: { checkSupplierItemsByIds: mock(async (supplierId: string, ids: string[]) => ids.map((id) => ({ id, price: 100 }))), }, })) mock.module('~/modules/purchase_order/commands/command', () => ({ default: { insertPurchaseOrder: mock(async (body: any, supplierItems: any[]) => ({ id: 'po-1', ...body })), updatePurchaseOrderWithItems: mock(async (id: string, body: any) => ({ id, ...body })), deletePurchaseOrder: mock(async (id: string) => [{ deletedAt: new Date() }]), }, })) mock.module('~/modules/purchase_order/queries/query', () => ({ default: { getPurchaseOrderById: mock(async (id: string) => { if (id === 'existing-po') return { id: 'existing-po' } return null }), }, })) }) describe('createPurchaseOrder', () => { it('should throw FieldValidationError when partner not found', async () => { expect(CommandService.createPurchaseOrder({ partnerId: 'missing-partner', supplierId: 'supplier-123', orderedAt: new Date().toISOString(), receiptAt: new Date().toISOString(), orderItems: [{ id: 'item-1', qty: 1 }], })).rejects.toThrow(FieldValidationError) }) it('should throw FieldValidationError when supplier not found', async () => { expect(CommandService.createPurchaseOrder({ partnerId: 'partner-123', supplierId: 'missing-supplier', orderedAt: new Date().toISOString(), receiptAt: new Date().toISOString(), orderItems: [{ id: 'item-1', qty: 1 }], })).rejects.toThrow(FieldValidationError) }) it('should throw MultiFieldValidationError when supplier does not provide items', async () => { // override supplier item check to return different item const supplierItemQuery = await import('~/modules/supplier_item/queries/query') supplierItemQuery.default.checkSupplierItemsByIds = mock(async () => [{ id: 'other-item', price: 100 }]) as any expect(CommandService.createPurchaseOrder({ partnerId: 'partner-123', supplierId: 'supplier-123', orderedAt: new Date().toISOString(), receiptAt: new Date().toISOString(), orderItems: [{ id: 'item-1', qty: 1 }], })).rejects.toThrow(MultiFieldValidationError) }) it('should throw MultiFieldValidationError when supplier has unset price', async () => { const supplierItemQuery = await import('~/modules/supplier_item/queries/query') supplierItemQuery.default.checkSupplierItemsByIds = mock(async (supplierId: string, ids: string[]) => ids.map((id) => ({ id, price: 0 }))) as any expect(CommandService.createPurchaseOrder({ partnerId: 'partner-123', supplierId: 'supplier-123', orderedAt: new Date().toISOString(), receiptAt: new Date().toISOString(), orderItems: [{ id: 'item-1', qty: 1 }], })).rejects.toThrow(MultiFieldValidationError) }) it('should create purchase order with merged items', async () => { const result = await CommandService.createPurchaseOrder({ partnerId: 'partner-123', supplierId: 'supplier-123', orderedAt: new Date().toISOString(), receiptAt: new Date().toISOString(), orderItems: [{ id: 'item-1', qty: 1 }, { id: 'item-1', qty: 2 }], }) expect(result).toBeDefined() expect(result.id).toBe('po-1') expect(result.orderItems).toBeDefined() // merged qty should be 3 const merged = result.orderItems.find((o: any) => o.id === 'item-1') expect(merged).toBeDefined() expect((merged as any).qty).toBe(3) }) }) describe('updatePurchaseOrder', () => { it('should throw NotFoundError when purchase order not found', async () => { expect(CommandService.updatePurchaseOrder('missing-po', { partnerId: 'partner-123', supplierId: 'supplier-123', orderedAt: new Date().toISOString(), receiptAt: new Date().toISOString(), orderItems: [{ id: 'item-1', qty: 1 }], })).rejects.toThrow() }) it('should update existing purchase order', async () => { const result = await CommandService.updatePurchaseOrder('existing-po', { partnerId: 'partner-123', supplierId: 'supplier-123', orderedAt: new Date().toISOString(), receiptAt: new Date().toISOString(), orderItems: [{ id: 'item-1', qty: 1 }], } as any) expect(result).toBeDefined() expect(result.id).toBe('existing-po') }) }) describe('deletePurchaseOrder', () => { it('should throw NotFoundError when purchase order not found', async () => { expect(CommandService.deletePurchaseOrder('missing-po')).rejects.toThrow() }) it('should delete existing purchase order', async () => { const result = await CommandService.deletePurchaseOrder('existing-po') expect(result).toBeDefined() expect(result.deletedAt).toBeValidDate() }) }) })