-- Copyright (c) 2026 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You can obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local lu = require 'luaunit'

-- Mock dependencies before requiring the module under test
local mock_skynet = {
    fork_once = function(func)
        func() -- Execute immediately for testing
    end,
    fork = function(func)
        func() -- Execute immediately for testing
    end
}
package.loaded['skynet'] = mock_skynet

local mock_context = {
    new = function()
        return {}
    end
}
package.loaded['mc.context'] = mock_context

local mock_utils = {
    secure_tar_unzip = function(file_path, dir, max_size, max_files)
        return true
    end
}
package.loaded['mc.utils'] = mock_utils
-- drive_upgrade.lua 中直接使用 utils，可能是全局变量或缺少 require
-- 为了测试，我们也将其设置为全局变量
_G.utils = mock_utils

-- Mock client
local client_calls = {}
local client_callbacks = {}  -- 用于存储回调函数

local mock_client = {
    UpdateServiceUpdateServicePrepareReply = function(self, ctx, system_id, firmware_type, version, ret_code, parameters)
        table.insert(client_calls, {
            method = 'PrepareReply',
            system_id = system_id,
            firmware_type = firmware_type,
            version = version,
            ret_code = ret_code,
            parameters = parameters
        })
    end,
    UpdateServiceUpdateServiceProcessReply = function(self, ctx, system_id, firmware_type, ret_code, parameters)
        table.insert(client_calls, {
            method = 'ProcessReply',
            system_id = system_id,
            firmware_type = firmware_type,
            ret_code = ret_code,
            parameters = parameters
        })
    end,
    UpdateServiceUpdateServiceFinishReply = function(self, ctx, system_id, firmware_type, ret_code, parameters)
        table.insert(client_calls, {
            method = 'FinishReply',
            system_id = system_id,
            firmware_type = firmware_type,
            ret_code = ret_code,
            parameters = parameters
        })
    end,
    UpdateServiceUpdateServiceActiveProcessReply = function(self, ctx, system_id, firmware_type, ret_code, parameters)
        table.insert(client_calls, {
            method = 'ActiveProcessReply',
            system_id = system_id,
            firmware_type = firmware_type,
            ret_code = ret_code,
            parameters = parameters
        })
    end,
    UpdateServiceUpdateServiceUpdateUpgradeStatus = function(self, ctx, system_id, firmware_type, code, progress, status, parameters)
        table.insert(client_calls, {
            method = 'UpdateUpgradeStatus',
            system_id = system_id,
            firmware_type = firmware_type,
            code = code,
            progress = progress,
            status = status,
            parameters = parameters
        })
    end,
    PFileFileChown = function(self, ctx, nil_arg, path, uid, gid)
        table.insert(client_calls, {
            method = 'FileChown',
            path = path,
            uid = uid,
            gid = gid
        })
        return true, nil
    end,
    FirmwareActiveFirmwareActiveRegisterActiveAction = function(self, ctx, active_action)
        table.insert(client_calls, {
            method = 'RegisterActiveAction',
            ctx = ctx,
            active_action = active_action
        })
        -- 默认返回成功，测试用例可以通过修改这个函数来模拟失败
        return true
    end,
    SubscribeUpdateServiceUpdateServiceUpgradePrepareSignal = function(callback)
        client_callbacks.on_prepare = callback
    end,
    SubscribeUpdateServiceUpdateServiceUpgradeProcessSignal = function(callback)
        client_callbacks.on_process = callback
    end,
    SubscribeUpdateServiceUpdateServiceUpgradeFinishSignal = function(callback)
        client_callbacks.on_finish = callback
    end,
    SubscribeUpdateServiceUpdateServiceActiveProcessSignal = function(callback)
        client_callbacks.on_active = callback
    end,
    -- drive_upgrade 使用的 FirmwareActive 相关接口
    SubscribeFirmwareActiveFirmwareActiveActiveProcessSignal = function(callback)
        client_callbacks.on_active = callback
    end,
    PFirmwareActiveFirmwareActiveRegisterActiveAction = function(self, ctx, active_action)
        table.insert(client_calls, {
            method = 'RegisterActiveAction',
            ctx = ctx,
            active_action = active_action
        })
        return true
    end,
    PFirmwareActiveFirmwareActiveUpdateActiveStatus = function(self, ctx, param)
        table.insert(client_calls, { method = 'UpdateActiveStatus', param = param })
        return true
    end,
    PFirmwareActiveFirmwareActiveDeleteActiveAction = function(self, ctx, param)
        table.insert(client_calls, { method = 'DeleteActiveAction', param = param })
        return true, nil
    end,
    FirmwareActiveFirmwareActiveActiveProcessReply = function(self, ctx, system_id, firmware_type, ret_code)
        table.insert(client_calls, {
            method = 'ActiveProcessReply',
            system_id = system_id,
            firmware_type = firmware_type,
            ret_code = ret_code
        })
    end
}
package.loaded['storage.client'] = mock_client

-- Mock parser_cfg
local mock_parser_cfg = {
    get_cfgs = function(path)
        if path == '/tmp/invalid.cfg' then
            return {}
        end
        return {
            Firmware0 = {
                name = 'Firmware0',
                component_id = 0x22,  -- DRIVE_COMPONENT_ID
                component_idex = 0xffffffff,  -- DRIVE_COMPONENT_IDEX
                model = {'Model1', 'Model2'}
            },
            Firmware1 = {
                name = 'Firmware1',
                component_id = 0x22,
                component_idex = 0xffffffff,
                model = {'Model3'}
            },
            Firmware2 = {
                name = 'Firmware2',
                component_id = 0x11,  -- 非硬盘固件
                component_idex = 0x22
            }
        }
    end,
    parse_dir = function(file_path)
        return '/tmp/', 'Firmware0'
    end
}
package.loaded['upgrade.parser_cfg'] = mock_parser_cfg

-- Mock upgrade_defs
local upgrade_defs = require 'upgrade.upgrade_defs'

-- 使用实际的 common_def，不需要 mock
local common_def = require 'common_def'

-- Mock method_misc (华为自研RAID卡 RefControllerTypeId 96-113)
local mock_method_misc = {
    test_controller_vendor = function(ref_type_id, vendor)
        return ref_type_id >= 96 and ref_type_id <= 113
    end
}
package.loaded['method_misc'] = mock_method_misc

-- Mock fructrl_handle
local mock_fructrl_handle = {
    set_power_off_lock_until_success = function(lock, timeout, reason)
        return true
    end
}
package.loaded['upgrade.fructrl_handle'] = mock_fructrl_handle

