-- 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 may 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

-- 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,
    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,
    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
}
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 = 1,
                component_idex = 2
            }
        }
    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'

-- Mock controller_firmware
local mock_controller_firmware_objects = {}
local mock_controller_firmware_mgmt = {}

-- 根据 component_id/component_idex 从 mock_controller_firmware_objects 中筛选可升级控制器
function mock_controller_firmware_mgmt:find_upgradable_raid_firmware_obj(component_id, component_idex)
    local res = {}
    for _, obj in pairs(mock_controller_firmware_objects) do
        -- 兼容两种字段命名风格
        local cid = obj.ComponentID or obj.component_id
        local cid_ex = obj.ComponentIDEx or obj.component_idex or obj.component_id_ex
        local ref = obj.ref_controller or obj.RefController

        if cid == component_id and cid_ex == component_idex and ref and ref.OOBSupport == 1 then
            table.insert(res, {
                ComponentID = cid,
                ComponentIDEx = cid_ex,
                Position = obj.Position,
                ref_controller = ref
            })
        end
    end
    return res
end

-- 是否存在支持 OOB 的可升级 RAID 控制器
function mock_controller_firmware_mgmt:has_valid_upgradable_raid()
    for _, obj in pairs(mock_controller_firmware_objects) do
        local ref = obj.ref_controller or obj.RefController
        if ref and ref.OOBSupport == 1 then
            return true
        end
    end
    return false
end

-- 根据配置匹配控制器固件版本
function mock_controller_firmware_mgmt:get_cfg_match_controller_version(component_id, component_idex)
    for _, obj in pairs(mock_controller_firmware_objects) do
        local cid = obj.ComponentID or obj.component_id
        local cid_ex = obj.ComponentIDEx or obj.component_idex or obj.component_id_ex
        local ref = obj.ref_controller or obj.RefController

        if cid == component_id and cid_ex == component_idex and ref and ref.FirmwareVersion then
            return ref.FirmwareVersion
        end
    end
    return ""
end

local mock_controller_firmware = {
    collection = {
        objects = mock_controller_firmware_objects,
        count = 0,
        fold = function(self, func, acc)
            for key, obj in pairs(self.objects) do
                local result = func(acc, obj, key)
                if result ~= nil then
                    return result
                end
            end
            return acc
        end
    },
    get_instance = function()
        -- controller_upgrade.lua 中通过 firmware_objs.get_instance()
        -- 获取到的是管理对象，这里返回我们自定义的 mock_controller_firmware_mgmt
        return mock_controller_firmware_mgmt
    end
}
package.loaded['controller_firmware.controller_firmware_object'] = mock_controller_firmware

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

local mock_raid_upgrade_intf = {
    upgrade_ctrl_process = function(dir, ctrl_id)
        return upgrade_defs.RET.OK
    end,
    get_upgrade_percent = function()
        return upgrade_defs.RET.OK
    end,
    set_upgrade_percent_zero = function()
        return upgrade_defs.RET.OK
    end
}
package.loaded['raid_upgrade_intf'] = mock_raid_upgrade_intf

local mock_sml = {
    upgrade_ctrl_process = function()
        return upgrade_defs.RET.OK
    end
}
package.loaded['sml'] = mock_sml

-- Now require the module under test
local controller_upgrade = require 'upgrade.controller_upgrade'

TEST_Controller_Upgrade = {}

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

    -- Reset mock data - 清空并重新设置对象表
    for k in pairs(mock_controller_firmware_objects) do
        mock_controller_firmware_objects[k] = nil
    end
    mock_controller_firmware.collection.objects = mock_controller_firmware_objects
    mock_controller_firmware.collection.count = 0

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

    -- upgrade_instance就相当于controller_upgrade模块的实例
    self.upgrade_instance = controller_upgrade.get_instance()
    self.upgrade_instance.cfgs = {}
    self.upgrade_instance.upgrade_list = {}
    self.upgrade_instance.before_upgrade_version = ''
    self.upgrade_instance.is_controller_upgrading = false
end

function TEST_Controller_Upgrade:tearDown()
    client_calls = {}
