-- 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 对象（但不设置到 package.loaded，避免文件加载时污染全局环境）
local fructl_calls = {}
local mock_fructl = {
    get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        return 'PowerOn'
    end,
    set_poweron_lock_until_success = function(bus, sys_id, state, time, reason)
        table.insert(fructl_calls, {
            method = 'set_poweron_lock_until_success',
            bus = bus,
            sys_id = sys_id,
            state = state,
            time = time,
            reason = reason
        })
        return true
    end
}

local parser_cfg_calls = {}
local mock_parser_cfg = {
    get_cfgs = function(path)
        table.insert(parser_cfg_calls, {method = 'get_cfgs', path = path})
        if path == '/tmp/invalid.cfg' then
            return {}
        end
        return {
            Firmware1 = {
                name = 'Firmware1',
                component_id = 1,
                component_idex = 2
            }
        }
    end
}

local vos_calls = {}
local mock_vos = {
    get_file_accessible = function(path)
        table.insert(vos_calls, {method = 'get_file_accessible', path = path})
        if path == '/tmp/not_exist.hpm' then
            return false
        end
        return true
    end
}

local utils_calls = {}
local mock_utils = {
    copy_file = function(src, dst)
        table.insert(utils_calls, {method = 'copy_file', src = src, dst = dst})
        return true
    end
}

local mock_utils_core = {
    stat = function(path)
        return {st_size = 1024}
    end,
    dir = function(path)
        return {'file1.tar', 'file2.tar'}
    end
}

-- mock_context 不再在文件顶层定义，改为在 setUp 中动态创建

local client_calls = {}
local mock_client = {
    FirmwareActiveFirmwareActiveRegisterActiveAction = function(self, ctx, list)
        table.insert(client_calls, {
            method = 'RegisterActiveAction',
            list = list
        })
        return true
    end
}

local mock_network_adapter_firmware_objects = {}
local mock_network_adapter_firmware = {
    collection = {
        objects = mock_network_adapter_firmware_objects,
        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
    }
}

local upgrade_flash_calls = {}
local mock_upgrade_flash = {
    set_network_adapter_firmware_route = function(obj, route)
        table.insert(upgrade_flash_calls, {
            method = 'set_route',
            obj = obj,
            route = route
        })
        return true
    end,
    network_adapter_upgrade_flash = function(self, file_path)
        table.insert(upgrade_flash_calls, {
            method = 'upgrade_flash',
            file_path = file_path
        })
        return 0 -- upgrade_defs.RET.OK
    end
}

-- 延迟加载被测试模块，在 setUp 中加载
local upgrade_defs
local upgrade_process

TEST_Upgrade_Process = {}

