-- 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 nvme_admin_command_process = require 'nvme.nvme_mi_protocol.nvme_admin_command_process'
local nvme_admin_command = require 'nvme.nvme_mi_protocol.nvme_mi_admin_command'
local nvme_mi_command = require 'nvme.nvme_mi_protocol.nvme_mi_command'
local NVME_MI_OPTIONS = require 'nvme.nvme_mi_protocol.nvme_mi_def'

TESTNvmeMiAdminProcess = {}

local function pack(obj)
    local result = ''
    for _, v in ipairs(obj) do
        result = result .. v
    end
    return result
end

-- 测试是否支持uuid
function TESTNvmeMiAdminProcess:test_check_support_uuid_by_identify_ctrl()
    local origin_func = nvme_admin_command.identify_ctrl
    nvme_admin_command.identify_ctrl = function ()
    end
    local ret = nvme_admin_command_process.check_support_uuid_by_identify_ctrl()
    lu.assertEquals(ret, false)

    nvme_admin_command.identify_ctrl = function ()
        return ''
    end
    ret = nvme_admin_command_process.check_support_uuid_by_identify_ctrl()
    lu.assertEquals(ret, false)
    nvme_admin_command.identify_ctrl = origin_func
    local nvme_obj = {
        queue = function (func)
            func()
        end,
        ctrl_id = 1,
        nvme = {
            Slot = 1
        },
        nvme_mi_obj = {
            Identify = function ()
                return {
                    value = function ()
                        return pack({
                            '\x00\x00\x00',                 -- rsvd
                            '\x00\x00\x00\x00',             -- cdw0
                            '\x00\x00\x00\x00',             -- cdw1
                            '\x00\x00\x00\x00',             -- cdw3
                            '\x00\x00',                     -- vid
                            '\x00\x00',                     -- ssvid
                            '\x48\x53\x35\x45\x32\x33\x41\x32\x34\x31\x30\x30\x50\x51\x30\x4D\x20\x20\x20\x20', -- sn
                            '\x48\x53\x35\x45\x32\x33\x41\x32\x34\x31\x30\x30\x50\x51\x30\x4D\x20\x20\x20\x20', -- mn
                            '\x48\x53\x35\x45\x32\x33\x41\x32\x34\x31\x30\x30\x50\x51\x30\x4D\x20\x20\x20\x20', -- sn
                            '\x00\x00\x00\x00\x00\x00\x00\x00',                     -- fr
                            '\x00\x00\x00\x00\x00\x00\x01\x00',
                            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
                            '\x11\x82\x08\x00'               -- ctratt
                        })
                    end
                }
            end
        }
    }
    ret = nvme_admin_command_process.check_support_uuid_by_identify_ctrl(nvme_obj)
    lu.assertEquals(ret, true)
end

-- 测试指定opcode是否支持uuid
function TESTNvmeMiAdminProcess:test_check_specified_opcode_support_uuid()
    local origin_func = nvme_admin_command.get_support_effects_log
    nvme_admin_command.get_support_effects_log = function ()
    end
    local ret = nvme_admin_command_process.check_specified_opcode_support_uuid()
    lu.assertEquals(ret, false)

    nvme_admin_command.get_support_effects_log = function ()
        return 'test'
    end
    ret = nvme_admin_command_process.check_specified_opcode_support_uuid()
    lu.assertEquals(ret, false)

    nvme_admin_command.get_support_effects_log = origin_func
    local nvme_obj = {
        queue = function (func)
            func()
        end,
        ctrl_id = 1,
        nvme = {
            Slot = 1
        },
        nvme_mi_obj = {
            GetLogPage = function ()
                return {
                    value = function ()
                        return pack({
                            '\x00\x00\x00',                 -- rsvd
                            '\x00\x00\x00\x00',             -- cdw0
                            '\x00\x00\x00\x00',             -- cdw1
                            '\x00\x00\x00\x00',             -- cdw3
                            '\x00\x00\x00\x00\x32\x33\x41\x32\x34\x31\x30\x30\x50\x51\x30\x4D\x20\x20\x20\x20',
                            '\x01\x00\x08\x00'               -- opcode0x05
                        })
                    end
                }
            end
        }
    }
    ret = nvme_admin_command_process.check_specified_opcode_support_uuid(nvme_obj, 0x05)
    lu.assertEquals(ret, true)
