-- 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 metric_collect = require 'lualib.metric_collect'
local client = require 'general_hardware.client'
local mdb = require 'mc.mdb'
local factory = require 'factory'

TestMetricCollect = {}

-- 测试数据
local mock_cpu_objects = {
    {
        PhysicalId = 0,
        TotalCores = 16,
        TotalThreads = 32,
        MaxSpeedMHz = 3000,
        path = "/system/chassis/1/cpu0"
    },
    {
        PhysicalId = 1,
        TotalCores = 16,
        TotalThreads = 32,
        MaxSpeedMHz = 3000,
        path = "/system/chassis/1/cpu1"
    }
}

local mock_processor_objects = {
    {
        path = "/system/chassis/1/cpu0",
        Presence = 1,
        SN = "CPU001234",
        Manufacturer = "test",
        Model = "xxxx",
        Name = "CPU0"
    },
    {
        path = "/system/chassis/1/cpu1",
        Presence = 1,
        SN = "CPU005678",
        Manufacturer = "test",
        Model = "xxxx",
        Name = "CPU1"
    }
}

local mock_vrdmgmt_obj = {
    CpuId = 0,
    Cpu0v9TACore = 900,
    Cpu0v75DDRVDD = 750,
    Cpu0v9TBCore = 900,
    Cpu0v9Uncore = 900,
    Cpu0v8NADVDD = 800,
    Cpu0v8NBDVDD = 800,
    Cpu1v1DDRVddq = 1100
}

local mock_unit_manager = {
    cpu_board_position = { "1", "2" }
}

function TestMetricCollect:setUp()
    -- 保存原始函数
    self.original_GetCPUObjects = client.GetCPUObjects
    self.original_GetSystemsProcessorObjects = client.GetSystemsProcessorObjects
    self.original_get_object = mdb.get_object
    self.original_get_obj = factory.get_obj

    -- 模拟函数
    client.GetCPUObjects = function()
        return mock_cpu_objects
    end

    client.GetSystemsProcessorObjects = function()
        return mock_processor_objects
    end

    mdb.get_object = function(bus, path, interface)
        if interface == 'bmc.kepler.Systems.VrdMgmt' then
            return mock_vrdmgmt_obj
        end
        return nil
    end

    factory.get_obj = function(name)
        if name == "unit_manager" then
            return mock_unit_manager
        end
        return nil
    end
end

function TestMetricCollect:tearDown()
    -- 恢复原始函数
    client.GetCPUObjects = self.original_GetCPUObjects
    client.GetSystemsProcessorObjects = self.original_GetSystemsProcessorObjects
    mdb.get_object = self.original_get_object
    factory.get_obj = self.original_get_obj
end

function TestMetricCollect:test_module_structure()
    -- 测试模块基本结构
    lu.assertNotNil(metric_collect)
    lu.assertNotNil(metric_collect.get_cpu_voltage_items)
    lu.assertNotNil(metric_collect.get_cpu_voltage_data)

    -- 验证这些是函数
    lu.assertEquals(type(metric_collect.get_cpu_voltage_items), "function")
    lu.assertEquals(type(metric_collect.get_cpu_voltage_data), "function")
end

