-- Copyright (c) 2025 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 config_manager = require 'config_manager.init'

TEST_config_manager = {}

-- 测试init函数 - 初始化config_manager
-- 测试目的：验证config_manager初始化后，nvme_configs应该被初始化为空表
-- 前置条件：无
-- 测试步骤：
--   1. 创建config_manager实例
--   2. 调用init方法进行初始化
-- 预期结果：nvme_configs应该不为nil，且长度为0
function TEST_config_manager:test_init()
    -- 创建config_manager实例
    local cm = config_manager.new()
    -- 执行初始化
    cm:init(nil, nil)
    
    -- 验证结果
    -- 1. nvme_configs应该被初始化，不为nil
    lu.assertNotNil(cm.nvme_configs)
    -- 2. nvme_configs应该为空表，长度为0
    lu.assertEquals(#cm.nvme_configs, 0)
end

-- 测试add_nvme_config_obj_cb函数 - 添加NVMe配置对象
-- 测试目的：验证add_nvme_config_obj_cb能够正确将对象添加到nvme_configs列表，
--           并设置对象的position属性
-- 前置条件：config_manager已初始化
-- 测试步骤：
--   1. 添加第一个对象，验证对象被添加到列表且position被设置
--   2. 添加第二个对象，验证列表长度增加且两个对象都正确保存
-- 预期结果：
--   1. 第一个对象添加后，列表长度为1，对象position被正确设置
--   2. 第二个对象添加后，列表长度为2，两个对象的position都被正确设置
function TEST_config_manager:test_add_nvme_config_obj_cb()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备第一个测试对象
    local test_obj = {}
    local position = 'test_position_1'
    
    -- 执行添加操作
    cm:add_nvme_config_obj_cb(test_obj, position)
    
    -- 验证第一个对象添加结果
    -- 1. 列表长度应该为1
    lu.assertEquals(#cm.nvme_configs, 1)
    -- 2. 列表中的第一个元素应该是test_obj
    lu.assertEquals(cm.nvme_configs[1], test_obj)
    -- 3. test_obj的position属性应该被正确设置
    lu.assertEquals(test_obj.position, position)
    
    -- 准备第二个测试对象
    local test_obj2 = {}
    local position2 = 'test_position_2'
    -- 添加第二个对象
    cm:add_nvme_config_obj_cb(test_obj2, position2)
    
    -- 验证第二个对象添加结果
    -- 1. 列表长度应该为2
    lu.assertEquals(#cm.nvme_configs, 2)
    -- 2. 列表中的第二个元素应该是test_obj2
    lu.assertEquals(cm.nvme_configs[2], test_obj2)
    -- 3. test_obj2的position属性应该被正确设置
    lu.assertEquals(test_obj2.position, position2)
end

-- 测试del_nvme_config_obj_cb函数 - 删除NVMe配置对象
-- 测试目的：验证del_nvme_config_obj_cb能够根据position正确删除列表中的对象，
--           包括删除中间、第一个和最后一个对象的情况
-- 前置条件：config_manager已初始化，列表中已有多个对象
-- 测试步骤：
--   1. 添加三个对象到列表
--   2. 删除中间的对象，验证列表正确更新
--   3. 删除第一个对象，验证列表正确更新
--   4. 删除最后一个对象，验证列表为空
-- 预期结果：
--   1. 删除中间对象后，列表长度为2，剩余对象顺序正确
--   2. 删除第一个对象后，列表长度为1，剩余对象正确
--   3. 删除最后一个对象后，列表长度为0
function TEST_config_manager:test_del_nvme_config_obj_cb()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备三个测试对象
    local test_obj1 = {}
    local position1 = 'test_position_1'
    local test_obj2 = {}
    local position2 = 'test_position_2'
    local test_obj3 = {}
    local position3 = 'test_position_3'
    
    -- 添加三个对象到列表
    cm:add_nvme_config_obj_cb(test_obj1, position1)
    cm:add_nvme_config_obj_cb(test_obj2, position2)
    cm:add_nvme_config_obj_cb(test_obj3, position3)
    
    -- 验证初始状态：列表应该包含3个对象
    lu.assertEquals(#cm.nvme_configs, 3)
    
    -- 测试删除中间的对象
    cm:del_nvme_config_obj_cb(test_obj2, position2)
    -- 验证删除结果：列表长度应该为2，剩余对象顺序正确
    lu.assertEquals(#cm.nvme_configs, 2)
    lu.assertEquals(cm.nvme_configs[1], test_obj1)
    lu.assertEquals(cm.nvme_configs[2], test_obj3)
    
    -- 测试删除第一个对象
    cm:del_nvme_config_obj_cb(test_obj1, position1)
    -- 验证删除结果：列表长度应该为1，剩余对象正确
    lu.assertEquals(#cm.nvme_configs, 1)
    lu.assertEquals(cm.nvme_configs[1], test_obj3)
    
    -- 测试删除最后一个对象
    cm:del_nvme_config_obj_cb(test_obj3, position3)
    -- 验证删除结果：列表应该为空
    lu.assertEquals(#cm.nvme_configs, 0)
end

-- 测试del_nvme_config_obj_cb函数 - 删除不存在的对象
-- 测试目的：验证当尝试删除不存在的position时，函数应该安全处理，
--           不会影响列表中的现有对象
-- 前置条件：config_manager已初始化，列表中已有对象
-- 测试步骤：
--   1. 添加一个对象到列表
--   2. 尝试删除不存在的position
-- 预期结果：列表长度应该保持不变，现有对象不受影响
function TEST_config_manager:test_del_nvme_config_obj_cb_not_exist()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备测试对象并添加到列表
    local test_obj = {}
    local position = 'test_position_1'
    cm:add_nvme_config_obj_cb(test_obj, position)
    
    -- 验证初始状态：列表应该包含1个对象
    lu.assertEquals(#cm.nvme_configs, 1)
    
    -- 尝试删除不存在的position（边界情况测试）
    cm:del_nvme_config_obj_cb({}, 'non_exist_position')
    
    -- 验证结果：列表长度应该保持不变，因为找不到匹配的position
    lu.assertEquals(#cm.nvme_configs, 1)
end

-- 测试on_add_object函数 - 处理NVMeConfig类型对象
-- 测试目的：验证on_add_object能够正确识别'NVMeConfig'类型并调用add_nvme_config_obj_cb
-- 前置条件：config_manager已初始化
-- 测试步骤：
--   1. 调用on_add_object，传入'NVMeConfig'类型
--   2. 验证对象被正确添加到列表
-- 预期结果：对象应该被添加到nvme_configs列表，且position属性被正确设置
function TEST_config_manager:test_on_add_object_nvme_config()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备测试数据
    local test_obj = {}
    local position = 'test_position_1'
    
    -- 执行添加操作，使用on_add_object接口
    cm:on_add_object('NVMeConfig', test_obj, position)
    
    -- 验证结果
    -- 1. 列表长度应该为1
    lu.assertEquals(#cm.nvme_configs, 1)
    -- 2. 列表中的对象应该是test_obj
    lu.assertEquals(cm.nvme_configs[1], test_obj)
    -- 3. 对象的position属性应该被正确设置
    lu.assertEquals(test_obj.position, position)
end

-- 测试on_add_object函数 - 不处理其他类型对象
-- 测试目的：验证on_add_object只处理'NVMeConfig'类型，其他类型应该被忽略
-- 前置条件：config_manager已初始化
-- 测试步骤：
--   1. 调用on_add_object，传入非'NVMeConfig'类型（如'OtherClass'）
--   2. 验证对象未被添加到列表
-- 预期结果：列表应该保持为空，对象的position属性不应该被设置
function TEST_config_manager:test_on_add_object_other_class()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备测试数据
    local test_obj = {}
    local position = 'test_position_1'
    
    -- 执行添加操作，使用非NVMeConfig类型（应该被忽略）
    cm:on_add_object('OtherClass', test_obj, position)
    
    -- 验证结果
    -- 1. 列表应该保持为空，因为'OtherClass'类型不被处理
    lu.assertEquals(#cm.nvme_configs, 0)
    -- 2. 对象的position属性不应该被设置
    lu.assertNil(test_obj.position)
end

-- 测试on_delete_object函数 - 处理NVMeConfig类型对象
-- 测试目的：验证on_delete_object能够正确识别'NVMeConfig'类型并调用del_nvme_config_obj_cb
-- 前置条件：config_manager已初始化，列表中已有对象
-- 测试步骤：
--   1. 添加两个对象到列表
--   2. 调用on_delete_object删除第一个对象
-- 预期结果：对象应该被正确删除，列表长度减少，剩余对象正确
function TEST_config_manager:test_on_delete_object_nvme_config()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备测试数据
    local test_obj1 = {}
    local position1 = 'test_position_1'
    local test_obj2 = {}
    local position2 = 'test_position_2'
    
    -- 添加两个对象到列表
    cm:add_nvme_config_obj_cb(test_obj1, position1)
    cm:add_nvme_config_obj_cb(test_obj2, position2)
    
    -- 验证初始状态：列表应该包含2个对象
    lu.assertEquals(#cm.nvme_configs, 2)
    
    -- 执行删除操作，使用on_delete_object接口
    cm:on_delete_object('NVMeConfig', test_obj1, position1)
    
    -- 验证结果
    -- 1. 列表长度应该为1
    lu.assertEquals(#cm.nvme_configs, 1)
    -- 2. 剩余的对象应该是test_obj2
    lu.assertEquals(cm.nvme_configs[1], test_obj2)
end

-- 测试on_delete_object函数 - 不处理其他类型对象
-- 测试目的：验证on_delete_object只处理'NVMeConfig'类型，其他类型应该被忽略
-- 前置条件：config_manager已初始化，列表中已有对象
-- 测试步骤：
--   1. 添加一个对象到列表
--   2. 调用on_delete_object，传入非'NVMeConfig'类型
-- 预期结果：对象应该保留在列表中，不会被删除
function TEST_config_manager:test_on_delete_object_other_class()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备测试数据
    local test_obj = {}
    local position = 'test_position_1'
    
    -- 添加对象到列表
    cm:add_nvme_config_obj_cb(test_obj, position)
    -- 验证初始状态：列表应该包含1个对象
    lu.assertEquals(#cm.nvme_configs, 1)
    
    -- 尝试删除，使用非NVMeConfig类型（应该被忽略）
    cm:on_delete_object('OtherClass', test_obj, position)
    
    -- 验证结果：对象应该还在列表中，因为'OtherClass'类型不被处理
    lu.assertEquals(#cm.nvme_configs, 1)
end

-- 测试get_nvme_systemid函数 - uid为nil的情况
-- 测试目的：验证当uid参数为nil时，函数应该提前返回nil，不进行后续查找
-- 前置条件：config_manager已初始化
-- 测试步骤：
--   1. 调用get_nvme_systemid，传入nil作为uid参数
-- 预期结果：函数应该返回nil，不进行任何查找操作
function TEST_config_manager:test_get_nvme_systemid_uid_nil()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 执行测试：传入nil作为uid（边界情况）
    local result = cm:get_nvme_systemid(nil, 1)
    
    -- 验证结果：应该返回nil，因为uid为nil时函数提前返回
    lu.assertNil(result)
end

-- 测试get_nvme_systemid函数 - 找不到匹配配置的情况
-- 测试目的：验证当传入的uid在配置中不存在时，函数应该返回nil
-- 前置条件：config_manager已初始化，已添加配置对象，但uid不匹配
-- 测试步骤：
--   1. 添加一个配置对象，包含uid1的配置
--   2. 调用get_nvme_systemid，传入uid2（不存在的uid）
-- 预期结果：应该返回nil，因为找不到匹配的uid配置
function TEST_config_manager:test_get_nvme_systemid_no_match()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备配置对象：包含uid1的配置
    -- Configuration结构：[1]--UID, [2]--SlotId, [3]--SystemId
    local nvme_config_obj = {
        MultiHostMappings = {
            {
                'uid1',  -- [1] UID
                {1, 2, 3},  -- [2] SlotId数组
                {10, 20, 30}  -- [3] SystemId数组
            }
        }
    }
    cm:add_nvme_config_obj_cb(nvme_config_obj, 'pos1')
    
    -- 执行测试：查找不存在的uid2
    local result = cm:get_nvme_systemid('uid2', 1)
    
    -- 验证结果：应该返回nil，因为配置中只有uid1，没有uid2
    lu.assertNil(result)
end

-- 测试get_nvme_systemid函数 - slot_id不匹配的情况
-- 测试目的：验证当uid匹配但slot_id不在配置的SlotId数组中时，函数应该返回nil
-- 前置条件：config_manager已初始化，已添加配置对象，uid匹配但slot_id不匹配
-- 测试步骤：
--   1. 添加一个配置对象，包含uid1的配置，SlotId为{1, 2, 3}
--   2. 调用get_nvme_systemid，传入uid1和slot_id=5（不在SlotId数组中）
-- 预期结果：应该返回nil，因为slot_id=5不在配置的SlotId数组中
function TEST_config_manager:test_get_nvme_systemid_slot_not_match()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备配置对象：uid1的SlotId为{1, 2, 3}，不包含5
    local nvme_config_obj = {
        MultiHostMappings = {
            {
                'uid1',  -- [1] UID
                {1, 2, 3},  -- [2] SlotId数组，只包含1,2,3
                {10, 20, 30}  -- [3] SystemId数组
            }
        }
    }
    cm:add_nvme_config_obj_cb(nvme_config_obj, 'pos1')
    
    -- 执行测试：查找uid1的slot_id=5（不在SlotId数组中）
    local result = cm:get_nvme_systemid('uid1', 5)
    
    -- 验证结果：应该返回nil，因为slot_id=5不在{1, 2, 3}中
    lu.assertNil(result)
end

-- 测试get_nvme_systemid函数 - 成功匹配的完整场景
-- 测试目的：验证当uid和slot_id都匹配时，函数能够正确返回对应的SystemId，
--           包括同一配置对象中的多个配置项和不同slot_id的匹配
-- 前置条件：config_manager已初始化，已添加包含多个配置项的配置对象
-- 测试步骤：
--   1. 添加配置对象，包含两个配置项（uid1和uid2）
--   2. 测试uid1的不同slot_id（1, 2, 3）的匹配
--   3. 测试uid2的不同slot_id（4, 5）的匹配
-- 预期结果：所有匹配的case都应该返回正确的SystemId值
function TEST_config_manager:test_get_nvme_systemid_success()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备配置对象：包含两个配置项，每个配置项有不同的uid和slot_id范围
    local nvme_config_obj = {
        MultiHostMappings = {
            {
                'uid1',  -- [1] UID
                {1, 2, 3},  -- [2] SlotId数组
                {10, 20, 30}  -- [3] SystemId数组，对应关系：slot1->10, slot2->20, slot3->30
            },
            {
                'uid2',  -- [1] UID
                {4, 5},  -- [2] SlotId数组
                {40, 50}  -- [3] SystemId数组，对应关系：slot4->40, slot5->50
            }
        }
    }
    cm:add_nvme_config_obj_cb(nvme_config_obj, 'pos1')
    
    -- 测试第一个配置项的不同slot_id匹配
    -- 测试第一个配置的第一个slot：uid1, slot_id=1 -> SystemId=10
    local result = cm:get_nvme_systemid('uid1', 1)
    lu.assertEquals(result, 10)
    
    -- 测试第一个配置的第二个slot：uid1, slot_id=2 -> SystemId=20
    result = cm:get_nvme_systemid('uid1', 2)
    lu.assertEquals(result, 20)
    
    -- 测试第一个配置的第三个slot：uid1, slot_id=3 -> SystemId=30
    result = cm:get_nvme_systemid('uid1', 3)
    lu.assertEquals(result, 30)
    
    -- 测试第二个配置项的不同slot_id匹配
    -- 测试第二个配置的第一个slot：uid2, slot_id=4 -> SystemId=40
    result = cm:get_nvme_systemid('uid2', 4)
    lu.assertEquals(result, 40)
    
    -- 测试第二个配置的第二个slot：uid2, slot_id=5 -> SystemId=50
    result = cm:get_nvme_systemid('uid2', 5)
    lu.assertEquals(result, 50)
end

-- 测试get_nvme_systemid函数 - 多个配置对象的情况
-- 测试目的：验证当有多个配置对象时，函数能够正确遍历所有配置对象并找到匹配的配置
-- 前置条件：config_manager已初始化，已添加多个配置对象
-- 测试步骤：
--   1. 添加两个不同的配置对象，每个包含不同的uid配置
--   2. 从第一个配置对象中查找system_id
--   3. 从第二个配置对象中查找system_id
-- 预期结果：应该能够从不同的配置对象中正确获取对应的SystemId
function TEST_config_manager:test_get_nvme_systemid_multiple_configs()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备第一个配置对象：包含uid1的配置
    local nvme_config_obj1 = {
        MultiHostMappings = {
            {
                'uid1',  -- [1] UID
                {1, 2},  -- [2] SlotId数组
                {10, 20}  -- [3] SystemId数组
            }
        }
    }
    
    -- 准备第二个配置对象：包含uid2的配置
    local nvme_config_obj2 = {
        MultiHostMappings = {
            {
                'uid2',  -- [1] UID
                {3, 4},  -- [2] SlotId数组
                {30, 40}  -- [3] SystemId数组
            }
        }
    }
    
    -- 添加两个配置对象到列表
    cm:add_nvme_config_obj_cb(nvme_config_obj1, 'pos1')
    cm:add_nvme_config_obj_cb(nvme_config_obj2, 'pos2')
    
    -- 测试从第一个配置对象获取system_id
    local result = cm:get_nvme_systemid('uid1', 1)
    lu.assertEquals(result, 10)
    
    -- 测试从第二个配置对象获取system_id
    result = cm:get_nvme_systemid('uid2', 3)
    lu.assertEquals(result, 30)
end

-- 测试get_nvme_systemid函数 - 同一个配置对象中有多个配置项（相同UID）的情况
-- 测试目的：验证当同一个配置对象中有多个配置项使用相同的UID时，
--           函数会使用最后一个匹配的配置项（因为match_config会被覆盖）
-- 前置条件：config_manager已初始化，已添加包含多个相同UID配置项的配置对象
-- 测试步骤：
--   1. 添加配置对象，包含两个配置项，都使用uid1但有不同的SlotId范围
--   2. 测试slot_id=1（只在第一个配置项中，但会被第二个配置项覆盖）
--   3. 测试slot_id=3和4（在第二个配置项中）
-- 预期结果：
--   1. slot_id=1应该返回nil，因为第二个配置项的SlotId是{3,4}，不包含1
--   2. slot_id=3和4应该返回正确的SystemId（30和40）
function TEST_config_manager:test_get_nvme_systemid_multiple_configurations()
    local cm = config_manager.new()
    cm:init(nil, nil)
    
    -- 准备配置对象：包含两个配置项，都使用相同的uid1但有不同的SlotId范围
    local nvme_config_obj = {
        MultiHostMappings = {
            {
                'uid1',  -- [1] UID
                {1, 2},  -- [2] SlotId数组：包含1和2
                {10, 20}  -- [3] SystemId数组
            },
            {
                'uid1',  -- [1] UID (相同的UID，会被第二个配置项覆盖)
                {3, 4},  -- [2] SlotId数组：包含3和4
                {30, 40}  -- [3] SystemId数组
            }
        }
    }
    cm:add_nvme_config_obj_cb(nvme_config_obj, 'pos1')
    
    -- 测试slot_id=1的情况
    -- 注意：代码逻辑会遍历所有配置项，找到匹配的uid后使用最后一个匹配的配置项
    -- 因为match_config会被覆盖，所以会使用第二个配置项
    -- 但第二个配置项的SlotId是{3,4}，不包含1，所以应该返回nil
    local result = cm:get_nvme_systemid('uid1', 1)
    lu.assertNil(result)
    
    -- 测试匹配第二个配置项的情况
    -- slot_id=3在第二个配置项的SlotId数组中，应该返回30
    result = cm:get_nvme_systemid('uid1', 3)
    lu.assertEquals(result, 30)
    
    -- slot_id=4在第二个配置项的SlotId数组中，应该返回40
    result = cm:get_nvme_systemid('uid1', 4)
    lu.assertEquals(result, 40)
end