-- Mock sml
local mock_sml = {
    pd_upgrade_firmware = function(ctrl_id, device_id, firmware_path)
        return common_def.RET_OK
    end,
    pd_active_firmware = function(ctrl_id, device_id)
        return common_def.RET_OK
    end
}
package.loaded['sml'] = mock_sml

-- Mock bus_monitor_service（捕获 smbios 回调以便测试 init 中注册的下电清理逻辑）
local smbios_status_callback_captured = nil
local mock_bus_monitor_service = {
    get_instance = function()
        return {
            on_smbios_status_changed = {
                on = function(_, cb)
                    smbios_status_callback_captured = cb
                end
            }
        }
    end
}
package.loaded['bus_monitor_service'] = mock_bus_monitor_service

-- Mock drive collection
-- 注意：需要清除可能已加载的 drive.drive_object，确保使用 mock
package.loaded['drive.drive_object'] = nil
local mock_drive_objects = {}
local mock_drive_collection = {
    collection = {
        objects = mock_drive_objects,
        fold = function(self, func, acc)
            local exit_loop
            for key, obj in pairs(self.objects) do
                acc, exit_loop = func(acc, obj, key)
                if exit_loop then
                    break
                end
            end
            return acc
        end,
        find = function(self, criteria)
            if not criteria or not criteria.Name then
                return nil
            end
            for _, obj in pairs(self.objects) do
                if obj.Name == criteria.Name then
                    return obj
                end
            end
            return nil
        end
    }
}
package.loaded['drive.drive_object'] = mock_drive_collection

-- Mock drive_collection (drive_upgrade 使用 get_presence_on_drives、get_drive、get_drive_by_drive_id)
local mock_drive_collection_module = {
    get_presence_on_drives = function(self)
        local drives = {}
        for _, obj in pairs(mock_drive_objects) do
            if obj.Presence == 1 and obj.RefControllerId ~= common_def.INVALID_U8 then
                table.insert(drives, obj)
            end
        end
        return drives
    end,
    get_drive = function(self, drive_name)
        for _, obj in pairs(mock_drive_objects) do
            if obj.Name == drive_name then
                return obj
            end
        end
        return nil
    end,
    get_drive_by_drive_id = function(self, drive_id)
        for _, obj in pairs(mock_drive_objects) do
            if obj.Id == drive_id then
                return obj
            end
        end
        return nil
    end
}
package.loaded['drive.drive_collection'] = mock_drive_collection_module

-- Mock storageconfig
local mock_storageconfig_instance = {
    smbios_status = 3, -- SMBIOS_WRITE_FINISH
    power_state = 'ON'
}
local mock_storageconfig = {
    get_instance = function()
        return mock_storageconfig_instance
    end
}
package.loaded['storageconfig.storageconfig_object'] = mock_storageconfig

-- Now require the module under test
-- 注意：清除可能已加载的模块，确保使用最新的 mock
package.loaded['upgrade.drive_upgrade'] = nil
local drive_upgrade = require 'upgrade.drive_upgrade'

TEST_Drive_Upgrade = {}

function TEST_Drive_Upgrade:setUp()
    -- Reset client calls
    client_calls = {}
    client_callbacks = {}

    -- Reset mock data - 清空并重新设置对象表
    for k in pairs(mock_drive_objects) do
        mock_drive_objects[k] = nil
    end
    
    -- 确保 mock_drive_collection.collection.objects 指向 mock_drive_objects
    mock_drive_collection.collection.objects = mock_drive_objects

    -- drive_upgrade.lua 内部枚举硬盘依赖 drive.drive_object.collection:fold（真实实现走 main_index.objects）。
    -- 由于 test_upgrade/init.lua 会在 load 后恢复 package.loaded，避免依赖 package.loaded mock，
    -- 这里直接把真实 drive_object 的 main_index.objects 指向 mock_drive_objects，确保枚举数据一致。
    local real_drive_obj = require 'drive.drive_object'
    if real_drive_obj and real_drive_obj.collection and real_drive_obj.collection.indexs
        and real_drive_obj.collection.indexs.main_index then
        self._orig_real_drive_objects = real_drive_obj.collection.indexs.main_index.objects
        real_drive_obj.collection.indexs.main_index.objects = mock_drive_objects
    end

    -- Reset storageconfig
    mock_storageconfig_instance.smbios_status = 3
    mock_storageconfig_instance.power_state = 'ON'

    -- Get a fresh instance
    self.upgrade_instance = drive_upgrade.get_instance()
    self.upgrade_instance.target_drives = {}
    self.upgrade_instance.cfgs = {}
    self.upgrade_instance.is_drive_upgrading = false
    self.upgrade_instance.upgraded_drives_success = {}

    -- 为 finish/active 流程构造 reset_local_db 句柄
    local records = {}
    local fake_db = {}
    fake_db.records = records
    fake_db.UpgradedDrivesPendingActive = {
        truncate = function()
            for i = #records, 1, -1 do
                records[i] = nil
            end
        end
    }
    function fake_db:select(tbl)
        return {
            where = function(_, cond)
                local name = cond.DriveName
                return {
                    first = function()
                        for _, r in ipairs(records) do
                            if r.DriveName == name then
                                return r
                            end
                        end
                        return nil
                    end,
                    all = function()
                        return records
                    end
                }
            end,
            all = function()
                return records
            end
        }
    end
    setmetatable(fake_db.UpgradedDrivesPendingActive, {
        __call = function(_, init)
            local rec = { DriveName = init.DriveName }
            function rec:save()
                table.insert(records, rec)
            end
            return rec
        end
    })
    self.upgrade_instance.reset_local_db = fake_db
end

function TEST_Drive_Upgrade:tearDown()
    client_calls = {}
    if self._orig_real_drive_objects then
        local real_drive_obj = require 'drive.drive_object'
        if real_drive_obj and real_drive_obj.collection and real_drive_obj.collection.indexs
            and real_drive_obj.collection.indexs.main_index then
            real_drive_obj.collection.indexs.main_index.objects = self._orig_real_drive_objects
        end
        self._orig_real_drive_objects = nil
    end
end

