first commit 🎉
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
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()
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, it, beforeEach, mock } from 'bun:test'
|
||||
import QueryService from '~/modules/purchase_order/queries/service'
|
||||
|
||||
// Default mocks
|
||||
mock.module('~/modules/purchase_order/queries/query', () => ({
|
||||
default: {
|
||||
listPurchaseOrders: mock(async (payload: any) => ({
|
||||
data: [{ id: 'po-1', orderNumber: 'PO-001' }],
|
||||
meta: { page: 1, size: 10, totalData: 1, totalPage: 1 },
|
||||
})),
|
||||
getPurchaseOrderById: mock(async (id: string) => {
|
||||
if (id === 'po-1') return { id: 'po-1', orderNumber: 'PO-001' }
|
||||
return null
|
||||
}),
|
||||
getPurchaseOrderReport: mock(async (params: any) => ({
|
||||
purchaseOrders: [{ id: 'po-1', orderNumber: 'PO-001' }],
|
||||
summary: { totalAmount: 1000, totalClaimAmount: 100, totalMarginAmount: 50 },
|
||||
})),
|
||||
},
|
||||
}))
|
||||
|
||||
describe('Purchase Order QueryService', () => {
|
||||
beforeEach(() => {
|
||||
mock.restore()
|
||||
mock.module('~/modules/purchase_order/queries/query', () => ({
|
||||
default: {
|
||||
listPurchaseOrders: mock(async (payload: any) => ({
|
||||
data: [{ id: 'po-1', orderNumber: 'PO-001' }],
|
||||
meta: { page: payload.page ?? 1, size: payload.size ?? 10, totalData: 1, totalPage: 1 },
|
||||
})),
|
||||
getPurchaseOrderById: mock(async (id: string) => {
|
||||
if (id === 'po-1') return { id: 'po-1', orderNumber: 'PO-001' }
|
||||
return null
|
||||
}),
|
||||
getPurchaseOrderReport: mock(async (params: any) => ({
|
||||
purchaseOrders: [{ id: 'po-1', orderNumber: 'PO-001' }],
|
||||
summary: { totalAmount: 1000, totalClaimAmount: 100, totalMarginAmount: 50 },
|
||||
})),
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
it('should return purchase orders list with meta', async () => {
|
||||
const result = await QueryService.getPurchaseOrders({ page: 1, size: 10 })
|
||||
expect(result).toBeDefined()
|
||||
expect(Array.isArray(result.data)).toBe(true)
|
||||
expect(result.meta).toBeDefined()
|
||||
expect(result.meta.page).toBe(1)
|
||||
})
|
||||
|
||||
it('should return a single purchase order when found', async () => {
|
||||
const po = await QueryService.getPurchaseOrder('po-1')
|
||||
expect(po).toBeDefined()
|
||||
expect(po).not.toBeNull()
|
||||
expect((po as any).id).toBe('po-1')
|
||||
})
|
||||
|
||||
it('should return null when purchase order not found', async () => {
|
||||
const po = await QueryService.getPurchaseOrder('missing-po')
|
||||
expect(po).toBeNull()
|
||||
})
|
||||
|
||||
it('should return report data', async () => {
|
||||
const report = await QueryService.getPurchaseOrderReport({
|
||||
partnerId: 'partner-123',
|
||||
startDate: new Date().toISOString(),
|
||||
endDate: new Date().toISOString(),
|
||||
} as any)
|
||||
expect(report).toBeDefined()
|
||||
expect(Array.isArray(report.purchaseOrders)).toBe(true)
|
||||
expect(report.summary).toBeDefined()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user