GuDingZiChan/backend/src/controllers/__tests__/equipmentController.update-status.test.ts

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
}
})