end

-- 测试获取制定的uuid index
function TESTNvmeMiAdminProcess:test_get_specified_uuid_index()
    local origin_func = nvme_admin_command.identify_uuid_list
    nvme_admin_command.identify_uuid_list = function ()
    end
    local ret = nvme_admin_command_process.get_specified_uuid_index()
    lu.assertEquals(ret, false)

    nvme_admin_command.identify_uuid_list = function ()
        return ''
    end
    ret = nvme_admin_command_process.get_specified_uuid_index()
    lu.assertEquals(ret, false)

    nvme_admin_command.identify_uuid_list = function ()
        return pack({'\x00\x00\x00',                 -- rsvd
            '\x00\x00\x00\x00',             -- cdw0
            '\x00\x00\x00\x00',             -- cdw1
            '\x00\x00\x00\x00',             -- cdw3
            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'   -- rsvd1
            })
    end
    ret = nvme_admin_command_process.get_specified_uuid_index()
    lu.assertEquals(ret, false)

    -- 预期uuid index = 2时为对应的uuid
    nvme_admin_command.identify_uuid_list = origin_func
    local nvme_obj = {
        queue = function (func)
            func()
        end,
        ctrl_id = 1,
        nvme = {
            Slot = 1
        },
        nvme_mi_obj = {
            Identify = function ()
                return {
                    value = function ()
                        return pack({
                            '\x00\x00\x00',                 -- rsvd
                            '\x00\x00\x00\x00',             -- cdw0
                            '\x00\x00\x00\x00',             -- cdw1
                            '\x00\x00\x00\x00',             -- cdw3
                            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
                            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',   -- rsvd1
                            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',   -- uuidl1
                            '\x2e\x77\xb6\x9c\x75\x34\x11\xed\xae\x2c\xa8\xbd\xd2\x80\x59\xc0',   -- uuidh1
                            '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',   -- uuidl2
                            '\x2e\x77\xb6\x9c\x75\x34\x11\xed\xae\x2c\xa8\xbd\xd2\x80\x59\xc1',   -- uuidh2
                        })
                    end
                }
            end
        }
    }

    ret = nvme_admin_command_process.get_specified_uuid_index(nvme_obj,
    NVME_MI_OPTIONS.HW_DEFINED_UUID_LL, NVME_MI_OPTIONS.HW_DEFINED_UUID_LH,
    NVME_MI_OPTIONS.HW_DEFINED_UUID_HL, NVME_MI_OPTIONS.HW_DEFINED_UUID_HH)
    lu.assertEquals(ret, 2)
end