function TEST_Upgrade_Process:setUp()
    -- 设置 package.loaded 中的 mock（只在测试用例执行时设置，避免文件加载时污染全局环境）
    package.loaded['skynet'] = {
        sleep = function(ms) end,
        fork = function(func) func() end,
        fork_once = function(func) func() end
    }
    package.loaded['mc.logging'] = {
        notice = function() end,
        error = function() end,
        info = function() end,
        maintenance = function() end,
        running = function() end,
        RLOG_INFO = 0,
        RLOG_ERROR = 1,
        MLOG_INFO = 0,
        FC__PUBLIC_OK = 0
    }
    package.loaded['infrastructure.fructl'] = mock_fructl
    package.loaded['upgrade.parser_cfg'] = mock_parser_cfg
    
    -- 对于 utils.vos，先加载真实模块，然后只覆盖需要 mock 的函数，保留其他函数（如 vos_tick_get）
    package.loaded['utils.vos'] = nil
    local real_vos = require 'utils.vos'
    local vos_mock = {}
    -- 复制真实模块的所有函数
    for k, v in pairs(real_vos) do
        vos_mock[k] = v
    end
    -- 覆盖需要 mock 的函数
    vos_mock.get_file_accessible = function(path)
        table.insert(vos_calls, {method = 'get_file_accessible', path = path})
        if path == '/tmp/not_exist.hpm' then
            return false
        end
        return true
    end
    package.loaded['utils.vos'] = vos_mock
    
    -- 对于 mc.context，先加载真实模块，然后只覆盖需要 mock 的函数，保留其他函数（如 get_context）
    package.loaded['mc.context'] = nil
    local real_context = require 'mc.context'
    local context_mock = {}
    -- 复制真实模块的所有函数
    for k, v in pairs(real_context) do
        context_mock[k] = v
    end
    -- 覆盖需要 mock 的函数
    context_mock.new = function()
        return {}
    end
    package.loaded['mc.context'] = context_mock
    
    package.loaded['mc.utils'] = mock_utils
    package.loaded['utils.core'] = mock_utils_core
    package.loaded['network_adapter.client'] = mock_client
    package.loaded['upgrade.network_adapter_firmware_object'] = mock_network_adapter_firmware
    package.loaded['upgrade.upgrade_flash'] = mock_upgrade_flash
    
    -- 清除被测试模块的缓存，确保使用最新的 mock
    package.loaded['upgrade.upgrade_process'] = nil
    package.loaded['upgrade.upgrade_defs'] = nil
    package.loaded['upgrade.network_adapter_upgrade'] = nil
    
    -- 加载被测试模块（使用最新的 mock）
    upgrade_defs = require 'upgrade.upgrade_defs'
    upgrade_process = require 'upgrade.upgrade_process'
    
    -- Reset call records
    fructl_calls = {}
    parser_cfg_calls = {}
    vos_calls = {}
    utils_calls = {}
    client_calls = {}
    upgrade_flash_calls = {}
    
    -- Reset network adapter firmware objects
    for k in pairs(mock_network_adapter_firmware_objects) do
        mock_network_adapter_firmware_objects[k] = nil
    end
    
    -- Reset mock functions
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        return 'PowerOn'
    end
    
    -- 更新 package.loaded['utils.vos'] 中的 get_file_accessible 函数
    package.loaded['utils.vos'].get_file_accessible = function(path)
        table.insert(vos_calls, {method = 'get_file_accessible', path = path})
        if path == '/tmp/not_exist.hpm' then
            return false
        end
        return true
    end
    
    -- Reset mock_utils_core.dir to default behavior
    mock_utils_core.dir = function(path)
        return {'file1.tar', 'file2.tar'}
    end
    
    -- Reset mock_client methods
    mock_client.FirmwareActiveFirmwareActiveRegisterActiveAction = function(self, ctx, list)
        table.insert(client_calls, {
            method = 'RegisterActiveAction',
            list = list
        })
        return true
    end
    
    -- Reset mock_upgrade_flash methods
    mock_upgrade_flash.network_adapter_upgrade_flash = function(self, file_path)
        table.insert(upgrade_flash_calls, {
            method = 'upgrade_flash',
            file_path = file_path
        })
        return 0 -- upgrade_defs.RET.OK
    end
    
    -- Reset mock_utils methods
    mock_utils.copy_file = function(src, dst)
        table.insert(utils_calls, {method = 'copy_file', src = src, dst = dst})
        return true
    end
    
    -- Create mock self object
    self.mock_self = {
        cfgs = {},
        upgrade_list = {},
        before_upgrade_version = '',
        is_network_adapter_upgrading = false,
        upgrade_adapter_count = 0,
        power_state = 'PowerOn',
        poweron_locked = false,
        bus = {},
        hpm_path = '/tmp/test.hpm',
        system_id = 1
    }
end

function TEST_Upgrade_Process:tearDown()
    -- 重置调用记录
    fructl_calls = {}
    parser_cfg_calls = {}
    vos_calls = {}
    utils_calls = {}
    client_calls = {}
    upgrade_flash_calls = {}
    
    -- 只清除 test_upgrade 特有的模块，不清除基础模块（如 skynet、mc.logging 等）
    -- 基础模块可能被其他测试使用，清除它们会导致其他测试失败
    package.loaded['upgrade.parser_cfg'] = nil
    package.loaded['upgrade.network_adapter_firmware_object'] = nil
    package.loaded['upgrade.upgrade_flash'] = nil
    package.loaded['upgrade.upgrade_process'] = nil
    package.loaded['upgrade.upgrade_defs'] = nil
    package.loaded['upgrade.network_adapter_upgrade'] = nil
end

-- 测试模块存在
function TEST_Upgrade_Process:test_module_exists()
    lu.assertNotNil(upgrade_process)
    lu.assertTrue(type(upgrade_process) == 'table')
end

