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