-- 测试是否支持不填充UUID获取0XEF LOG PAGE
function TESTNvmeMiAdminProcess:test_check_support_hw_defined_without_uuid()
    local origin_func = nvme_admin_command.get_log_page
    local nvme_obj = {
        ctrl_id = 0,
        nvme = {
            Slot = 1
        }
    }
    nvme_admin_command.get_log_page = function ()
    end
    local ret = nvme_admin_command_process.check_support_hw_defined_without_uuid(nvme_obj)
    lu.assertEquals(ret, false)

    nvme_admin_command.get_log_page = function ()
        return pack({
            '\x00\x00\x00',                 -- rsvd
            '\x00\x00\x00\x00',             -- cdw0
            '\x00\x00\x00\x00',             -- cdw1
            '\x00\x00\x00\x00',             -- cdw3
            '\x00\x01\x00\x64\x00\x48\x12\xA5\x5A\x21\x84\x00',  -- magic_header
        })
    end
    ret = nvme_admin_command_process.check_support_hw_defined_without_uuid(nvme_obj)
    lu.assertEquals(ret, false)

    nvme_admin_command.get_log_page = function ()
        local str = string.rep('\x00', 1512)
        return pack({
            '\x00\x00\x00',                 -- rsvd
            '\x00\x00\x00\x00',             -- cdw0
            '\x00\x00\x00\x00',             -- cdw1
            '\x00\x00\x00\x00',             -- cdw3
            '\x00\x01\x00\x64\x00\x48\x12\xA5\x5A\x21\x84\x00',  -- magic_header
            str,                            -- attr_list
        })
    end
    ret = nvme_admin_command_process.check_support_hw_defined_without_uuid(nvme_obj)
    lu.assertEquals(ret, true)
    nvme_admin_command.get_log_page = origin_func
end

