-- 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.
--         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 c_object = require 'mc.orm.object'
local class = require 'mc.class'
local c_storage_service = require 'storage.service'
local lu = require 'luaunit'
local skynet = require 'skynet'
local log = require 'mc.logging'

TestMdbObject = class(c_storage_service)

local mdb_objects = {
    'StorageConfig',
    'Controller',
    'Nvme',
    'Drive',
    'VirtualVPDConnect',
    'DriveInfoTable',
    'DrivesTableInfo',
    'GlobalStorageConfigTable',
    'Volume',
    'DiskArray'
}

function TestMdbObject:setupClass()
    require 'storage_mdb_object'
end

function TestMdbObject:test_all_objects()
    local ret
    local mdb_object
    for _, v in pairs(mdb_objects) do
        mdb_object = c_object(v)
        ret = mdb_object.create_mdb_object({})
        lu.assertEquals(ret, {})
    end
end

--- Drive.before_add_object 相关用例

-- start_slot 为普通有效值（非 0xFE），不等待、直接允许上树，不在此处设置 Id/Name/NodeId
function TestMdbObject:test_drive_before_add_object_normal_start_slot()
    local c_drive = c_object('Drive')
    local base_obj = {
        HddBackplaneStartSlot = 8,
        RelativeSlot = 2
    }

    local ok = c_drive.before_add_object(base_obj)
    lu.assertTrue(ok)
    lu.assertNil(base_obj.Id)
    lu.assertNil(base_obj.Name)
    lu.assertNil(base_obj.NodeId)
end

-- start_slot 为 0xFF，仅 CSR 生效，不等待、直接允许上树，不在此处设置 Id/Name/NodeId
function TestMdbObject:test_drive_before_add_object_start_slot_ff()
    local c_drive = c_object('Drive')
    local base_obj = {
        HddBackplaneStartSlot = 0xFF,
        RelativeSlot = 1
    }

    local ok = c_drive.before_add_object(base_obj)
    lu.assertTrue(ok)
    lu.assertNil(base_obj.Id)
    lu.assertNil(base_obj.Name)
    lu.assertNil(base_obj.NodeId)
end

-- start_slot 为 0xFE，等待一次后变为有效值，循环应退出并按新值计算
function TestMdbObject:test_drive_before_add_object_wait_until_stable()
    local c_drive = c_object('Drive')
    local base_obj = {
        HddBackplaneStartSlot = 0xFE,
        RelativeSlot = 1,
        ObjectName = 'Drive0'
    }

    local old_sleep = skynet.sleep
    local sleep_count = 0
    skynet.sleep = function(_)
        sleep_count = sleep_count + 1
        if sleep_count == 1 then
            -- 第一次等待后，模拟背板起始槽位变为 10
            base_obj.HddBackplaneStartSlot = 10
        end
    end

    local ok = c_drive.before_add_object(base_obj)

    skynet.sleep = old_sleep

    lu.assertTrue(ok)
    lu.assertEquals(sleep_count, 1)
    lu.assertEquals(base_obj.Id, 11)
    lu.assertEquals(base_obj.Name, 'Disk11')
    lu.assertEquals(base_obj.NodeId, 'HDDPlaneDisk11')
end

-- start_slot 一直为 0xFE，达到超时时间后返回（不上树）
function TestMdbObject:test_drive_before_add_object_timeout()
    local c_drive = c_object('Drive')
    local base_obj = {
        HddBackplaneStartSlot = 0xFE,
        RelativeSlot = 1,
        ObjectName = 'DriveTimeout'
    }

    local old_sleep = skynet.sleep
    local old_info = log.info
    local sleep_count = 0
    local log_called = false

    skynet.sleep = function(_)
        sleep_count = sleep_count + 1
    end

    log.info = function(_, fmt, ...)
        local msg = string.format(fmt, ...)
        if msg:find('timeout waiting HddBackplaneStartSlot') then
            log_called = true
        end
    end

    local ok = c_drive.before_add_object(base_obj)

    skynet.sleep = old_sleep
    log.info = old_info

    -- 超时场景返回 nil / false，且不会设置 Id/Name/NodeId
    lu.assertNil(ok)
    lu.assertTrue(log_called)
    lu.assertNil(base_obj.Id)
    lu.assertNil(base_obj.Name)
    lu.assertNil(base_obj.NodeId)
end
