-- Copyright (c) 2024 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'
local ncsi_comm = require 'ncsi.ncsi_comm'
local ncsi_core = require 'ncsi.ncsi_core'
local skynet = require 'skynet'

-- 测试套件
TestNCSIChangeActivePort = {}

-- 模拟数据库对象
local function create_mock_db()
    local db = {
        NcsiNCInfo = 'NcsiNCInfo',
        NcsiNCPortInfo = 'NcsiNCPortInfo',
        select_calls = 0,
        where_calls = 0,
        first_calls = 0
    }
    
    function db:select(table_name)
        self.select_calls = self.select_calls + 1
        self.last_table = table_name
        
        local where_obj = {
            where_calls = 0,
            first_calls = 0
        }
        
        function where_obj:where(conditions)
            self.where_calls = self.where_calls + 1
            self.last_conditions = conditions
            return self
        end
        
        function where_obj:first()
            self.first_calls = self.first_calls + 1
            
            if table_name == db.NcsiNCInfo then
                return {
                    EthId = 0,
                    VlanId = 100,
                    VlanState = false,
                    ActivePort = 1
                }
            elseif table_name == db.NcsiNCPortInfo then
                return {
                    PortId = self.last_conditions.PortId,
                    PackageId = 0,
                    ChannelId = self.last_conditions.PortId - 1,
                    PortAvailable = true,
                    RxEnable = true,
                    Slikscreen = 'NC' .. self.last_conditions.PortId,
                    LinkStatus = 1,
                    save = function() end
                }
            end
            return nil
        end
        
        return where_obj
    end
    
    return db
end

-- 保存原始函数
local original_skynet_sleep = skynet.sleep

-- 重置函数调用计数
local function reset_counters()
    TestNCSIChangeActivePort.ncsi_disable_channelTX_calls = 0
    TestNCSIChangeActivePort.skynet_sleep_calls = 0
end

-- 模拟ncsi_disable_channelTX函数
local function mock_ncsi_disable_channelTX(package_id, channel_id, eth_name)
    TestNCSIChangeActivePort.ncsi_disable_channelTX_calls = TestNCSIChangeActivePort.ncsi_disable_channelTX_calls + 1
    
    -- 根据测试需要返回不同的结果
    if TestNCSIChangeActivePort.mock_return_value then
        return TestNCSIChangeActivePort.mock_return_value
    end
    
    -- 默认返回成功
    return true
end

-- 模拟skynet.sleep函数
local function mock_skynet_sleep(ms)
    TestNCSIChangeActivePort.skynet_sleep_calls = TestNCSIChangeActivePort.skynet_sleep_calls + 1
    TestNCSIChangeActivePort.last_sleep_ms = ms
    return true
end

-- 初始化函数，在每个测试用例前执行
function TestNCSIChangeActivePort:setUp()
    -- 创建模拟数据库
    self.db = create_mock_db()
    self.bus = {}
    
    -- 创建ncsi_comm实例
    self.ncsi_comm = ncsi_comm.new(self.db, self.bus)
    
    -- 保存原始函数
    self.original_ncsi_disable_channelTX = ncsi_core.ncsi_disable_channelTX
    self.original_skynet_sleep = skynet.sleep
    
    -- 使用模拟函数
    ncsi_core.ncsi_disable_channelTX = mock_ncsi_disable_channelTX
    skynet.sleep = mock_skynet_sleep
    
    -- 重置计数器
    reset_counters()
    
    -- 默认模拟返回值
    TestNCSIChangeActivePort.mock_return_value = nil
end

-- 清理函数，在每个测试用例后执行
function TestNCSIChangeActivePort:tearDown()
    -- 恢复原始函数
    ncsi_core.ncsi_disable_channelTX = self.original_ncsi_disable_channelTX
    skynet.sleep = self.original_skynet_sleep
end

-- 测试场景1：第一次调用就成功
function TestNCSIChangeActivePort:test_disable_channelTX_first_try_success()
    -- 设置模拟返回值：第一次就成功
    TestNCSIChangeActivePort.mock_return_value = true
    
    -- 执行测试
    self.ncsi_comm:ncsi_change_active_port(self.db, true, 1, 2)
    
    -- 验证结果
    lu.assertEquals(TestNCSIChangeActivePort.ncsi_disable_channelTX_calls, 1, "应该只调用一次ncsi_disable_channelTX")
end


-- 测试场景2：多次失败后成功
function TestNCSIChangeActivePort:test_disable_channelTX_multiple_failures_then_success()
    -- 记录调用次数并控制返回值
    local call_count = 0
    local max_failures = 3
    
    ncsi_core.ncsi_disable_channelTX = function(package_id, channel_id, eth_name)
        TestNCSIChangeActivePort.ncsi_disable_channelTX_calls = TestNCSIChangeActivePort.ncsi_disable_channelTX_calls + 1
        call_count = call_count + 1
        
        -- 前max_failures次返回失败，之后返回成功
        return call_count > max_failures
    end
    
    -- 执行测试
    self.ncsi_comm:ncsi_change_active_port(self.db, true, 1, 2)
    
    -- 验证结果
    lu.assertEquals(TestNCSIChangeActivePort.ncsi_disable_channelTX_calls, max_failures + 1, "应该调用")
    lu.assertEquals(TestNCSIChangeActivePort.last_sleep_ms, 10, "skynet.sleep的参数应该是10")
end


-- 测试场景3：达到最大重试次数仍然失败
function TestNCSIChangeActivePort:test_disable_channelTX_max_retries_reached()
    -- 设置模拟返回值：始终失败
    TestNCSIChangeActivePort.mock_return_value = false
    
    -- 执行测试
    self.ncsi_comm:ncsi_change_active_port(self.db, true, 1, 2)
    
    -- 验证结果
    lu.assertEquals(TestNCSIChangeActivePort.ncsi_disable_channelTX_calls, 1, "应该调用10次ncsi_disable_channelTX")
end

return TestNCSIChangeActivePort