-- 测试 get_hw_defined_smart_log：get_log_page 返回 nil 时返回 nil
function TESTNvmeMiAdminProcess:test_get_hw_defined_smart_log_log_page_nil()
    local origin_get_log_page = nvme_admin_command.get_log_page
    nvme_admin_command.get_log_page = function()
        return nil
    end
    local nvme_obj = {
        queue = function(f) f() end,
        ctrl_id = 0,
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command.get_hw_defined_smart_log(nvme_obj)
    lu.assertIsNil(ret)
    nvme_admin_command.get_log_page = origin_get_log_page
end

-- 测试 get_hw_defined_smart_log：magic 正确时返回 attr_list
function TESTNvmeMiAdminProcess:test_get_hw_defined_smart_log_success()
    local origin_get_log_page = nvme_admin_command.get_log_page
    local attr_content = string.rep('\x01', 1512)
    nvme_admin_command.get_log_page = function()
        return pack({
            '\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x01\x00\x64\x00\x48\x12\xA5\x5A\x21\x84\x00',  -- magic_header, identifier=HW_DEFINED_MAGIC
            attr_content,
        })
    end
    local nvme_obj = {
        queue = function(f) f() end,
        ctrl_id = 0,
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command.get_hw_defined_smart_log(nvme_obj)
    lu.assertNotNil(ret)
    lu.assertEquals(ret, attr_content)
    nvme_admin_command.get_log_page = origin_get_log_page
end

-- 测试 get_hw_defined_smart_log：magic 错误时返回 nil
function TESTNvmeMiAdminProcess:test_get_hw_defined_smart_log_wrong_magic()
    local origin_get_log_page = nvme_admin_command.get_log_page
    nvme_admin_command.get_log_page = function()
        return pack({
            '\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x01\x00\x64\x00\x00\x00\x00\x00\x00\x00\x00',  -- wrong identifier
            string.rep('\x00', 1512),
        })
    end
    local nvme_obj = {
        queue = function(f) f() end,
        ctrl_id = 0,
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command.get_hw_defined_smart_log(nvme_obj)
    lu.assertIsNil(ret)
    nvme_admin_command.get_log_page = origin_get_log_page
end

-- 测试 get_hw_defined_smart_log_raw：返回解析后的 attr_list
function TESTNvmeMiAdminProcess:test_get_hw_defined_smart_log_raw_success()
    local origin_get_log_page = nvme_admin_command.get_log_page
    local attr_content = string.rep('\x01', 16)
    local log_page = NVME_MI_OPTIONS.hw_defined_smart_log_raw_response:pack({
        rsvd = 0,
        cdw0 = 0,
        cdw1 = 0,
        cdw3 = 0,
        attr_list = attr_content,
    })
    nvme_admin_command.get_log_page = function()
        return log_page
    end
    local nvme_obj = {
        queue = function(f) f() end,
        ctrl_id = 0,
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command_process.get_hw_defined_smart_log_raw(nvme_obj, 0)
    lu.assertEquals(ret, attr_content)
    nvme_admin_command.get_log_page = origin_get_log_page
end

-- 测试 get_hw_defined_smart_log 传入 uuid_index 时传给 get_log_page
function TESTNvmeMiAdminProcess:test_get_hw_defined_smart_log_with_uuid_index()
    local origin_get_log_page = nvme_admin_command.get_log_page
    local captured_input
    nvme_admin_command.get_log_page = function(_, input)
        captured_input = input
        return pack({
            '\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x01\x00\x64\x00\x48\x12\xA5\x5A\x21\x84\x00',
            string.rep('\x00', 1512),
        })
    end
    local nvme_obj = {
        queue = function(f) f() end,
        ctrl_id = 0,
        nvme = { Slot = 1 }
    }
    nvme_admin_command.get_hw_defined_smart_log(nvme_obj, 2)
    lu.assertNotNil(captured_input)
    lu.assertEquals(captured_input.uuid_idx, 2)
    nvme_admin_command.get_log_page = origin_get_log_page
end

-- 测试更新admin_command_info
function TESTNvmeMiAdminProcess:test_update_admin_command()
    local origin_func = nvme_admin_command.update_smart_log
    nvme_admin_command.update_smart_log = function ()
    end
    local rsp = {['test'] = 'tttt'}
    local nvme_obj = {
        queue = function(func)
            func()
        end,
        ctrl_id = 0,
        nvme = {
            Slot = 1
        },
        nvme_mi_obj = {
            FwLog = function ()
                return {
                    value = function ()
                        return rsp
                    end
                }
            end,
            FeatureIdentifiers = function ()
                return {
                    value = function ()
                        return rsp
                    end
                }
            end,
            ErrorInformation = function ()
                return {
                    value = function ()
                        return rsp
                    end
                }
            end,
            SupportedLogPages = function ()
                return {
                    value = function ()
                        return rsp
                    end
                }
            end
        }
    }
    nvme_admin_command.update_admin_command_info(nvme_obj)
    for k, v in pairs(rsp) do
        lu.assertEquals(nvme_obj.fw_log[k], v)
    end
    for k, v in pairs(rsp) do
        lu.assertEquals(nvme_obj.feature_identifiers[k], v)
    end
    for k, v in pairs(rsp) do
        lu.assertEquals(nvme_obj.error_log[k], v)
    end
    for k, v in pairs(rsp) do
        lu.assertEquals(nvme_obj.supported_log_pages[k], v)
    end
    nvme_admin_command.update_smart_log = origin_func
end

-- 测试更新mi_command_info
function TESTNvmeMiAdminProcess:test_update_mi_command()
    local rsp = {['test'] = 'tttt'}
    local nvme_obj = {
        queue = function(func)
            func()
        end,
        ctrl_id = 0,
        nvme = {
            Slot = 1
        },
        nvme_mi_obj = {
            ControllerHealthStatus = function ()
                return {
                    value = function ()
                        return rsp
                    end
                }
            end,
            MctpTransSize = function ()
                return {
                    value = function ()
                        return rsp
                    end
                }
            end,
            SubsystemInfo = function ()
                return {
                    value = function ()
                        return rsp
                    end
                }
            end,
            SubsystemHealthStatus = function ()
                return {
                    value = function ()
                        return rsp
                    end
                }
            end
        }
    }
    nvme_mi_command.update_nvme_mi_command_info(nvme_obj)
    for k, v in pairs(rsp) do
        lu.assertEquals(nvme_obj.controller_health_status[k], v)
    end
    for k, v in pairs(rsp) do
        lu.assertEquals(nvme_obj.mctp_trans_unit_size[k], v)
    end
    for k, v in pairs(rsp) do
        lu.assertEquals(nvme_obj.nvm_subsystem_info[k], v)
    end
    for k, v in pairs(rsp) do
        lu.assertEquals(nvme_obj.subsys_health_status[k], v)
    end
end

function TESTNvmeMiAdminProcess:test_get_smart_log_raw_nil()
    local origin_get_log_page = nvme_admin_command.get_log_page
    nvme_admin_command.get_log_page = function()
        return nil
    end
    local nvme_obj = {
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command_process.get_smart_log_raw(nvme_obj)
    lu.assertNil(ret)
    lu.assertNil(nvme_obj.smart_log)
    nvme_admin_command.get_log_page = origin_get_log_page
end

function TESTNvmeMiAdminProcess:test_get_smart_log_raw_unpack_failed()
    local origin_get_log_page = nvme_admin_command.get_log_page
    local origin_unpack = NVME_MI_OPTIONS.log_response.unpack
    nvme_admin_command.get_log_page = function()
        return pack({
            '\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            string.rep('\x00', 500),
        })
    end
    NVME_MI_OPTIONS.log_response.unpack = function()
        return nil
    end
    local nvme_obj = {
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command_process.get_smart_log_raw(nvme_obj)
    lu.assertNil(ret)
    NVME_MI_OPTIONS.log_response.unpack = origin_unpack
    nvme_admin_command.get_log_page = origin_get_log_page
end

function TESTNvmeMiAdminProcess:test_get_smart_log_raw_success()
    local origin_get_log_page = nvme_admin_command.get_log_page
    local data = string.rep('\x01', 500)
    nvme_admin_command.get_log_page = function()
        return pack({
            '\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            data,
        })
    end
    local nvme_obj = {
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command_process.get_smart_log_raw(nvme_obj)
    lu.assertEquals(ret, data)
    nvme_admin_command.get_log_page = origin_get_log_page
end

function TESTNvmeMiAdminProcess:test_get_hw_defined_smart_log_raw_nil()
    local origin_get_log_page = nvme_admin_command.get_log_page
    nvme_admin_command.get_log_page = function()
        return nil
    end
    local nvme_obj = {
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command_process.get_hw_defined_smart_log_raw(nvme_obj, 0)
    lu.assertNil(ret)
    nvme_admin_command.get_log_page = origin_get_log_page
end

function TESTNvmeMiAdminProcess:test_get_hw_defined_smart_log_raw_unpack_failed()
    local origin_get_log_page = nvme_admin_command.get_log_page
    local origin_unpack = NVME_MI_OPTIONS.hw_defined_smart_log_raw_response.unpack
    nvme_admin_command.get_log_page = function()
        return pack({
            '\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            '\x00\x00\x00\x00',
            string.rep('\x00', 3000),
        })
    end
    NVME_MI_OPTIONS.hw_defined_smart_log_raw_response.unpack = function()
        return nil
    end
    local nvme_obj = {
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command_process.get_hw_defined_smart_log_raw(nvme_obj, 0)
    lu.assertNil(ret)
    NVME_MI_OPTIONS.hw_defined_smart_log_raw_response.unpack = origin_unpack
    nvme_admin_command.get_log_page = origin_get_log_page
end

function TESTNvmeMiAdminProcess:test_get_hw_defined_smart_log_raw_success()
    local origin_get_log_page = nvme_admin_command.get_log_page
    local attr_content = string.rep('\x02', 3000)
    local log_page = NVME_MI_OPTIONS.hw_defined_smart_log_raw_response:pack({
        rsvd = 0,
        cdw0 = 0,
        cdw1 = 0,
        cdw3 = 0,
        attr_list = attr_content,
    })
    nvme_admin_command.get_log_page = function()
        return log_page
    end
    local nvme_obj = {
        nvme = { Slot = 1 }
    }
    local ret = nvme_admin_command_process.get_hw_defined_smart_log_raw(nvme_obj, 0)
    lu.assertEquals(ret, attr_content)
    nvme_admin_command.get_log_page = origin_get_log_page
end