-- 测试initial_set_route - 非SFC协议
function TEST_Upgrade_Process:test_initial_set_route_with_non_sfc_protocol()
    -- 添加非SFC协议的固件对象
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        Protocol = 'I2C',
        DefaultRoute = 0
    }
    
    upgrade_flash_calls = {}
    upgrade_process.initial_set_route()
    
    -- 不应该调用set_route
    local has_set_route = false
    for _, call in ipairs(upgrade_flash_calls) do
        if call.method == 'set_route' then
            has_set_route = true
        end
    end
    lu.assertFalse(has_set_route)
end

-- 测试network_adapter_upgrade_prepare - 没有匹配的firmware对象
function TEST_Upgrade_Process:test_prepare_no_matching_firmware()
    -- 添加不匹配的firmware对象
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 999,
        ComponentIDEx = 999,
        FirmwareVersion = '1.0.0'
    }
    
    local ret_code, version = upgrade_process.network_adapter_upgrade_prepare(
        self.mock_self,
        1,
        '/tmp/test.cfg',
        '/tmp/test.hpm'
    )
    
    -- 应该返回成功，但版本为空
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(version, '')
end

-- 测试network_adapter_upgrade_prepare - 电源OFF状态加上电锁
function TEST_Upgrade_Process:test_prepare_power_off_lock()
    -- Mock电源状态为OFF
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        return 'OFF'
    end
    
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        FirmwareVersion = '1.0.0'
    }
    
    upgrade_process.network_adapter_upgrade_prepare(
        self.mock_self,
        1,
        '/tmp/test.cfg',
        '/tmp/test.hpm'
    )
    
    -- 应该加上电锁
    local has_lock = false
    for _, call in ipairs(fructl_calls) do
        if call.method == 'set_poweron_lock_until_success' then
            has_lock = true
            lu.assertEquals(call.sys_id, 1)
            lu.assertEquals(call.state, true)
            lu.assertEquals(call.reason, 'Nic')
        end
    end
    lu.assertTrue(has_lock)
    lu.assertTrue(self.mock_self.poweron_locked)
end