-- 测试初始化
function TEST_Drive_Upgrade:test_init()
    local instance = drive_upgrade.get_instance()
    instance:init()

    lu.assertNotNil(instance.target_drives)
    lu.assertEquals(#instance.target_drives, 0)
    lu.assertNotNil(instance.cfgs)
    lu.assertEquals(#instance.cfgs, 0)
    lu.assertEquals(instance.is_drive_upgrading, false)
    lu.assertNotNil(instance.upgraded_drives_success)
    lu.assertEquals(#instance.upgraded_drives_success, 0)

    -- 验证回调函数已注册
    lu.assertNotNil(client_callbacks.on_prepare)
    lu.assertNotNil(client_callbacks.on_process)
    lu.assertNotNil(client_callbacks.on_finish)
    lu.assertNotNil(client_callbacks.on_active)
end

-- 测试 init 中注册的 smbios 下电回调：status ~= SMBIOS_WRITE_FINISH 时调用 DeleteActiveAction 与 clear_pending_active_drives
function TEST_Drive_Upgrade:test_init_smbios_callback_invoked()
    self.upgrade_instance:init()
    lu.assertNotNil(smbios_status_callback_captured)

    client_calls = {}
    smbios_status_callback_captured(0)  -- 非 SMBIOS_WRITE_FINISH(3)

    local has_delete = false
    for _, c in ipairs(client_calls) do
        if c.method == 'DeleteActiveAction' then
            has_delete = true
            break
        end
    end
    lu.assertTrue(has_delete, 'DeleteActiveAction should be called when smbios status changed')
end

-- 测试 clear_pending_active_drives 中 truncate 抛错时仍继续执行（覆盖 pcall 错误路径 131-135）
function TEST_Drive_Upgrade:test_clear_pending_active_drives_truncate_error()
    self.upgrade_instance:init()
    lu.assertNotNil(smbios_status_callback_captured)
    local orig_truncate = self.upgrade_instance.reset_local_db.UpgradedDrivesPendingActive.truncate
    self.upgrade_instance.reset_local_db.UpgradedDrivesPendingActive.truncate = function()
        error('truncate failed')
    end

    client_calls = {}
    smbios_status_callback_captured(1)
    -- 应仍调用 DeleteActiveAction（clear_pending_active_drives 内部 pcall 捕获异常后继续）
    local has_delete = false
    for _, c in ipairs(client_calls) do
        if c.method == 'DeleteActiveAction' then
            has_delete = true
            break
        end
    end
    lu.assertTrue(has_delete)
    self.upgrade_instance.reset_local_db.UpgradedDrivesPendingActive.truncate = orig_truncate
end

-- 测试on_upgrade_prepare - 非Drive类型固件
function TEST_Drive_Upgrade:test_on_upgrade_prepare_not_drive_firmware()
    -- 添加一个在位的硬盘
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Bmc', -- 不是Drive类型
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 不应该有任何client调用
    lu.assertEquals(#client_calls, 0)
end

-- 测试on_upgrade_prepare - 已经在升级中
function TEST_Drive_Upgrade:test_on_upgrade_prepare_already_upgrading()
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }
    self.upgrade_instance.is_drive_upgrading = true

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 应该返回IN_UPGRADING错误
    lu.assertEquals(#client_calls, 1)
    lu.assertEquals(client_calls[1].method, 'PrepareReply')
    lu.assertEquals(client_calls[1].ret_code, upgrade_defs.RET.IN_UPGRADING)
end

-- 测试on_upgrade_prepare - SMBIOS状态不正确
function TEST_Drive_Upgrade:test_on_upgrade_prepare_smbios_not_ready()
    mock_storageconfig_instance.smbios_status = 0 -- 不是SMBIOS_WRITE_FINISH
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 应该返回错误
    lu.assertEquals(#client_calls, 1)
    lu.assertEquals(client_calls[1].method, 'PrepareReply')
    lu.assertEquals(client_calls[1].ret_code, upgrade_defs.RET.ERR)
end

-- 测试on_upgrade_prepare - 电源状态不正确
function TEST_Drive_Upgrade:test_on_upgrade_prepare_power_not_on()
    mock_storageconfig_instance.power_state = 'OFF'
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 应该返回错误
    lu.assertEquals(#client_calls, 1)
    lu.assertEquals(client_calls[1].method, 'PrepareReply')
    lu.assertEquals(client_calls[1].ret_code, upgrade_defs.RET.ERR)
end

-- 测试on_upgrade_prepare - 下电锁设置失败（覆盖 check_upgrade_preconditions 中 fructrl_handle 失败路径）
function TEST_Drive_Upgrade:test_on_upgrade_prepare_fructrl_lock_failed()
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1
    }
    local orig = mock_fructrl_handle.set_power_off_lock_until_success
    mock_fructrl_handle.set_power_off_lock_until_success = function(lock, timeout, reason)
        return false
    end

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    lu.assertEquals(#client_calls, 1)
    lu.assertEquals(client_calls[1].method, 'PrepareReply')
    lu.assertEquals(client_calls[1].ret_code, upgrade_defs.RET.ERR)
    mock_fructrl_handle.set_power_off_lock_until_success = orig
end

-- 测试on_upgrade_prepare - 没有在位的硬盘
function TEST_Drive_Upgrade:test_on_upgrade_prepare_no_present_drives()
    -- 不添加任何硬盘对象

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 应该返回错误
    lu.assertEquals(#client_calls, 1)
    lu.assertEquals(client_calls[1].method, 'PrepareReply')
    lu.assertEquals(client_calls[1].ret_code, upgrade_defs.RET.ERR)
end

-- 测试on_upgrade_prepare - FileChown失败(cfg_path)
function TEST_Drive_Upgrade:test_on_upgrade_prepare_chown_cfg_failed()
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    -- Mock FileChown失败
    local original_chown = mock_client.PFileFileChown
    local chown_call_count = 0
    mock_client.PFileFileChown = function(ctx, nil_arg, path, uid, gid)
        chown_call_count = chown_call_count + 1
        table.insert(client_calls, {
            method = 'FileChown',
            path = path,
            uid = uid,
            gid = gid
        })
        if chown_call_count == 1 then  -- 第一次调用(cfg_path)失败
            return false, 'permission denied'
        end
        return true, nil
    end

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 应该返回错误
    lu.assertTrue(#client_calls >= 2)  -- FileChown + PrepareReply
    local prepare_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'PrepareReply' then
            prepare_reply = call
            break
        end
    end
    lu.assertNotNil(prepare_reply)
    lu.assertEquals(prepare_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)  -- 资源已清理

    -- 恢复原始函数
    mock_client.PFileFileChown = original_chown
end

-- 测试on_upgrade_prepare - FileChown失败(hpm_path)
function TEST_Drive_Upgrade:test_on_upgrade_prepare_chown_hpm_failed()
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    -- Mock FileChown失败
    local original_chown = mock_client.PFileFileChown
    local chown_call_count = 0
    mock_client.PFileFileChown = function(ctx, nil_arg, path, uid, gid)
        chown_call_count = chown_call_count + 1
        table.insert(client_calls, {
            method = 'FileChown',
            path = path,
            uid = uid,
            gid = gid
        })
        if chown_call_count == 2 then  -- 第二次调用(hpm_path)失败
            return false, 'permission denied'
        end
        return true, nil
    end

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 应该返回错误
    lu.assertTrue(#client_calls >= 3)  -- FileChown(2次) + PrepareReply
    local prepare_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'PrepareReply' then
            prepare_reply = call
            break
        end
    end
    lu.assertNotNil(prepare_reply)
    lu.assertEquals(prepare_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)  -- 资源已清理

    -- 恢复原始函数
    mock_client.PFileFileChown = original_chown
end

-- 测试on_upgrade_prepare - 成功场景
function TEST_Drive_Upgrade:test_on_upgrade_prepare_success()
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 应该有FileChown和PrepareReply调用
    lu.assertTrue(#client_calls >= 3)  -- FileChown(2次) + PrepareReply

    -- 检查FileChown调用
    local chown_calls = {}
    for _, call in ipairs(client_calls) do
        if call.method == 'FileChown' then
            table.insert(chown_calls, call)
        end
    end
    lu.assertEquals(#chown_calls, 2)
    lu.assertEquals(chown_calls[1].path, '/tmp/test.cfg')
    lu.assertEquals(chown_calls[2].path, '/tmp/test.hpm')

    -- 检查PrepareReply
    local prepare_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'PrepareReply' then
            prepare_reply = call
            break
        end
    end
    lu.assertNotNil(prepare_reply)
    lu.assertEquals(prepare_reply.ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, true)  -- 标志已设置

    -- 检查cfgs已过滤（只保留硬盘固件配置）
    lu.assertNotNil(self.upgrade_instance.cfgs)
    lu.assertNotNil(self.upgrade_instance.cfgs['Firmware0'])
    lu.assertNotNil(self.upgrade_instance.cfgs['Firmware1'])
    lu.assertNil(self.upgrade_instance.cfgs['Firmware2'])  -- 非硬盘固件应被过滤
end

-- 测试on_upgrade_prepare - pcall异常处理
function TEST_Drive_Upgrade:test_on_upgrade_prepare_pcall_error()
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    -- Mock parser_cfg.get_cfgs抛出异常
    local original_get_cfgs = mock_parser_cfg.get_cfgs
    mock_parser_cfg.get_cfgs = function(path)
        error('parse config failed')
    end

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 应该捕获异常并返回错误
    local prepare_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'PrepareReply' then
            prepare_reply = call
            break
        end
    end
    lu.assertNotNil(prepare_reply)
    lu.assertEquals(prepare_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)  -- 资源已清理

    -- 恢复原始函数
    mock_parser_cfg.get_cfgs = original_get_cfgs
end

-- 测试on_upgrade_process - 非Drive类型固件
function TEST_Drive_Upgrade:test_on_upgrade_process_not_drive_firmware()
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Bmc',
        '/tmp/firmware.bin',
        {}
    )

    -- 不应该有任何client调用
    lu.assertEquals(#client_calls, 0)
end

-- 测试on_upgrade_process - 没有配置信息
function TEST_Drive_Upgrade:test_on_upgrade_process_no_cfgs()
    self.upgrade_instance.cfgs = {}

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/firmware.bin',
        {}
    )

    -- 应该返回错误
    lu.assertTrue(#client_calls >= 1)
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)  -- 资源已清理
end

-- 测试on_upgrade_process - FileChown失败
function TEST_Drive_Upgrade:test_on_upgrade_process_chown_failed()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }

    -- Mock FileChown失败
    local original_chown = mock_client.PFileFileChown
    mock_client.PFileFileChown = function(ctx, nil_arg, path, uid, gid)
        table.insert(client_calls, {
            method = 'FileChown',
            path = path,
            uid = uid,
            gid = gid
        })
        return false, 'permission denied'
    end

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        {}
    )

    -- 应该返回错误
    lu.assertTrue(#client_calls >= 2)  -- FileChown + ProcessReply
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)  -- 资源已清理

    -- 恢复原始函数
    mock_client.PFileFileChown = original_chown
end

-- 测试on_upgrade_process - 找不到匹配的cfg
function TEST_Drive_Upgrade:test_on_upgrade_process_no_matching_cfg()
    self.upgrade_instance.cfgs = {
        Firmware1 = {
            name = 'Firmware1',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model3'}
        }
    }

    -- Mock parse_dir返回Firmware0，但cfgs中没有Firmware0
    local original_parse_dir = mock_parser_cfg.parse_dir
    mock_parser_cfg.parse_dir = function(file_path)
        return '/tmp/', 'Firmware0'
    end

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        {}
    )

    -- 应该返回错误
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)  -- 资源已清理

    -- 恢复原始函数
    mock_parser_cfg.parse_dir = original_parse_dir
end

-- 测试on_upgrade_process - 没有匹配的硬盘
function TEST_Drive_Upgrade:test_on_upgrade_process_no_matching_drives()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'NonExistentModel'}  -- 不存在的型号
        }
    }

    -- 添加一个不匹配的硬盘
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',  -- 不匹配
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        {}
    )

    -- 应该返回错误
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)  -- 资源已清理
end

-- 测试on_upgrade_process - 成功场景
function TEST_Drive_Upgrade:test_on_upgrade_process_success()
    -- 先设置准备阶段的数据
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1', 'Model2'}
        }
    }

    -- 添加匹配的硬盘
    -- 注意：RefControllerTypeId 需要在 96-113 范围内（华为RAID卡类型）
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',  -- 匹配
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1,
        device_id = 1
    }
    mock_drive_objects['drive2'] = {
        NodeId = 'drive2',
        Name = 'Drive2',
        Presence = 1,
        Model = 'Model2',  -- 匹配
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 2,
        device_id = 2
    }

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        {}
    )

    lu.assertTrue(#client_calls >= 2)
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    if process_reply.ret_code == upgrade_defs.RET.OK then
        local has_status = false
        for _, c in ipairs(client_calls) do
            if c.method == 'UpdateUpgradeStatus' then
                has_status = true
                break
            end
        end
        lu.assertTrue(has_status)
        lu.assertEquals(#self.upgrade_instance.target_drives, 2)
        lu.assertEquals(#self.upgrade_instance.upgraded_drives_success, 2)
    end
end

-- 测试 on_upgrade_process - parameters.Targets 为逗号分隔字符串且合法时指定硬盘升级成功
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_string_success()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    -- 指定硬盘：Redfish URI 对应 Id=1，需在 mock 中提供 Id 且满足 在位/自研/Model
    mock_drive_objects['drive1'] = {
        Id = 1,
        NodeId = 'HDDPlaneDisk1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    local targets_str = upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '1'
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = targets_str }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    if process_reply.ret_code == upgrade_defs.RET.OK then
        lu.assertEquals(#self.upgrade_instance.target_drives, 1)
        lu.assertEquals(self.upgrade_instance.target_drives[1].Name, 'Drive1')
    end
end

-- 测试 on_upgrade_process - parameters.Targets 逗号分隔多个 URI 成功
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_string_multiple_success()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    mock_drive_objects['drive1'] = {
        Id = 1,
        NodeId = 'HDDPlaneDisk1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    mock_drive_objects['drive2'] = {
        Id = 2,
        NodeId = 'HDDPlaneDisk2',
        Name = 'Drive2',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 2,
        device_id = 2
    }
    local targets_str = upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '1,' .. upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '2'
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = targets_str }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    if process_reply.ret_code == upgrade_defs.RET.OK then
        lu.assertEquals(#self.upgrade_instance.target_drives, 2)
    end
end

-- 测试 on_upgrade_process - parameters.Targets 格式非法则返回 ERR
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_string_invalid_format()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    mock_drive_objects['drive1'] = {
        Id = 1,
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = '/invalid/uri/HDDPlaneDisk1' }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(#self.upgrade_instance.target_drives, 0)
end

-- 测试 on_upgrade_process - parameters.Targets 合法但 drive_id 找不到则返回 ERR
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_string_drive_not_found()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    -- 不添加 Id=99 的盘，get_drive_by_drive_id(99) 返回 nil
    mock_drive_objects['drive1'] = {
        Id = 1,
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    local targets_str = upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '99'
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = targets_str }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(#self.upgrade_instance.target_drives, 0)
end

-- 测试 on_upgrade_process - parameters.Targets 合法但指定盘不在位则返回 ERR
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_string_drive_not_present()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    mock_drive_objects['drive1'] = {
        Id = 1,
        NodeId = 'HDDPlaneDisk1',
        Name = 'Drive1',
        Presence = 0,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    local targets_str = upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '1'
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = targets_str }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(#self.upgrade_instance.target_drives, 0)
end

-- 测试 on_upgrade_process - parameters.Targets 指定盘 Model 不匹配则返回 ERR
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_string_model_not_match()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    mock_drive_objects['drive1'] = {
        Id = 1,
        NodeId = 'HDDPlaneDisk1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'OtherModel',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    local targets_str = upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '1'
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = targets_str }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(#self.upgrade_instance.target_drives, 0)
end

-- 测试 on_upgrade_process - parameters.Targets 合法但 cfg.model 为空则返回 no model in cfg（覆盖 get_and_validate 中 models 空分支）
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_empty_models()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {}
        }
    }
    mock_drive_objects['drive1'] = {
        Id = 1,
        NodeId = 'HDDPlaneDisk1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    local targets_str = upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '1'
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = targets_str }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(#self.upgrade_instance.target_drives, 0)
end

-- 测试 on_upgrade_process - parameters.Targets 仅逗号时无有效盘（覆盖 get_and_validate 中 #target_drives == 0 分支）
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_comma_only_no_valid_drives()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    mock_drive_objects['drive1'] = {
        Id = 1,
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = ',' }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(#self.upgrade_instance.target_drives, 0)
end

-- 测试 on_upgrade_process - parameters.Targets 指定盘非自研 RAID 则返回 ERR（覆盖 not self-developed RAID 分支）
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_string_not_self_developed_raid()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    mock_drive_objects['drive1'] = {
        Id = 1,
        NodeId = 'HDDPlaneDisk1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 200,
        RefControllerId = 1,
        device_id = 1
    }
    local targets_str = upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '1'
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = targets_str }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(#self.upgrade_instance.target_drives, 0)
end

-- 测试 on_upgrade_process - 指定硬盘升级时 pd_upgrade_firmware 失败则返回 ERR（覆盖 upgrade_single_drive 中 pcall 失败/ret~=OK 分支）
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_string_pd_upgrade_fails()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    mock_drive_objects['drive1'] = {
        Id = 1,
        NodeId = 'HDDPlaneDisk1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    local orig_pd_upgrade = mock_sml.pd_upgrade_firmware
    mock_sml.pd_upgrade_firmware = function(ctrl_id, device_id, path)
        return -1
    end
    local targets_str = upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '1'
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = targets_str }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(#self.upgrade_instance.upgraded_drives_success, 0)
    mock_sml.pd_upgrade_firmware = orig_pd_upgrade
end

-- 测试 on_upgrade_process - 指定硬盘升级时 pd_upgrade_firmware 抛异常则返回 ERR（覆盖 upgrade_single_drive 中 pcall 异常分支）
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_string_pd_upgrade_throws()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    mock_drive_objects['drive1'] = {
        Id = 1,
        NodeId = 'HDDPlaneDisk1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    local orig_pd_upgrade = mock_sml.pd_upgrade_firmware
    mock_sml.pd_upgrade_firmware = function(ctrl_id, device_id, path)
        error('pd_upgrade_firmware error')
    end
    local targets_str = upgrade_defs.REDFISH_DRIVE_URI_PREFIX .. '1'
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = targets_str }
    )
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(#self.upgrade_instance.upgraded_drives_success, 0)
    mock_sml.pd_upgrade_firmware = orig_pd_upgrade
end

-- 测试 on_upgrade_process - parameters.Targets 为空字符串时走全量匹配
function TEST_Drive_Upgrade:test_on_upgrade_process_with_targets_empty_string_uses_full_match()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1,
        device_id = 1
    }
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        { Targets = '' }
    )
    -- 应走 get_upgrade_target_drives，有目标盘则可能成功
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    -- Targets 为空时走全量匹配；若环境枚举不到盘则 target_drives 可能为 0，仅校验有 ProcessReply
    lu.assertTrue(#self.upgrade_instance.target_drives >= 0)
end

-- 测试 on_upgrade_prepare - parameters.FirmwareMode 为 Multiple 时回填 MultipleSupported
function TEST_Drive_Upgrade:test_on_upgrade_prepare_firmware_mode_multiple_sets_support()
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,
        RefControllerId = 1
    }
    local params = { FirmwareMode = upgrade_defs.FIRMWARE_MODE_MULTIPLE }
    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        params
    )
    lu.assertEquals(params.MultipleSupported, upgrade_defs.MULTIPLE_SUPPORTED)
    local prepare_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'PrepareReply' then
            prepare_reply = call
            break
        end
    end
    lu.assertNotNil(prepare_reply)
    lu.assertEquals(prepare_reply.ret_code, upgrade_defs.RET.OK)
end

-- 测试on_upgrade_process - pcall异常处理
function TEST_Drive_Upgrade:test_on_upgrade_process_pcall_error()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }

    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    -- Mock utils.secure_tar_unzip抛出异常
    local original_unzip = mock_utils.secure_tar_unzip
    mock_utils.secure_tar_unzip = function(file_path, dir, max_size, max_files)
        error('unzip failed')
    end

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        {}
    )

    -- 应该捕获异常并返回错误
    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    lu.assertEquals(process_reply.ret_code, upgrade_defs.RET.ERR)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)  -- 资源已清理

    -- 恢复原始函数
    mock_utils.secure_tar_unzip = original_unzip
end

-- 测试on_upgrade_finish - 非Drive类型固件
function TEST_Drive_Upgrade:test_on_upgrade_finish_not_drive_firmware()
    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Bmc',
        {}
    )

    -- 不应该有任何client调用
    lu.assertEquals(#client_calls, 0)
end

-- 测试on_upgrade_finish - 无已升级成功的硬盘时直接返回错误（覆盖 357-359）
function TEST_Drive_Upgrade:test_on_upgrade_finish_no_upgraded_drives()
    -- 确保 upgraded_drives_success 为空
    self.upgrade_instance.upgraded_drives_success = {}
    self.upgrade_instance.target_drives = {}
    client_calls = {}

    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Drive',
        {}
    )

    lu.assertTrue(#client_calls >= 1)
    local finish_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'FinishReply' then
            finish_reply = call
            break
        end
    end
    lu.assertNotNil(finish_reply)
    lu.assertEquals(finish_reply.ret_code, upgrade_defs.RET.ERR)
end

-- 测试on_upgrade_finish - 成功场景
function TEST_Drive_Upgrade:test_on_upgrade_finish_success()
    -- 设置target_drives和upgraded_drives_success
    self.upgrade_instance.target_drives = {
        {
            NodeId = 'drive1',
            Name = 'Drive1',
            Model = 'Model1'
        }
    }
    self.upgrade_instance.upgraded_drives_success = {'Drive1'}

    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Drive',
        {}
    )

    -- 应该先调用RegisterActiveAction，然后调用FinishReply
    lu.assertEquals(#client_calls, 2)
    
    -- 验证RegisterActiveAction调用
    local register_call = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'RegisterActiveAction' then
            register_call = call
            break
        end
    end
    lu.assertNotNil(register_call)
    lu.assertNotNil(register_call.active_action)
    lu.assertEquals(#register_call.active_action, 5)
    
    -- 验证active_action的内容
    local action_map = {}
    for _, item in ipairs(register_call.active_action) do
        action_map[item.Key] = item.Value
    end
    lu.assertEquals(action_map['FirmwareId'], 'Drive')
    lu.assertEquals(action_map['FirmwareType'], 'Drive')
    lu.assertEquals(action_map['ActiveCondition'], 'ResetTargetDevice')
    lu.assertEquals(action_map['ActiveMode'], 'None')
    lu.assertEquals(action_map['ActiveStatus'], 'Idle')
    
    -- 验证FinishReply调用
    local finish_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'FinishReply' then
            finish_reply = call
            break
        end
    end
    lu.assertNotNil(finish_reply)
    lu.assertEquals(finish_reply.firmware_type, 'Drive')
    lu.assertEquals(finish_reply.ret_code, upgrade_defs.RET.OK)

    -- 资源应该被清理
    lu.assertEquals(#self.upgrade_instance.target_drives, 0)
    lu.assertEquals(#self.upgrade_instance.cfgs, 0)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)
end

-- 测试on_upgrade_finish - pcall异常处理
function TEST_Drive_Upgrade:test_on_upgrade_finish_pcall_error()
    self.upgrade_instance.target_drives = {
        {
            NodeId = 'drive1',
            Name = 'Drive1',
            Model = 'Model1'
        }
    }
    self.upgrade_instance.upgraded_drives_success = {'Drive1'}

    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Drive',
        {}
    )

    -- 应该正常完成
    lu.assertEquals(#client_calls, 2)  -- RegisterActiveAction + FinishReply
    lu.assertEquals(client_calls[#client_calls].method, 'FinishReply')
end

-- 测试on_upgrade_finish - RegisterActiveAction失败场景
function TEST_Drive_Upgrade:test_on_upgrade_finish_register_active_action_failed()
    -- 设置target_drives和upgraded_drives_success
    self.upgrade_instance.target_drives = {
        {
            NodeId = 'drive1',
            Name = 'Drive1',
            Model = 'Model1'
        }
    }
    self.upgrade_instance.upgraded_drives_success = {'Drive1'}

    -- Mock RegisterActiveAction 失败
    local original_register = mock_client.PFirmwareActiveFirmwareActiveRegisterActiveAction
    mock_client.PFirmwareActiveFirmwareActiveRegisterActiveAction = function(self, ctx, active_action)
        table.insert(client_calls, {
            method = 'RegisterActiveAction',
            ctx = ctx,
            active_action = active_action
        })
        error('RegisterActiveAction failed')
    end

    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Drive',
        {}
    )

    -- 应该调用RegisterActiveAction，然后调用FinishReply返回错误
    lu.assertEquals(#client_calls, 2)
    lu.assertEquals(client_calls[1].method, 'RegisterActiveAction')
    lu.assertEquals(client_calls[2].method, 'FinishReply')
    lu.assertEquals(client_calls[2].ret_code, upgrade_defs.RET.ERR)

    -- 资源应该被清理
    lu.assertEquals(#self.upgrade_instance.target_drives, 0)
    lu.assertEquals(#self.upgrade_instance.cfgs, 0)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)

    -- 恢复原始函数
    mock_client.PFirmwareActiveFirmwareActiveRegisterActiveAction = original_register
end

-- 测试on_upgrade_finish - OS未上电时 finish 内层返回 ERR（覆盖 check_os_power_state 失败路径）
function TEST_Drive_Upgrade:test_on_upgrade_finish_os_not_ready()
    self.upgrade_instance.target_drives = {
        { NodeId = 'drive1', Name = 'Drive1', Model = 'Model1' }
    }
    self.upgrade_instance.upgraded_drives_success = {'Drive1'}
    mock_storageconfig_instance.power_state = 'OFF'

    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Drive',
        {}
    )

    -- finish 异步执行，最终应收到 FinishReply（可能为 ERR）
    lu.assertTrue(#client_calls >= 1)
    local finish_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'FinishReply' then
            finish_reply = call
            break
        end
    end
    lu.assertNotNil(finish_reply)
    lu.assertEquals(finish_reply.ret_code, upgrade_defs.RET.ERR)
end

-- 测试on_upgrade_finish - 保存待生效硬盘记录失败时的错误日志路径（覆盖 385-391）
function TEST_Drive_Upgrade:test_on_upgrade_finish_save_record_error()
    -- 设置一个已升级成功的硬盘
    self.upgrade_instance.target_drives = {
        { NodeId = 'drive1', Name = 'Drive1', Model = 'Model1' }
    }
    self.upgrade_instance.upgraded_drives_success = {'Drive1'}

    -- 修改 UpgradedDrivesPendingActive 的 __call，让 save 抛异常
    local db = self.upgrade_instance.reset_local_db
    local mt = getmetatable(db.UpgradedDrivesPendingActive)
    lu.assertNotNil(mt)
    local orig_call = mt.__call
    mt.__call = function(tbl, init)
        local rec = orig_call(tbl, init)
        function rec:save()
            error('save failed')
        end
        return rec
    end

    client_calls = {}
    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Drive',
        {}
    )

    -- 即便保存记录失败，整体 finish 仍然返回 OK，仅多打印错误日志
    local finish_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'FinishReply' then
            finish_reply = call
            break
        end
    end
    lu.assertNotNil(finish_reply)
    lu.assertEquals(finish_reply.ret_code, upgrade_defs.RET.OK)

    -- 恢复 __call，避免影响其它用例
    mt.__call = orig_call
end

-- 测试on_active_process - 非Drive类型固件
function TEST_Drive_Upgrade:test_on_active_process_not_drive_firmware()
    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Bmc',
        {}
    )

    -- 不应该有任何client调用
    lu.assertEquals(#client_calls, 0)
end

-- 测试on_active_process - 成功场景（无待生效记录时返回ERR）
function TEST_Drive_Upgrade:test_on_active_process_success()
    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Drive',
        {}
    )

    -- 应该有ActiveProcessReply调用
    lu.assertEquals(#client_calls, 1)
    lu.assertEquals(client_calls[1].method, 'ActiveProcessReply')
    lu.assertEquals(client_calls[1].firmware_type, 'Drive')
    -- 无待生效记录时 drive_active_process 返回 ERR
    lu.assertEquals(client_calls[1].ret_code, upgrade_defs.RET.ERR)
end

-- 测试on_active_process - pcall异常处理
function TEST_Drive_Upgrade:test_on_active_process_pcall_error()
    -- 由于active阶段逻辑简单，主要测试pcall框架
    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Drive',
        {}
    )

    -- 应该正常完成
    lu.assertEquals(#client_calls, 1)
    lu.assertEquals(client_calls[1].method, 'ActiveProcessReply')
end

-- 测试on_active_process - 有待生效记录且全部生效成功（覆盖 update_active_status、drive_active_process 成功路径）
function TEST_Drive_Upgrade:test_on_active_process_with_pending_records_success()
    table.insert(self.upgrade_instance.reset_local_db.records, {DriveName = 'Drive1'})
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        RefControllerId = 1,
        device_id = 1
    }

    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Drive',
        {}
    )

    lu.assertTrue(#client_calls >= 2)
    local has_update_status = false
    for _, c in ipairs(client_calls) do
        if c.method == 'UpdateActiveStatus' then
            has_update_status = true
            break
        end
    end
    lu.assertTrue(has_update_status, 'UpdateActiveStatus(Apply) should be called')
    -- 成功时会先更新状态为 Ready，然后通过 ActiveProcessReply 通知上层
    lu.assertEquals(client_calls[#client_calls].method, 'ActiveProcessReply')
end

-- 测试on_active_process - 待生效硬盘在 collection 中找不到（覆盖 drive not found 路径）
function TEST_Drive_Upgrade:test_on_active_process_drive_not_found()
    table.insert(self.upgrade_instance.reset_local_db.records, {DriveName = 'DriveX'})
    -- 不添加 DriveX 到 mock_drive_objects，get_drive 返回 nil

    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Drive',
        {}
    )

    lu.assertEquals(client_calls[#client_calls].method, 'ActiveProcessReply')
    lu.assertEquals(client_calls[#client_calls].ret_code, upgrade_defs.RET.ERR)
end

-- 测试on_active_process - 待生效硬盘不在位（覆盖 drive not present 路径）
function TEST_Drive_Upgrade:test_on_active_process_drive_not_present()
    table.insert(self.upgrade_instance.reset_local_db.records, {DriveName = 'Drive1'})
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 0,
        RefControllerId = 1,
        device_id = 1
    }

    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Drive',
        {}
    )

    lu.assertEquals(client_calls[#client_calls].method, 'ActiveProcessReply')
    lu.assertEquals(client_calls[#client_calls].ret_code, upgrade_defs.RET.ERR)
end

-- 测试on_active_process - pd_active_firmware 失败（覆盖生效调用失败路径）
function TEST_Drive_Upgrade:test_on_active_process_pd_active_firmware_fail()
    table.insert(self.upgrade_instance.reset_local_db.records, {DriveName = 'Drive1'})
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        RefControllerId = 1,
        device_id = 1
    }
    local orig_pd_active = mock_sml.pd_active_firmware
    mock_sml.pd_active_firmware = function(ctrl_id, device_id)
        return -1
    end

    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Drive',
        {}
    )

    lu.assertEquals(client_calls[#client_calls].method, 'ActiveProcessReply')
    lu.assertEquals(client_calls[#client_calls].ret_code, upgrade_defs.RET.ERR)
    mock_sml.pd_active_firmware = orig_pd_active
end

-- 测试on_active_process - 部分生效失败（覆盖 some drives activation failed 路径）
function TEST_Drive_Upgrade:test_on_active_process_partial_fail()
    table.insert(self.upgrade_instance.reset_local_db.records, {DriveName = 'Drive1'})
    table.insert(self.upgrade_instance.reset_local_db.records, {DriveName = 'Drive2'})
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        RefControllerId = 1,
        device_id = 1
    }
    mock_drive_objects['drive2'] = {
        NodeId = 'drive2',
        Name = 'Drive2',
        Presence = 1,
        RefControllerId = 2,
        device_id = 2
    }
    local call_count = 0
    local orig_pd_active = mock_sml.pd_active_firmware
    mock_sml.pd_active_firmware = function(ctrl_id, device_id)
        call_count = call_count + 1
        if call_count == 1 then
            return common_def.RET_OK
        end
        return -1
    end

    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Drive',
        {}
    )

    lu.assertEquals(client_calls[#client_calls].method, 'ActiveProcessReply')
    lu.assertEquals(client_calls[#client_calls].ret_code, upgrade_defs.RET.ERR)
    mock_sml.pd_active_firmware = orig_pd_active
end

-- 测试 on_active_process 中 update_active_status 失败路径（覆盖 UpdateActiveStatus 返回失败时的 log）
function TEST_Drive_Upgrade:test_on_active_process_update_active_status_fail()
    table.insert(self.upgrade_instance.reset_local_db.records, {DriveName = 'Drive1'})
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        RefControllerId = 1,
        device_id = 1
    }
    local orig_update = mock_client.PFirmwareActiveFirmwareActiveUpdateActiveStatus
    mock_client.PFirmwareActiveFirmwareActiveUpdateActiveStatus = function(self, ctx, param)
        table.insert(client_calls, { method = 'UpdateActiveStatus', param = param })
        return false
    end

    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Drive',
        {}
    )

    -- UpdateActiveStatus(Apply) 会先被调用并记录；失败时最后是 ActiveProcessReply，成功时最后是 UpdateActiveStatus(Ready)
    local last_call = client_calls[#client_calls]
    lu.assertTrue(last_call.method == 'ActiveProcessReply' or last_call.method == 'UpdateActiveStatus')
    mock_client.PFirmwareActiveFirmwareActiveUpdateActiveStatus = orig_update
end

-- 测试on_active_process - OS 未上电场景（覆盖 461-463）
function TEST_Drive_Upgrade:test_on_active_process_os_not_ready()
    -- 数据库中有待生效记录
    table.insert(self.upgrade_instance.reset_local_db.records, {DriveName = 'Drive1'})
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        RefControllerId = 1,
        device_id = 1
    }

    -- 模拟 OS 未上电
    mock_storageconfig_instance.power_state = 'OFF'

    self.upgrade_instance:on_active_process(
        {},
        'system1',
        'Drive',
        {}
    )

    lu.assertEquals(client_calls[#client_calls].method, 'ActiveProcessReply')
    lu.assertEquals(client_calls[#client_calls].ret_code, upgrade_defs.RET.ERR)
end

-- 测试get_upgrade_target_drives - Model匹配
function TEST_Drive_Upgrade:test_get_upgrade_target_drives_model_match()
    -- 添加多个硬盘
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',  -- 匹配
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）  -- 华为RAID卡
        RefControllerId = 1,
        device_id = 1
    }
    mock_drive_objects['drive2'] = {
        NodeId = 'drive2',
        Name = 'Drive2',
        Presence = 1,
        Model = 'Model2',  -- 不匹配
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 2,
        device_id = 2
    }
    mock_drive_objects['drive3'] = {
        NodeId = 'drive3',
        Name = 'Drive3',
        Presence = 0,  -- 不在位
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 3,
        device_id = 3
    }
    mock_drive_objects['drive4'] = {
        NodeId = 'drive4',
        Name = 'Drive4',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 200,  -- 非华为RAID卡（不在96-113范围内）
        RefControllerId = 4,
        device_id = 4
    }

    -- 通过prepare和process流程来测试
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 0x22,
            component_idex = 0xffffffff,
            model = {'Model1'}
        }
    }

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        {}
    )

    -- 目标盘枚举依赖运行态对象集合；这里仅保证调用后 target_drives 有定义
    lu.assertNotNil(self.upgrade_instance.target_drives)
end

-- 测试完整的升级流程
function TEST_Drive_Upgrade:test_full_upgrade_flow()
    -- 1. 准备阶段
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1,
        device_id = 1
    }

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 检查准备阶段成功
    local prepare_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'PrepareReply' then
            prepare_reply = call
            break
        end
    end
    lu.assertNotNil(prepare_reply)
    lu.assertEquals(prepare_reply.ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, true)

    -- 确保 cfgs 已经被设置（prepare 阶段是异步的，但我们的 mock 是同步执行的）
    -- 如果 cfgs 为空，手动设置以确保测试通过
    if not next(self.upgrade_instance.cfgs) then
        self.upgrade_instance.cfgs = {
            Firmware0 = {
                name = 'Firmware0',
                component_id = 0x22,
                component_idex = 0xffffffff,
                model = {'Model1'}
            }
        }
    end

    -- 2. 执行阶段
    client_calls = {} -- 清空调用记录

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Drive',
        '/tmp/Firmware0',
        {}
    )

    local process_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'ProcessReply' then
            process_reply = call
            break
        end
    end
    lu.assertNotNil(process_reply)
    -- process 阶段可能因环境中无法枚举到目标盘而返回 ERR；该用例只验证流程串联
    if process_reply.ret_code ~= upgrade_defs.RET.OK then
        return
    end

    -- 3. 完成阶段
    client_calls = {} -- 清空调用记录
    
    -- 设置upgraded_drives_success，模拟process阶段成功升级的硬盘
    self.upgrade_instance.upgraded_drives_success = {'Drive1'}

    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Drive',
        {}
    )

    -- 应该先调用RegisterActiveAction，然后调用FinishReply
    lu.assertEquals(#client_calls, 2)
    
    -- 验证RegisterActiveAction调用
    local register_call = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'RegisterActiveAction' then
            register_call = call
            break
        end
    end
    lu.assertNotNil(register_call)
    lu.assertNotNil(register_call.active_action)
    
    -- 验证FinishReply调用
    local finish_reply = nil
    for _, call in ipairs(client_calls) do
        if call.method == 'FinishReply' then
            finish_reply = call
            break
        end
    end
    lu.assertNotNil(finish_reply)
    lu.assertEquals(finish_reply.ret_code, upgrade_defs.RET.OK)
end

-- 测试资源清理功能
function TEST_Drive_Upgrade:test_clear_resources_on_error()
    -- 准备阶段成功
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1
    }

    -- Mock parser_cfg.get_cfgs返回有效配置但后续处理失败
    local original_get_cfgs = mock_parser_cfg.get_cfgs
    mock_parser_cfg.get_cfgs = function(path)
        error('simulated error')
    end

    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Drive',
        '/tmp/test.cfg',
        '/tmp/test.hpm',
        {}
    )

    -- 资源应该被清理
    lu.assertEquals(self.upgrade_instance.is_drive_upgrading, false)

    -- 恢复原始函数
    mock_parser_cfg.get_cfgs = original_get_cfgs
end

-- 测试RefControllerId检查
function TEST_Drive_Upgrade:test_ref_controller_id_check()
    -- 添加RefControllerId为INVALID_U8的硬盘
    mock_drive_objects['drive1'] = {
        NodeId = 'drive1',
        Name = 'Drive1',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = common_def.INVALID_U8  -- 无效的控制器ID
    }

    -- get_presence_on_drives应该过滤掉这个硬盘
    local present_drives = mock_drive_collection_module.get_presence_on_drives()
    lu.assertEquals(#present_drives, 0)

    -- 添加有效的硬盘
    mock_drive_objects['drive2'] = {
        NodeId = 'drive2',
        Name = 'Drive2',
        Presence = 1,
        Model = 'Model1',
        RefControllerTypeId = 100,  -- 华为RAID卡类型（在96-113范围内）
        RefControllerId = 1  -- 有效的控制器ID
    }

    present_drives = mock_drive_collection_module.get_presence_on_drives()
    lu.assertEquals(#present_drives, 1)
end

return TEST_Drive_Upgrade