function TestMetricCollect:test_get_cpu_voltage_items()
    -- 测试BCU位置的CPU电压项
    local test_obj = { path = "/test/path_1" }

    local device_type, classification, identification, metric_names = metric_collect:get_cpu_voltage_items("test_bus",
        test_obj)

    lu.assertEquals(device_type, "CPU")
    lu.assertEquals(#classification, 6)
    lu.assertEquals(#identification, 3)
    lu.assertEquals(#metric_names, 7)

    -- 验证分类信息
    local found_manufacturer = false
    for _, item in ipairs(classification) do
        if item.PropName == "Manufacturer" then
            lu.assertEquals(item.PropVal, "test")
            found_manufacturer = true
            break
        end
    end
    lu.assertTrue(found_manufacturer)

    -- 验证标识信息
    local found_sn = false
    for _, item in ipairs(identification) do
        if item.PropName == "SN" then
            lu.assertEquals(item.PropVal, "CPU001234")
            found_sn = true
            break
        end
    end
    lu.assertTrue(found_sn)

    -- 测试非BCU位置
    local test_obj_non_bcu = { path = "/test/path_3" }
    local device_type2, classification2, identification2, metric_names2 = metric_collect:get_cpu_voltage_items(
    "test_bus", test_obj_non_bcu)
    lu.assertEquals(device_type2, "")
    lu.assertEquals(#classification2, 0)
    lu.assertEquals(#identification2, 0)
    lu.assertEquals(#metric_names2, 0)
end

function TestMetricCollect:test_get_cpu_voltage_data()
    -- 测试获取CPU电压数据
    local test_obj = { path = "/test/path_1" }
    local metric_names = {
        "cpu.tacore_voltage",
        "cpu.ddrvdd_voltage",
        "cpu.tbcore_voltage"
    }

    local data = metric_collect:get_cpu_voltage_data("test_bus", test_obj, metric_names)
    lu.assertEquals(#data, 3)

    -- 验证数据格式
    for _, item in ipairs(data) do
        lu.assertNotNil(item.MetricName)
        lu.assertNotNil(item.Data)
        lu.assertEquals(#item.Data, 1)
    end

    -- 测试非BCU位置
    local test_obj_non_bcu = { path = "/test/path_3" }
    local empty_data = metric_collect:get_cpu_voltage_data("test_bus", test_obj_non_bcu, metric_names)
    lu.assertEquals(#empty_data, 0)
end

function TestMetricCollect:test_get_cpu_voltage_items_with_invalid_processor()
    -- 测试处理器不存在的情况
    local test_obj = { path = "/test/path_1" }

    -- 模拟处理器不存在
    client.GetSystemsProcessorObjects = function()
        return {}
    end

    local device_type, classification, identification, metric_names = metric_collect:get_cpu_voltage_items("test_bus",
        test_obj)
    lu.assertEquals(device_type, "")
    lu.assertEquals(#classification, 0)
    lu.assertEquals(#identification, 0)
    lu.assertEquals(#metric_names, 0)

    -- 恢复模拟函数
    client.GetSystemsProcessorObjects = function()
        return mock_processor_objects
    end
end

function TestMetricCollect:test_get_cpu_voltage_items_with_invalid_presence()
    -- 测试处理器存在但Presence不为1的情况
    local test_obj = { path = "/test/path_1" }

    -- 模拟处理器Presence为0
    local mock_processor_objects_invalid = {
        {
            path = "/system/chassis/1/cpu0",
            Presence = 0,
            SN = "CPU001234",
            Manufacturer = "test",
            Model = "xxxx",
            Name = "CPU0"
        }
    }

    client.GetSystemsProcessorObjects = function()
        return mock_processor_objects_invalid
    end

    local device_type, classification, identification, metric_names = metric_collect:get_cpu_voltage_items("test_bus",
        test_obj)
    lu.assertEquals(device_type, "")
    lu.assertEquals(#classification, 0)
    lu.assertEquals(#identification, 0)
    lu.assertEquals(#metric_names, 0)

    -- 恢复模拟函数
    client.GetSystemsProcessorObjects = function()
        return mock_processor_objects
    end
end

function TestMetricCollect:test_get_cpu_voltage_items_with_empty_sn()
    -- 测试处理器SN为空的情况
    local test_obj = { path = "/test/path_1" }

    -- 模拟处理器SN为空
    local mock_processor_objects_empty_sn = {
        {
            path = "/system/chassis/1/cpu0",
            Presence = 1,
            SN = "",
            Manufacturer = "test",
            Model = "xxxx",
            Name = "CPU0"
        }
    }

    client.GetSystemsProcessorObjects = function()
        return mock_processor_objects_empty_sn
    end

    local device_type, classification, identification, metric_names = metric_collect:get_cpu_voltage_items("test_bus",
        test_obj)
    lu.assertEquals(device_type, "")
    lu.assertEquals(#classification, 0)
    lu.assertEquals(#identification, 0)
    lu.assertEquals(#metric_names, 0)

    -- 恢复模拟函数
    client.GetSystemsProcessorObjects = function()
        return mock_processor_objects
    end
end

function TestMetricCollect:test_get_cpu_voltage_data_with_missing_properties()
    -- 测试VRD管理对象属性缺失的情况
    local test_obj = { path = "/test/path_1" }
    local metric_names = {
        "cpu.tacore_voltage",
        "cpu.ddrvdd_voltage"
    }

    -- Cpu0v75DDRVDD属性缺失
    local mock_vrdmgmt_obj_incomplete = {
        CpuId = 0,
        Cpu0v9TACore = 900,  -- 这个属性存在
    }

    mdb.get_object = function(bus, path, interface)
        if interface == 'bmc.kepler.Systems.VrdMgmt' then
            return mock_vrdmgmt_obj_incomplete
        end
        return nil
    end

    local data = metric_collect:get_cpu_voltage_data("test_bus", test_obj, metric_names)
    lu.assertEquals(#data, 2)

    -- 验证第一个metric有正常值
    lu.assertEquals(data[1].MetricName, "cpu.tacore_voltage")
    lu.assertEquals(data[1].Data[1], "900")

    -- 验证第二个metric返回无效值255
    lu.assertEquals(data[2].MetricName, "cpu.ddrvdd_voltage")
    lu.assertEquals(data[2].Data[1], "255")

    -- 恢复模拟函数
    mdb.get_object = function(bus, path, interface)
        if interface == 'bmc.kepler.Systems.VrdMgmt' then
            return mock_vrdmgmt_obj
        end
        return nil
    end
end