end

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

    lu.assertNotNil(instance.cfgs)
    lu.assertEquals(#instance.cfgs, 0)
    lu.assertNotNil(instance.upgrade_list)
    lu.assertEquals(#instance.upgrade_list, 0)
    lu.assertEquals(instance.before_upgrade_version, '')
    lu.assertEquals(instance.is_controller_upgrading, false)
end

-- 测试on_upgrade_prepare - 非RAID类型固件
function TEST_Controller_Upgrade:test_on_upgrade_prepare_not_raid_firmware()
    -- 设置有效的前置条件
    mock_controller_firmware.collection.count = 1
    mock_controller_firmware_objects['test'] = {
        component_id = 1,
        component_id_ex = 2,
        RefController = {
            Id = 'ctrl1',
            OOBSupport = 1,
            FirmwareVersion = '1.0.0'
        }
    }

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

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

-- 测试on_upgrade_prepare - SMBIOS状态不正确
function TEST_Controller_Upgrade:test_on_upgrade_prepare_smbios_not_ready()
    mock_storageconfig_instance.smbios_status = 0 -- 不是SMBIOS_WRITE_FINISH
    mock_controller_firmware.collection.count = 1

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

    -- 应该返回错误
    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_Controller_Upgrade:test_on_upgrade_prepare_power_not_on()
    mock_storageconfig_instance.power_state = 'OFF'
    mock_controller_firmware.collection.count = 1

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

    -- 应该返回错误
    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 - controller firmware对象不存在
function TEST_Controller_Upgrade:test_on_upgrade_prepare_no_controller_firmware()
    mock_controller_firmware.collection.count = 0
    mock_controller_firmware.collection.objects = nil

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

    -- 应该返回错误
    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 - 没有支持OOB的控制器
function TEST_Controller_Upgrade:test_on_upgrade_prepare_no_oob_support()
    mock_controller_firmware.collection.count = 1
    mock_controller_firmware_objects['test'] = {
        component_id = 1,
        component_id_ex = 2,
        RefController = {
            Id = 'ctrl1',
            OOBSupport = 0, -- 不支持OOB
            FirmwareVersion = '1.0.0'
        }
    }

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

    -- 应该返回错误
    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_Controller_Upgrade:test_on_upgrade_prepare_already_upgrading()
    mock_controller_firmware.collection.count = 1
    mock_controller_firmware_objects['test'] = {
        component_id = 1,
        component_id_ex = 2,
        RefController = {
            Id = 'ctrl1',
            OOBSupport = 1, -- 支持OOB
            FirmwareVersion = '1.0.0'
        }
    }
    self.upgrade_instance.is_controller_upgrading = true

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

    -- 应该返回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 - FileChown失败
function TEST_Controller_Upgrade:test_on_upgrade_prepare_chown_failed()
    mock_controller_firmware.collection.count = 1
    mock_controller_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        ref_controller = {
            Id = 'ctrl1',
            OOBSupport = 1,
            FirmwareVersion = '1.0.0'
        }
    }

    -- 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_prepare(
        {},
        'system1',
        'Raid',
        '/tmp/test.cfg',
        nil,
        {}
    )

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

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

-- 测试on_upgrade_prepare - 成功场景
function TEST_Controller_Upgrade:test_on_upgrade_prepare_success()
    mock_controller_firmware.collection.count = 1
    mock_controller_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        ref_controller = {
            Id = 'ctrl1',
            OOBSupport = 1,
            FirmwareVersion = '1.0.0'
        }
    }

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

    -- 应该有FileChown和PrepareReply调用
    lu.assertTrue(#client_calls >= 2)
    lu.assertEquals(client_calls[1].method, '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.OK)
end

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

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

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

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Raid',
        '/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)
end

-- 测试on_upgrade_process - FileChown失败
function TEST_Controller_Upgrade:test_on_upgrade_process_chown_failed()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 1,
            component_idex = 2
        }
    }

    -- 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',
        'Raid',
        '/tmp/firmware.bin',
        {}
    )

    -- 应该返回错误
    lu.assertTrue(#client_calls >= 2)

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

-- 测试on_upgrade_process - 没有需要升级的固件
function TEST_Controller_Upgrade:test_on_upgrade_process_no_upgrade_list()
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 999, -- 不匹配的ID
            component_idex = 999
        }
    }

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

    -- 应该返回错误
    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)
