374 lines
13 KiB
TypeScript
374 lines
13 KiB
TypeScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
|
|
import prisma from '../../utils/prisma'
|
|
import { updateEquipment } from '../equipmentController'
|
|
|
|
const createMockResponse = (role = 'admin') => {
|
|
const responseBody: { statusCode: number; body: any } = {
|
|
statusCode: 200,
|
|
body: null
|
|
}
|
|
|
|
const res = {
|
|
locals: { user: { id: 1, role } },
|
|
status(code: number) {
|
|
responseBody.statusCode = code
|
|
return this
|
|
},
|
|
json(payload: any) {
|
|
responseBody.body = payload
|
|
return this
|
|
}
|
|
}
|
|
|
|
return { res, responseBody }
|
|
}
|
|
|
|
const currentEquipment = {
|
|
id: 903,
|
|
equipment_code: 'JS-PC-0119',
|
|
code_generation_mode: 'auto',
|
|
equipment_name: '主机',
|
|
equipment_model: '',
|
|
equipment_type: '主机',
|
|
unit_price: 0,
|
|
total_quantity: 1,
|
|
remaining_quantity: 1,
|
|
total_value: 0,
|
|
borrowed_value: 0,
|
|
available_value: 0,
|
|
purchase_date: null,
|
|
supplier: '',
|
|
description: '',
|
|
status: '待报废',
|
|
storage_location_id: null,
|
|
department_id: null,
|
|
asset_type_id: null,
|
|
parent_id: null,
|
|
updated_at: new Date('2026-07-14T01:00:00.000Z')
|
|
}
|
|
|
|
test('已丢失资产基础编辑保留状态与零库存,禁止增总数量生成虚假库存', async () => {
|
|
const originalFindUnique = prisma.equipment.findUnique
|
|
const originalTransaction = prisma.$transaction
|
|
const lostEquipment = { ...currentEquipment, status: '已丢失', remaining_quantity: 0, unit_price: 100, total_value: 100 }
|
|
let updateData: any
|
|
let writes = 0
|
|
;(prisma.equipment.findUnique as any) = async () => lostEquipment
|
|
;(prisma.$transaction as any) = async (callback: any) => callback({
|
|
$queryRawUnsafe: async () => [],
|
|
operationLog: { create: async () => ({ id: 1 }) },
|
|
equipment: {
|
|
updateMany: async (args: any) => { writes++; updateData = args.data; return { count: 1 } },
|
|
count: async () => 0
|
|
}
|
|
})
|
|
try {
|
|
const metadata = createMockResponse()
|
|
await updateEquipment({ params: { id: '903' }, body: { equipment_name: '丢失主机', total_quantity: 1, unit_price: 120 } } as any, metadata.res as any)
|
|
assert.equal(metadata.responseBody.statusCode, 200)
|
|
assert.equal(updateData.status, '已丢失')
|
|
assert.equal(updateData.remaining_quantity, 0)
|
|
assert.equal(updateData.available_value, 0)
|
|
assert.equal(updateData.borrowed_value, 0)
|
|
const quantity = createMockResponse()
|
|
await updateEquipment({ params: { id: '903' }, body: { total_quantity: 2 } } as any, quantity.res as any)
|
|
assert.equal(quantity.responseBody.statusCode, 409)
|
|
assert.equal(writes, 1)
|
|
} finally {
|
|
;(prisma.equipment.findUnique as any) = originalFindUnique
|
|
;(prisma.$transaction as any) = originalTransaction
|
|
}
|
|
})
|
|
|
|
test('普通编辑资产时不应把待报废资产冲回可用', async () => {
|
|
const originalFindUnique = prisma.equipment.findUnique
|
|
const originalTransaction = prisma.$transaction
|
|
|
|
let capturedUpdateData: any = null
|
|
;(prisma.equipment.findUnique as any) = async () => currentEquipment
|
|
;(prisma.$transaction as any) = async (callback: any) => callback({
|
|
$queryRawUnsafe: async () => [],
|
|
operationLog: { create: async () => ({ id: 1 }) },
|
|
equipment: {
|
|
updateMany: async (args: any) => {
|
|
capturedUpdateData = args.data
|
|
return { count: 1 }
|
|
},
|
|
count: async () => 0
|
|
}
|
|
})
|
|
|
|
try {
|
|
const { res, responseBody } = createMockResponse()
|
|
await updateEquipment(
|
|
{
|
|
params: { id: '903' },
|
|
body: {
|
|
equipment_name: '主机',
|
|
equipment_model: '',
|
|
unit_price: 0,
|
|
total_quantity: 1,
|
|
purchase_date: '',
|
|
supplier: '',
|
|
description: ''
|
|
}
|
|
} as any,
|
|
res as any
|
|
)
|
|
|
|
assert.equal(responseBody.statusCode, 200)
|
|
assert.equal(capturedUpdateData.status, '待报废')
|
|
} finally {
|
|
;(prisma.equipment.findUnique as any) = originalFindUnique
|
|
;(prisma.$transaction as any) = originalTransaction
|
|
}
|
|
})
|
|
|
|
test('管理员也不能通过基础资料接口直接改部门或状态', async () => {
|
|
const originalFindUnique = prisma.equipment.findUnique
|
|
const originalTransaction = prisma.$transaction
|
|
let transactionCount = 0
|
|
|
|
;(prisma.equipment.findUnique as any) = async () => ({ ...currentEquipment, status: '可用' })
|
|
;(prisma.$transaction as any) = async () => {
|
|
transactionCount += 1
|
|
}
|
|
|
|
try {
|
|
for (const body of [{ department_id: 8 }, { status: '维护中' }]) {
|
|
const { res, responseBody } = createMockResponse('admin')
|
|
await updateEquipment({ params: { id: '903' }, body } as any, res as any)
|
|
assert.equal(responseBody.statusCode, 422)
|
|
}
|
|
assert.equal(transactionCount, 0)
|
|
} finally {
|
|
;(prisma.equipment.findUnique as any) = originalFindUnique
|
|
;(prisma.$transaction as any) = originalTransaction
|
|
}
|
|
})
|
|
|
|
test('资产状态在保存前变化时 CAS 失败并返回 409', async () => {
|
|
const originalFindUnique = prisma.equipment.findUnique
|
|
const originalTransaction = prisma.$transaction
|
|
let capturedWhere: any = null
|
|
let findUniqueCount = 0
|
|
|
|
;(prisma.equipment.findUnique as any) = async () => {
|
|
findUniqueCount += 1
|
|
return currentEquipment
|
|
}
|
|
;(prisma.$transaction as any) = async (callback: any) => callback({
|
|
$queryRawUnsafe: async () => [],
|
|
equipment: {
|
|
updateMany: async (args: any) => {
|
|
capturedWhere = args.where
|
|
return { count: 0 }
|
|
}
|
|
}
|
|
})
|
|
|
|
try {
|
|
const { res, responseBody } = createMockResponse()
|
|
await updateEquipment(
|
|
{ params: { id: '903' }, body: { equipment_name: '新名称' } } as any,
|
|
res as any
|
|
)
|
|
|
|
assert.equal(responseBody.statusCode, 409)
|
|
assert.equal(findUniqueCount, 1, '冲突后不应读取并返回伪成功数据')
|
|
assert.equal(capturedWhere.status, currentEquipment.status)
|
|
assert.equal(capturedWhere.department_id, currentEquipment.department_id)
|
|
assert.equal(capturedWhere.parent_id, currentEquipment.parent_id)
|
|
assert.equal(capturedWhere.remaining_quantity, currentEquipment.remaining_quantity)
|
|
assert.equal(capturedWhere.updated_at, currentEquipment.updated_at)
|
|
} finally {
|
|
;(prisma.equipment.findUnique as any) = originalFindUnique
|
|
;(prisma.$transaction as any) = originalTransaction
|
|
}
|
|
})
|
|
|
|
test('行政纠错只写入本次提交的基础字段', async () => {
|
|
const originalFindUnique = prisma.equipment.findUnique
|
|
const originalTransaction = prisma.$transaction
|
|
const originalBorrowCount = prisma.borrowRecord.count
|
|
const originalMaintenanceCount = prisma.maintenanceRecord.count
|
|
const originalDisposalItemCount = prisma.disposalItem.count
|
|
const originalDepartmentFindFirst = prisma.department.findFirst
|
|
const administrativeEquipment = {
|
|
...currentEquipment,
|
|
status: '可用',
|
|
department_id: 7,
|
|
parent_id: null
|
|
}
|
|
let capturedUpdateData: any = null
|
|
|
|
;(prisma.equipment.findUnique as any) = async () => administrativeEquipment
|
|
;(prisma.borrowRecord.count as any) = async () => 0
|
|
;(prisma.maintenanceRecord.count as any) = async () => 0
|
|
;(prisma.disposalItem.count as any) = async () => 0
|
|
;(prisma.department.findFirst as any) = async () => ({ id: 7, name: '行政部' })
|
|
;(prisma.$transaction as any) = async (callback: any) => callback({
|
|
$queryRawUnsafe: async () => [],
|
|
operationLog: { create: async () => ({ id: 1 }) },
|
|
borrowRecord: { count: async () => 0 },
|
|
maintenanceRecord: { count: async () => 0 },
|
|
disposalItem: { count: async () => 0 },
|
|
equipment: {
|
|
updateMany: async (args: any) => {
|
|
capturedUpdateData = args.data
|
|
return { count: 1 }
|
|
},
|
|
count: async () => 0
|
|
}
|
|
})
|
|
|
|
try {
|
|
const { res, responseBody } = createMockResponse('administrative')
|
|
await updateEquipment(
|
|
{ params: { id: '903' }, body: { equipment_name: '更正后的主机名称' } } as any,
|
|
res as any
|
|
)
|
|
|
|
assert.equal(responseBody.statusCode, 200)
|
|
assert.deepEqual(capturedUpdateData, { equipment_name: '更正后的主机名称' })
|
|
assert.equal(Object.prototype.hasOwnProperty.call(capturedUpdateData, 'status'), false)
|
|
assert.equal(Object.prototype.hasOwnProperty.call(capturedUpdateData, 'department_id'), false)
|
|
assert.equal(Object.prototype.hasOwnProperty.call(capturedUpdateData, 'parent_id'), false)
|
|
assert.equal(Object.prototype.hasOwnProperty.call(capturedUpdateData, 'remaining_quantity'), false)
|
|
} finally {
|
|
;(prisma.equipment.findUnique as any) = originalFindUnique
|
|
;(prisma.$transaction as any) = originalTransaction
|
|
;(prisma.borrowRecord.count as any) = originalBorrowCount
|
|
;(prisma.maintenanceRecord.count as any) = originalMaintenanceCount
|
|
;(prisma.disposalItem.count as any) = originalDisposalItemCount
|
|
;(prisma.department.findFirst as any) = originalDepartmentFindFirst
|
|
}
|
|
})
|
|
|
|
test('行政不能用非正整数数量纠错间接改变资产状态', async () => {
|
|
const originalFindUnique = prisma.equipment.findUnique
|
|
const originalTransaction = prisma.$transaction
|
|
const originalBorrowCount = prisma.borrowRecord.count
|
|
const originalMaintenanceCount = prisma.maintenanceRecord.count
|
|
const originalDisposalItemCount = prisma.disposalItem.count
|
|
let transactionCount = 0
|
|
|
|
;(prisma.equipment.findUnique as any) = async () => ({ ...currentEquipment, status: '可用' })
|
|
;(prisma.borrowRecord.count as any) = async () => 0
|
|
;(prisma.maintenanceRecord.count as any) = async () => 0
|
|
;(prisma.disposalItem.count as any) = async () => 0
|
|
;(prisma.$transaction as any) = async () => {
|
|
transactionCount += 1
|
|
}
|
|
|
|
try {
|
|
for (const invalidQuantity of [0, -1, 1.5]) {
|
|
const { res, responseBody } = createMockResponse('administrative')
|
|
await updateEquipment(
|
|
{ params: { id: '903' }, body: { total_quantity: invalidQuantity } } as any,
|
|
res as any
|
|
)
|
|
assert.equal(responseBody.statusCode, 422)
|
|
assert.match(responseBody.body.message, /总数量必须/)
|
|
}
|
|
assert.equal(transactionCount, 0)
|
|
} finally {
|
|
;(prisma.equipment.findUnique as any) = originalFindUnique
|
|
;(prisma.$transaction as any) = originalTransaction
|
|
;(prisma.borrowRecord.count as any) = originalBorrowCount
|
|
;(prisma.maintenanceRecord.count as any) = originalMaintenanceCount
|
|
;(prisma.disposalItem.count as any) = originalDisposalItemCount
|
|
}
|
|
})
|
|
|
|
test('修改资产类型名称时同步更新 asset_type_id', async () => {
|
|
const originalFindUnique = prisma.equipment.findUnique
|
|
const originalCount = prisma.equipment.count
|
|
const originalAssetTypeFindFirst = prisma.assetType.findFirst
|
|
const originalTransaction = prisma.$transaction
|
|
const equipment = { ...currentEquipment, status: '可用', asset_type_id: 1 }
|
|
const newType = {
|
|
id: 42,
|
|
name: '配件',
|
|
status: 'active',
|
|
can_be_parent: true,
|
|
can_be_child: true
|
|
}
|
|
let capturedUpdateData: any = null
|
|
|
|
;(prisma.equipment.findUnique as any) = async () => equipment
|
|
;(prisma.equipment.count as any) = async () => 0
|
|
;(prisma.assetType.findFirst as any) = async () => newType
|
|
;(prisma.$transaction as any) = async (callback: any) => callback({
|
|
$queryRawUnsafe: async () => [],
|
|
operationLog: { create: async () => ({ id: 1 }) },
|
|
assetType: { findUnique: async () => newType },
|
|
equipment: {
|
|
count: async () => 0,
|
|
updateMany: async (args: any) => {
|
|
capturedUpdateData = args.data
|
|
return { count: 1 }
|
|
}
|
|
}
|
|
})
|
|
|
|
try {
|
|
const { res, responseBody } = createMockResponse('admin')
|
|
await updateEquipment(
|
|
{ params: { id: '903' }, body: { equipment_type: '配件' } } as any,
|
|
res as any
|
|
)
|
|
|
|
assert.equal(responseBody.statusCode, 200)
|
|
assert.deepEqual(capturedUpdateData, {
|
|
equipment_type: '配件',
|
|
asset_type_id: 42
|
|
})
|
|
} finally {
|
|
;(prisma.equipment.findUnique as any) = originalFindUnique
|
|
;(prisma.equipment.count as any) = originalCount
|
|
;(prisma.assetType.findFirst as any) = originalAssetTypeFindFirst
|
|
;(prisma.$transaction as any) = originalTransaction
|
|
}
|
|
})
|
|
|
|
test('有子资产的设备不能改成不支持父资产的类型', async () => {
|
|
const originalFindUnique = prisma.equipment.findUnique
|
|
const originalCount = prisma.equipment.count
|
|
const originalAssetTypeFindFirst = prisma.assetType.findFirst
|
|
const originalTransaction = prisma.$transaction
|
|
let transactionCount = 0
|
|
|
|
;(prisma.equipment.findUnique as any) = async () => ({ ...currentEquipment, status: '可用' })
|
|
;(prisma.equipment.count as any) = async () => 1
|
|
;(prisma.assetType.findFirst as any) = async () => ({
|
|
id: 43,
|
|
name: '普通配件',
|
|
status: 'active',
|
|
can_be_parent: false,
|
|
can_be_child: true
|
|
})
|
|
;(prisma.$transaction as any) = async () => {
|
|
transactionCount += 1
|
|
}
|
|
|
|
try {
|
|
const { res, responseBody } = createMockResponse('admin')
|
|
await updateEquipment(
|
|
{ params: { id: '903' }, body: { equipment_type: '普通配件' } } as any,
|
|
res as any
|
|
)
|
|
|
|
assert.equal(responseBody.statusCode, 422)
|
|
assert.match(responseBody.body.message, /必须允许作为父资产/)
|
|
assert.equal(transactionCount, 0)
|
|
} finally {
|
|
;(prisma.equipment.findUnique as any) = originalFindUnique
|
|
;(prisma.equipment.count as any) = originalCount
|
|
;(prisma.assetType.findFirst as any) = originalAssetTypeFindFirst
|
|
;(prisma.$transaction as any) = originalTransaction
|
|
}
|
|
})
|