-- 测试network_adapter_upgrade_prepare - 加锁后电源状态变为ON
function TEST_Upgrade_Process:test_prepare_power_on_after_lock()
    local call_count = 0
    -- Mock电源状态：第一次OFF，第二次PowerOn
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        call_count = call_count + 1
        if call_count == 1 then
            return 'OFF'
        else
            return 'PowerOn'
        end
    end
    
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        FirmwareVersion = '1.0.0'
    }
    
    upgrade_process.network_adapter_upgrade_prepare(
        self.mock_self,
        1,
        '/tmp/test.cfg',
        '/tmp/test.hpm'
    )
    
    -- 应该加锁然后解锁
    local lock_calls = {}
    for _, call in ipairs(fructl_calls) do
        if call.method == 'set_poweron_lock_until_success' then
            table.insert(lock_calls, call)
        end
    end
    lu.assertEquals(#lock_calls, 2)
    lu.assertEquals(lock_calls[1].state, true)  -- 加锁
    lu.assertEquals(lock_calls[2].state, false) -- 解锁
    lu.assertFalse(self.mock_self.poweron_locked)
end

-- 测试network_adapter_upgrade_process - 成功场景（电源ON，缓存文件）
function TEST_Upgrade_Process:test_process_power_on_cache_file()
    self.mock_self.power_state = 'PowerOn'
    self.mock_self.hpm_path = '/tmp/test.hpm'
    self.mock_self.cfgs = {Firmware1 = {}}
    
    local ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    -- 应该缓存文件而不是升级flash
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    
    -- 验证copy_file被调用
    lu.assertTrue(#utils_calls >= 1)
    local has_copy = false
    for _, call in ipairs(utils_calls) do
        if call.method == 'copy_file' then
            has_copy = true
            lu.assertEquals(call.src, '/tmp/test.hpm')
        end
    end
    lu.assertTrue(has_copy)
    
    -- 不应该调用upgrade_flash
    local has_upgrade_flash = false
    for _, call in ipairs(upgrade_flash_calls) do
        if call.method == 'upgrade_flash' then
            has_upgrade_flash = true
        end
    end
    lu.assertFalse(has_upgrade_flash)
end

-- 测试network_adapter_upgrade_process - 成功场景（电源OFF，升级flash）
function TEST_Upgrade_Process:test_process_power_off_upgrade_flash()
    -- Mock电源状态为OFF
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        return 'OFF'
    end
    
    self.mock_self.power_state = 'OFF'
    self.mock_self.hpm_path = '/tmp/test.hpm'
    self.mock_self.cfgs = {Firmware1 = {}}
    
    local ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    -- 应该升级flash
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    
    -- 验证upgrade_flash被调用
    lu.assertTrue(#upgrade_flash_calls >= 1)
    local has_upgrade_flash = false
    for _, call in ipairs(upgrade_flash_calls) do
        if call.method == 'upgrade_flash' then
            has_upgrade_flash = true
            lu.assertEquals(call.file_path, '/tmp/firmware.tar')
        end
    end
    lu.assertTrue(has_upgrade_flash)
end

-- 测试network_adapter_upgrade_process - hpm文件不可访问
function TEST_Upgrade_Process:test_process_hpm_not_accessible()
    self.mock_self.power_state = 'PowerOn'
    self.mock_self.hpm_path = '/tmp/not_exist.hpm'
    self.mock_self.cfgs = {Firmware1 = {}}
    
    -- Mock get_file_accessible返回false
    package.loaded['utils.vos'].get_file_accessible = function(path)
        table.insert(vos_calls, {method = 'get_file_accessible', path = path})
        return false
    end
    
    local ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    -- 缓存失败，应该继续升级flash
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
end

-- 测试network_adapter_upgrade_process - upgrade_flash返回错误
function TEST_Upgrade_Process:test_process_upgrade_flash_failed()
    -- Mock电源状态为OFF
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        return 'OFF'
    end
    
    -- Mock upgrade_flash返回错误
    mock_upgrade_flash.network_adapter_upgrade_flash = function(self, file_path)
        table.insert(upgrade_flash_calls, {
            method = 'upgrade_flash',
            file_path = file_path
        })
        return upgrade_defs.RET.ERR
    end
    
    self.mock_self.power_state = 'OFF'
    self.mock_self.hpm_path = '/tmp/test.hpm'
    self.mock_self.cfgs = {Firmware1 = {}}
    
    local ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    -- 应该返回错误
    lu.assertEquals(ret_code, upgrade_defs.RET.ERR)
end

-- 测试network_adapter_upgrade_process - upgrade_flash抛出异常
function TEST_Upgrade_Process:test_process_upgrade_flash_exception()
    -- Mock电源状态为OFF
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        return 'OFF'
    end
    
    -- Mock upgrade_flash抛出异常
    mock_upgrade_flash.network_adapter_upgrade_flash = function(self, file_path)
        error('flash upgrade failed')
    end
    
    self.mock_self.power_state = 'OFF'
    self.mock_self.hpm_path = '/tmp/test.hpm'
    self.mock_self.cfgs = {Firmware1 = {}}
    
    local ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    -- 应该捕获异常并返回错误
    lu.assertEquals(ret_code, upgrade_defs.RET.ERR)
end

-- 测试network_adapter_upgrade_process - prepare和process阶段电源状态不一致
function TEST_Upgrade_Process:test_process_power_state_changed()
    -- Mock电源状态变化
    local call_count = 0
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        call_count = call_count + 1
        if call_count <= 2 then
            return 'PowerOn'
        else
            return 'OFF'
        end
    end
    
    self.mock_self.power_state = 'PowerOn' -- prepare阶段是PowerOn
    self.mock_self.hpm_path = '/tmp/test.hpm'
    self.mock_self.cfgs = {Firmware1 = {}}
    
    upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    -- 应该更新电源状态
    lu.assertTrue(#fructl_calls >= 1)
    local has_get_power = false
    for _, call in ipairs(fructl_calls) do
        if call.method == 'get_power_status' then
            has_get_power = true
        end
    end
    lu.assertTrue(has_get_power)
end

-- 测试network_adapter_upgrade_finish - 有缓存文件，注册激活任务
function TEST_Upgrade_Process:test_finish_with_cached_file()
    -- Mock vos.get_file_accessible返回true
    package.loaded['utils.vos'].get_file_accessible = function(path)
        table.insert(vos_calls, {method = 'get_file_accessible', path = path})
        return true
    end
    
    local ret_code = upgrade_process.network_adapter_upgrade_finish(self.mock_self)
    
    -- 应该注册激活任务
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertTrue(#client_calls >= 1)
    lu.assertEquals(client_calls[1].method, 'RegisterActiveAction')
    lu.assertNotNil(client_calls[1].list)
end

-- 测试network_adapter_upgrade_finish - 没有缓存文件，不注册激活任务
function TEST_Upgrade_Process:test_finish_without_cached_file()
    -- Mock utils_core.dir返回空目录
    mock_utils_core.dir = function(path)
        return {}
    end
    
    -- Mock vos.get_file_accessible返回true
    package.loaded['utils.vos'].get_file_accessible = function(path)
        table.insert(vos_calls, {method = 'get_file_accessible', path = path})
        return true
    end
    
    local ret_code = upgrade_process.network_adapter_upgrade_finish(self.mock_self)
    
    -- 不应该注册激活任务
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(#client_calls, 0)
end

-- 测试network_adapter_upgrade_finish - 缓存目录不存在
function TEST_Upgrade_Process:test_finish_cache_dir_not_exist()
    -- Mock vos.get_file_accessible返回false
    package.loaded['utils.vos'].get_file_accessible = function(path)
        table.insert(vos_calls, {method = 'get_file_accessible', path = path})
        return false
    end
    
    local ret_code = upgrade_process.network_adapter_upgrade_finish(self.mock_self)
    
    -- 不应该注册激活任务
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(#client_calls, 0)
end

-- 测试network_adapter_upgrade_finish - 注册激活任务抛出异常
function TEST_Upgrade_Process:test_finish_register_active_exception()
    -- Mock client抛出异常
    mock_client.FirmwareActiveFirmwareActiveRegisterActiveAction = function()
        error('register failed')
    end
    
    local ret_code = upgrade_process.network_adapter_upgrade_finish(self.mock_self)
    
    -- 应该捕获异常，但返回成功
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
end

-- 测试完整的upgrade_process流程
function TEST_Upgrade_Process:test_full_upgrade_process_flow()
    -- 准备阶段
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        FirmwareVersion = '1.0.0'
    }
    
    local ret_code, version = upgrade_process.network_adapter_upgrade_prepare(
        self.mock_self,
        1,
        '/tmp/test.cfg',
        '/tmp/test.hpm'
    )
    
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(version, '1.0.0')
    lu.assertTrue(self.mock_self.is_network_adapter_upgrading)
    
    -- 执行阶段
    self.mock_self.cfgs = {Firmware1 = {}}
    ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    
    -- 完成阶段
    ret_code = upgrade_process.network_adapter_upgrade_finish(self.mock_self)
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
end

-- ========== 新增测试用例以提高代码覆盖率 ==========

-- 测试prepare - cfg为空导致version为空
function TEST_Upgrade_Process:test_prepare_empty_cfgs()
    mock_parser_cfg.get_cfgs = function(path)
        table.insert(parser_cfg_calls, {method = 'get_cfgs', path = path})
        return {}
    end
    
    local ret_code, version = upgrade_process.network_adapter_upgrade_prepare(
        self.mock_self,
        1,
        '/tmp/test.cfg',
        '/tmp/test.hpm'
    )
    
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(version, '')
    lu.assertTrue(self.mock_self.is_network_adapter_upgrading)
end

-- 测试prepare - 多个cfg，只有第二个匹配
function TEST_Upgrade_Process:test_prepare_multiple_cfgs_second_matches()
    mock_parser_cfg.get_cfgs = function(path)
        return {
            Firmware1 = {
                name = 'Firmware1',
                component_id = 99,  -- 不匹配
                component_idex = 99
            },
            Firmware2 = {
                name = 'Firmware2',
                component_id = 1,  -- 匹配
                component_idex = 2
            }
        }
    end
    
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        FirmwareVersion = '2.0.0'
    }
    
    local ret_code, version = upgrade_process.network_adapter_upgrade_prepare(
        self.mock_self,
        1,
        '/tmp/test.cfg',
        '/tmp/test.hpm'
    )
    
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(version, '2.0.0')
end

-- 测试prepare - 下电状态且未加锁（应该加锁）
function TEST_Upgrade_Process:test_prepare_power_off_should_lock()
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        return 'OFF'
    end
    
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        FirmwareVersion = '1.0.0'
    }
    
    self.mock_self.power_state = nil
    self.mock_self.poweron_locked = false
    
    local ret_code = upgrade_process.network_adapter_upgrade_prepare(
        self.mock_self,
        1,
        '/tmp/test.cfg',
        '/tmp/test.hpm'
    )
    
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(self.mock_self.power_state, 'OFF')
    lu.assertTrue(self.mock_self.poweron_locked)
    
    -- 验证加锁调用
    local lock_calls = {}
    for _, call in ipairs(fructl_calls) do
        if call.method == 'set_poweron_lock_until_success' and call.state == true then
            table.insert(lock_calls, call)
        end
    end
    lu.assertEquals(#lock_calls, 1)
end

-- 测试prepare - 加锁后电源状态变为上电（应该解锁）
function TEST_Upgrade_Process:test_prepare_power_on_after_lock()
    local power_status_call_count = 0
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        power_status_call_count = power_status_call_count + 1
        if power_status_call_count == 1 then
            return 'OFF'  -- 第一次检查：下电
        else
            return 'PowerOn'  -- 加锁后检查：上电
        end
    end
    
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        FirmwareVersion = '1.0.0'
    }
    
    self.mock_self.power_state = nil
    self.mock_self.poweron_locked = false
    
    local ret_code = upgrade_process.network_adapter_upgrade_prepare(
        self.mock_self,
        1,
        '/tmp/test.cfg',
        '/tmp/test.hpm'
    )
    
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(self.mock_self.power_state, 'PowerOn')  -- 状态更新为上电
    lu.assertFalse(self.mock_self.poweron_locked)  -- 应该解锁
    
    -- 验证先加锁再解锁
    local lock_unlock_sequence = {}
    for _, call in ipairs(fructl_calls) do
        if call.method == 'set_poweron_lock_until_success' then
            table.insert(lock_unlock_sequence, call.state)
        end
    end
    lu.assertEquals(#lock_unlock_sequence, 2)
    lu.assertEquals(lock_unlock_sequence[1], true)   -- 加锁
    lu.assertEquals(lock_unlock_sequence[2], false)  -- 解锁
end

-- 测试prepare - 已经加过锁（不应该重复加锁）
function TEST_Upgrade_Process:test_prepare_already_locked()
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        return 'OFF'
    end
    
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        FirmwareVersion = '1.0.0'
    }
    
    self.mock_self.power_state = 'OFF'
    self.mock_self.poweron_locked = true  -- 已经加过锁
    
    local ret_code = upgrade_process.network_adapter_upgrade_prepare(
        self.mock_self,
        1,
        '/tmp/test.cfg',
        '/tmp/test.hpm'
    )
    
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    
    -- 不应该再次加锁
    local lock_calls = {}
    for _, call in ipairs(fructl_calls) do
        if call.method == 'set_poweron_lock_until_success' then
            table.insert(lock_calls, call)
        end
    end
    lu.assertEquals(#lock_calls, 0)  -- 不应该有加锁调用
end

-- 测试process - hpm文件不可访问，缓存失败
function TEST_Upgrade_Process:test_process_hpm_not_accessible_cache_fail()
    mock_fructl.get_power_status = function()
        return 'PowerOn'  -- 上电状态会尝试缓存
    end
    
    package.loaded['utils.vos'].get_file_accessible = function(path)
        table.insert(vos_calls, {method = 'get_file_accessible', path = path})
        if path == '/tmp/not_accessible.hpm' then
            return false  -- hpm文件不可访问
        end
        return true
    end
    
    self.mock_self.hpm_path = '/tmp/not_accessible.hpm'
    self.mock_self.power_state = 'PowerOn'
    
    local ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    -- 即使缓存失败，也应该返回成功（因为上电状态缓存是可选的）
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
end

-- 测试process - copy_file失败
function TEST_Upgrade_Process:test_process_copy_file_fail()
    mock_fructl.get_power_status = function()
        return 'PowerOn'
    end
    
    mock_utils.copy_file = function(src, dst)
        table.insert(utils_calls, {method = 'copy_file', src = src, dst = dst})
        error('copy failed')  -- 抛出异常
    end
    
    self.mock_self.hpm_path = '/tmp/test.hpm'
    self.mock_self.power_state = 'PowerOn'
    
    local ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    -- 缓存失败，但返回成功
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
end

-- 测试process - 下电状态但process阶段变成上电
function TEST_Upgrade_Process:test_process_power_state_changed_to_on()
    local power_call_count = 0
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        power_call_count = power_call_count + 1
        if power_call_count <= 2 then
            return 'OFF'  -- prepare阶段：下电
        else
            return 'PowerOn'  -- process阶段：上电
        end
    end
    
    self.mock_self.power_state = 'OFF'
    self.mock_self.poweron_locked = false
    
    local ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    -- 应该尝试缓存而不是刷写
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
end

-- 测试initial_set_route - 非SFC协议（不设置路由）
function TEST_Upgrade_Process:test_initial_set_route_non_sfc()
    mock_network_adapter_firmware_objects['test'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        Protocol = 'I2C',  -- 非SFC协议
        DefaultRoute = 0x00
    }
    
    upgrade_process.initial_set_route()
    
    -- 不应该有set_route调用
    local set_route_calls = {}
    for _, call in ipairs(upgrade_flash_calls) do
        if call.method == 'set_route' then
            table.insert(set_route_calls, call)
        end
    end
    lu.assertEquals(#set_route_calls, 0)
end

-- 测试initial_set_route - 多个网卡，部分是SFC协议
function TEST_Upgrade_Process:test_initial_set_route_mixed_protocols()
    mock_network_adapter_firmware_objects['test1'] = {
        ComponentID = 1,
        ComponentIDEx = 2,
        Protocol = 'SFC',
        DefaultRoute = 0x01
    }
    mock_network_adapter_firmware_objects['test2'] = {
        ComponentID = 3,
        ComponentIDEx = 4,
        Protocol = 'I2C',
        DefaultRoute = 0x02
    }
    mock_network_adapter_firmware_objects['test3'] = {
        ComponentID = 5,
        ComponentIDEx = 6,
        Protocol = 'SFC',
        DefaultRoute = 0x03
    }
    
    -- Mock set_network_adapter_firmware_route
    local set_route_calls = {}
    mock_upgrade_flash.set_network_adapter_firmware_route = function(obj, route)
        table.insert(set_route_calls, {
            component_id = obj.ComponentID,
            route = route
        })
    end
    
    upgrade_process.initial_set_route()
    
    -- 只有SFC协议的应该被设置
    lu.assertEquals(#set_route_calls, 2)
    local expected = { [1] = { component_id = 1, route = 0x01 }, [5] = { component_id = 5, route = 0x03 } }
    for _, call in ipairs(set_route_calls) do
        local exp = expected[call.component_id]
        lu.assertNotNil(exp, 'unexpected component_id ' .. tostring(call.component_id))
        lu.assertEquals(call.route, exp.route)
    end
end

-- 测试finish - 缓存目录存在但Utils_core.dir为空
function TEST_Upgrade_Process:test_finish_cache_dir_empty_by_dir()
    package.loaded['utils.vos'].get_file_accessible = function(path)
        table.insert(vos_calls, {method = 'get_file_accessible', path = path})
        return true  -- 目录存在
    end
    
    mock_utils_core.dir = function(path)
        return {}  -- 但是为空
    end
    
    local ret_code = upgrade_process.network_adapter_upgrade_finish(self.mock_self)
    
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertEquals(#client_calls, 0)  -- 不应该注册
end

-- 测试process - 下电状态且未加锁（process阶段加锁）
function TEST_Upgrade_Process:test_process_power_off_add_lock()
    mock_fructl.get_power_status = function()
        table.insert(fructl_calls, {method = 'get_power_status'})
        return 'OFF'
    end
    
    self.mock_self.power_state = nil  -- 初始未设置
    self.mock_self.poweron_locked = false
    
    local ret_code = upgrade_process.network_adapter_upgrade_process(
        self.mock_self,
        1,
        'Nic',
        '/tmp/firmware.tar',
        {}
    )
    
    lu.assertEquals(ret_code, upgrade_defs.RET.OK)
    lu.assertTrue(self.mock_self.poweron_locked)
    
    -- 验证调用了upgrade_flash
    lu.assertTrue(#upgrade_flash_calls >= 1)
end

return TEST_Upgrade_Process