end

-- 测试on_upgrade_process - 成功场景
function TEST_Controller_Upgrade:test_on_upgrade_process_success()
    -- 先设置准备阶段的数据
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 1,
            component_idex = 2
        }
    }

    -- 添加匹配的controller firmware
    mock_controller_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        ref_controller = {
            Id = 'ctrl1',
            OOBSupport = 1,
            FirmwareVersion = '1.0.0'
        }
    }

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

    -- 应该有多个调用：FileChown, UpdateUpgradeStatus (多次), ProcessReply
    lu.assertTrue(#client_calls >= 2)

    -- 检查是否有UpdateUpgradeStatus调用
    local has_status_update = false
    for _, call in ipairs(client_calls) do
        if call.method == 'UpdateUpgradeStatus' then
            has_status_update = true
            break
        end
    end
    lu.assertTrue(has_status_update)

    -- 检查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.OK)
end

-- 测试on_upgrade_finish - 非RAID类型固件
function TEST_Controller_Upgrade:test_on_upgrade_finish_not_raid_firmware()
    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Bmc',
        {}
    )

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

-- 测试on_upgrade_finish - 成功场景
function TEST_Controller_Upgrade:test_on_upgrade_finish_success()
    self.upgrade_instance:on_upgrade_finish(
        {},
        'system1',
        'Raid',
        {}
    )

    -- 应该有FinishReply调用
    lu.assertEquals(#client_calls, 1)
    lu.assertEquals(client_calls[1].method, 'FinishReply')
    lu.assertEquals(client_calls[1].firmware_type, 'Raid')
    lu.assertEquals(client_calls[1].ret_code, upgrade_defs.RET.OK)
end

-- 测试check_upgrade_preconditions - 检查OOBSupport
function TEST_Controller_Upgrade:test_check_oob_support_in_get_upgrade_list()
    -- 添加两个controller firmware，一个支持OOB，一个不支持
    local cfg = {
        component_id = 1,
        component_idex = 2
    }

    mock_controller_firmware_objects['test1'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        ref_controller = {
            Id = 'ctrl1',
            OOBSupport = 1, -- 支持OOB
            FirmwareVersion = '1.0.0'
        }
    }

    mock_controller_firmware_objects['test2'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        ref_controller = {
            Id = 'ctrl2',
            OOBSupport = 0, -- 不支持OOB
            FirmwareVersion = '1.0.0'
        }
    }

    -- 准备升级
    self.upgrade_instance.cfgs = {
        Firmware0 = cfg
    }

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Raid',
        '/tmp/Firmware0', -- 这里的Firmware0要和cfgs中的Firmware0一致
        {}
    )

    -- 升级列表应该只包含支持OOB的控制器
    lu.assertEquals(#self.upgrade_instance.upgrade_list, 1)
    lu.assertEquals(self.upgrade_instance.upgrade_list[1].ref_controller.Id, 'ctrl1')
end

-- 测试完整的升级流程
function TEST_Controller_Upgrade:test_full_upgrade_flow()
    -- 1. 准备阶段
    mock_controller_firmware.collection.count = 1
    mock_controller_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        ref_controller = {
            Id = 'ctrl1',
            OOBSupport = 1,
            FirmwareVersion = '1.0.0'
        }
    }

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

    -- 检查准备阶段成功
    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_controller_upgrading, true)

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

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Raid',
        '/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.OK)

    -- 3. 完成阶段
    client_calls = {} -- 清空调用记录

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

    lu.assertEquals(#client_calls, 1)
    lu.assertEquals(client_calls[1].method, 'FinishReply')
    lu.assertEquals(client_calls[1].ret_code, upgrade_defs.RET.OK)
end

-- 测试混合OOB支持场景
function TEST_Controller_Upgrade:test_mixed_oob_support()
    -- 添加多个控制器，有的支持OOB，有的不支持
    mock_controller_firmware_objects['test1'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        ref_controller = {
            Id = 'ctrl1',
            OOBSupport = 1, -- 支持OOB
            FirmwareVersion = '1.0.0'
        }
    }

    mock_controller_firmware_objects['test2'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        ref_controller = {
            Id = 'ctrl2',
            OOBSupport = 0, -- 不支持OOB
            FirmwareVersion = '1.0.0'
        }
    }

    mock_controller_firmware.collection.count = 2

    -- 准备阶段应该成功（因为有支持OOB的控制器）
    self.upgrade_instance:on_upgrade_prepare(
        {},
        'system1',
        'Raid',
        '/tmp/test.cfg',
        nil,
        {}
    )

    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

-- 测试准备阶段的pcall异常处理
function TEST_Controller_Upgrade:test_on_upgrade_prepare_pcall_error()
    mock_controller_firmware.collection.count = 1
    mock_controller_firmware_objects['test'] = {
        component_id = 1,
        component_id_ex = 2,
        RefController = {
            Id = 'ctrl1',
            OOBSupport = 1,
            FirmwareVersion = '1.0.0'
        }
    }

    -- 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',
        'Raid',
        '/tmp/test.cfg',
        nil,
        {}
    )

    -- 应该捕获异常并返回错误
    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)

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

-- 测试执行阶段的pcall异常处理
function TEST_Controller_Upgrade:test_on_upgrade_process_pcall_error()
    -- 先设置准备阶段的数据
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 1,
            component_idex = 2
        }
    }

    -- 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',
        'Raid',
        '/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)

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

-- 测试准备阶段找不到匹配的固件版本
function TEST_Controller_Upgrade:test_on_upgrade_prepare_no_version_found()
    mock_controller_firmware.collection.count = 1
    mock_controller_firmware_objects['test'] = {
        ComponentID = 999, -- 不匹配的component_id
        ComponentIDEx = 999,
        ref_controller = {
            Id = 'ctrl1',
            OOBSupport = 1,
            FirmwareVersion = '1.0.0'
        }
    }

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

    -- 准备阶段应该成功，但版本号为空字符串
    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(prepare_reply.version, '')
end

-- 测试升级列表为空的情况
function TEST_Controller_Upgrade:test_upgrade_list_empty_components_mismatch()
    -- 准备阶段设置不匹配的组件
    self.upgrade_instance.cfgs = {
        Firmware0 = {
            name = 'Firmware0',
            component_id = 999,  -- 不匹配
            component_idex = 999
        }
    }
    self.upgrade_instance.is_controller_upgrading = true

    -- 添加控制器但组件ID不匹配
    mock_controller_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        ref_controller = {
            Id = 'ctrl1',
            OOBSupport = 1,
            FirmwareVersion = '1.0.0'
        }
    }

    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Raid',
        '/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)
end

-- 测试资源清理功能
function TEST_Controller_Upgrade:test_clear_resources_on_error()
    -- 准备阶段成功
    mock_controller_firmware.collection.count = 1
    mock_controller_firmware_objects['test'] = {
        component_id = 1,
        component_id_ex = 2,
        RefController = {
            Id = 'ctrl1',
            OOBSupport = 1,
            FirmwareVersion = '1.0.0'
        }
    }

    -- 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',
        'Raid',
        '/tmp/test.cfg',
        nil,
        {}
    )

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

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

-- 测试进程阶段清理资源
function TEST_Controller_Upgrade:test_clear_resources_on_process_error()
    -- 设置初始状态
    self.upgrade_instance.cfgs = {}
    self.upgrade_instance.is_controller_upgrading = true
    self.upgrade_instance.before_upgrade_version = 'test_version'
    self.upgrade_instance.upgrade_list = {'item1', 'item2'}

    -- 执行阶段（没有cfgs会失败）
    self.upgrade_instance:on_upgrade_process(
        {},
        'system1',
        'Raid',
        '/tmp/firmware.bin',
        {}
    )

    -- 资源应该被清理
    lu.assertEquals(self.upgrade_instance.is_controller_upgrading, false)
    lu.assertEquals(self.upgrade_instance.before_upgrade_version, '')
    lu.assertEquals(#self.upgrade_instance.upgrade_list, 0)
end

-- 测试信号订阅回调
function TEST_Controller_Upgrade:test_signal_callbacks_registered()
    local instance = controller_upgrade.get_instance()
    instance:init()

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

return TEST_Controller_Upgrade