-- 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 c_bma = require 'bma'
local test_data = require 'test_datas.bma_nic'
local c_network_adapter_db = require 'network_adapter.db'
local c_ipv4_address = require 'device.class.ipv4_address'
local c_ipv6_address = require 'device.class.ipv6_address'
local c_network_bonding = require 'device.class.network_bonding'
local hook_tasks = require 'test_common.hook_tasks'
local c_object_manage = require 'mc.orm.object_manage'
local c_tasks = require 'mc.orm.tasks'
local test_utils = require 'test_utils'
local json = require 'cjson'

local ipv4_obj = test_utils.ipv4_obj
local ipv6_obj = test_utils.ipv6_obj
local find_address = test_utils.find_address

TEST_bma_eth = {}

function TEST_bma_eth:setUp()
    self.database = c_network_adapter_db(':memory:')

    hook_tasks.hook()
    self.object_manage = c_object_manage.new(self.database)
    self.object_manage.app = self

    self.bma = c_bma.new()

    local position = '00010102'
    self.network_adapter_obj = test_utils.add_network_adapter(self.object_manage,
        'NetworkAdapter_1', {
            BoardID = 0x123,
            DeviceLocator = 'some locator',
            Position = 'position',
            Type = 'Physical',
            Model = 'MyMode',
            Name = "a network card",
            Bus = 0xbc,
            Device = 0,
            Function = 0,
            DevBus = 0xbc,
            DevDevice = 0,
            DevFunction = 1,
            SpecialPcieCard = true
        }, position)
    self.network_port_obj = test_utils.add_network_port(self.object_manage, 'NetworkPort_1',
        {PortID = 0, NetDevFuncType = 1}, position, self.network_adapter_obj)
    test_utils.add_object_complete(self.object_manage, position)

    self.object_manage.mc:prepare_ok()

    local eth_data = json.decode(test_data.EthernetInterface)
    self.url = eth_data['@odata.id']
end

function TEST_bma_eth:CreateVLANs()
end

function TEST_bma_eth:tearDown()
    c_bma.destroy()
    c_object_manage.destroy()
    self.database.db:close()
    hook_tasks.unhook()
end

function TEST_bma_eth:CreateIPv4Address(SystemID, ID1, ID2, ID3, prop_setting_cb)
    return test_utils.CreateIPv4Address(SystemID, ID1, ID2, ID3, prop_setting_cb)
end

function TEST_bma_eth:CreateIPv6Address(SystemID, ID1, ID2, ID3, prop_setting_cb)
    return test_utils.CreateIPv6Address(SystemID, ID1, ID2, ID3, prop_setting_cb)
end

function TEST_bma_eth:test_bma_update_eth()
    local na = self.network_adapter_obj
    local np = self.network_port_obj
    local data = json.decode(test_data.EthernetInterface)
    local huawei = data.Oem.Huawei

    self.bma:add(self.url, data)

    lu.assertEquals(na.Model, 'MyMode') -- Model 以 sr 配置为准，除非 sr 中没有配置
    lu.assertEquals(na.Manufacturer, data.Manufacturer) -- Manufacturer 以 sr 配置为准，除非 sr 中没有配置
    lu.assertEquals(na.DisplayName, huawei.NICName)
    lu.assertEquals(na.FirmwareVersion, huawei.FirmwareVersion)
    lu.assertEquals(na.DriverName, huawei.DriverInfo.DriverName)
    lu.assertEquals(na.DriverVersion, huawei.DriverInfo.DriverVersion)
    lu.assertEquals(na.Description, data.Description)
    lu.assertEquals(na.RootBDF, huawei.BDFNumber.RootBDF)

    lu.assertEquals(np.Name, huawei.NICName)
    lu.assertEquals(np.MACAddress, string.upper(data.MACAddress))
    lu.assertEquals(np.BDF, huawei.BDFNumber.BDF)
    lu.assertEquals(np.DriverName, huawei.DriverInfo.DriverName)
    lu.assertEquals(np.DriverVersion, huawei.DriverInfo.DriverVersion)

    local ipv4_addrs = c_ipv4_address.get_addresses()
    lu.assertEquals(#ipv4_addrs, #data.IPv4Addresses)
    for _, v in ipairs(ipv4_addrs) do
        lu.assertEquals(ipv4_obj(v), ipv4_obj(find_address(data.IPv4Addresses, v.Address)))
    end

    local ipv6_addrs = c_ipv6_address.get_addresses()
    lu.assertEquals(#ipv6_addrs, #data.IPv6Addresses)
    for _, v in ipairs(ipv6_addrs) do
        lu.assertEquals(ipv6_obj(v), ipv6_obj(find_address(data.IPv6Addresses, v.Address)))
    end
end

function TEST_bma_eth:test_bma_update_ipv4_address()
    local data = json.decode(test_data.EthernetInterface)

    -- 装入初始化测试数据
    self.bma:add(self.url, data)
    local old_ipv4_addrs = c_ipv4_address.get_addresses()

    -- 修改旧的 ipv4 地址信息
    data.IPv4Addresses[1].SubnetMask = '0.0.0.1'
    -- 再插入一个新 ipv4 地址
    data.IPv4Addresses[#data.IPv4Addresses + 1] = {
        Address = '127.0.0.0',
        AddressOrigin = 'Static',
        Gateway = {'196.128.0.0'},
        SubnetMask = '0.0.0.0'
    }
    self.bma:add(self.url, data)

    local new_ipv4_addrs = c_ipv4_address.get_addresses()
    lu.assertEquals(#new_ipv4_addrs, #old_ipv4_addrs + 1)
    for _, v in ipairs(new_ipv4_addrs) do
        lu.assertEquals(ipv4_obj(v), ipv4_obj(find_address(data.IPv4Addresses, v.Address)))
    end

    -- 删除第一个地址
    table.remove(data.IPv4Addresses, 1)
    self.bma:add(self.url, data)
    local last_ipv4_addrs = c_ipv4_address.get_addresses()
    lu.assertEquals(#last_ipv4_addrs, #new_ipv4_addrs - 1)
    for _, v in ipairs(last_ipv4_addrs) do
        lu.assertEquals(ipv4_obj(v), ipv4_obj(find_address(data.IPv4Addresses, v.Address)))
    end

    -- 清空地址
    data.IPv4Addresses = {}
    self.bma:add(self.url, data)
    lu.assertEquals(c_ipv4_address.get_addresses(), {})
end

function TEST_bma_eth:test_bma_add_and_update_ipv4_address()
    local data = json.decode(test_data.EthernetInterface)

    -- 装入初始化测试数据
    self.bma:add(self.url, data)
    local old_ipv4_addrs = c_ipv4_address.get_addresses()

    -- 部分更新，new_data 仅包含要更新的信息
    local new_data = {IPv4Addresses = test_utils.table_clone(data.IPv4Addresses)}
    new_data.IPv4Addresses[1].SubnetMask = '0.0.0.1'
    new_data.IPv4Addresses[#new_data.IPv4Addresses + 1] = {
        Address = '127.0.0.0',
        AddressOrigin = 'Static',
        Gateway = {'196.128.0.0'},
        SubnetMask = '0.0.0.0'
    }
    self.bma:update(self.url, new_data)

    local new_ipv4_addrs = c_ipv4_address.get_addresses()
    lu.assertEquals(#new_ipv4_addrs, #old_ipv4_addrs + 1)
    for _, v in ipairs(new_ipv4_addrs) do
        lu.assertEquals(ipv4_obj(v), ipv4_obj(find_address(new_data.IPv4Addresses, v.Address)))
    end

    -- 删除第一个地址
    table.remove(new_data.IPv4Addresses, 1)
    self.bma:update(self.url, new_data)
    local last_ipv4_addrs = c_ipv4_address.get_addresses()
    lu.assertEquals(#last_ipv4_addrs, #new_ipv4_addrs - 1)
    for _, v in ipairs(last_ipv4_addrs) do
        lu.assertEquals(ipv4_obj(v), ipv4_obj(find_address(new_data.IPv4Addresses, v.Address)))
    end

    -- 清空地址
    new_data.IPv4Addresses = {}
    self.bma:update(self.url, new_data)
    lu.assertEquals(c_ipv4_address.get_addresses(), {})
end

function TEST_bma_eth:test_bma_update_ipv6_address()
    local data = json.decode(test_data.EthernetInterface)

    -- 装入初始化测试数据
    self.bma:add(self.url, data)
    local old_ipv6_addrs = c_ipv6_address.get_addresses()

    -- 修改旧的 ipv6 地址信息
    data.IPv6Addresses[1].PrefixLength = '128'
    -- 再插入一个新 ipv6 地址
    data.IPv6Addresses[#data.IPv6Addresses + 1] = {
        Address = '8888:1871:1:0:f40:3935:cf5c:aaaa',
        AddressOrigin = 'SLAAC',
        AddressState = 'Deprecated',
        PrefixLength = '64'
    }
    self.bma:add(self.url, data)

    local new_ipv6_addrs = c_ipv6_address.get_addresses()
    lu.assertEquals(#new_ipv6_addrs, #old_ipv6_addrs + 1)
    for _, v in ipairs(new_ipv6_addrs) do
        lu.assertEquals(ipv6_obj(v), ipv6_obj(find_address(data.IPv6Addresses, v.Address)))
    end

    -- 删除第一个地址
    table.remove(data.IPv6Addresses, 1)
    self.bma:add(self.url, data)
    local last_ipv6_addrs = c_ipv6_address.get_addresses()
    lu.assertEquals(#last_ipv6_addrs, #new_ipv6_addrs - 1)
    for _, v in ipairs(last_ipv6_addrs) do
        lu.assertEquals(ipv6_obj(v), ipv6_obj(find_address(data.IPv6Addresses, v.Address)))
    end

    -- 清空地址
    data.IPv6Addresses = {}
    self.bma:add(self.url, data)
    lu.assertEquals(c_ipv6_address.get_addresses(), {})
end

function TEST_bma_eth:test_bma_add_team()
    local data = json.decode(test_data.Team)
    local url = data['@odata.id']
    -- 装入初始化测试数据
    self.bma:add(url, data)
    local team_objs = c_network_bonding.collection:fetch({Id = 'team00'})
    for _, v in pairs(team_objs) do
        lu.assertEquals(v.Name, 'teamA')
    end
    -- ports更新测试
    data['Ports'] = {'eno1'}
    self.bma:add(url, data)
    team_objs = c_network_bonding.collection:fetch({Id = 'team00'})
    for _, v in pairs(team_objs) do
        lu.assertEquals(v.Ports[1], 'eno1')
    end
end

function TEST_bma_eth:test_bma_reset_eth()
    local na = self.network_adapter_obj
    local np = self.network_port_obj
    local data = json.decode(test_data.EthernetInterface)
    local huawei = data.Oem.Huawei

    self.bma:add(self.url, data)
    self.bma:on_reset()

    lu.assertEquals(na.DisplayName, '')
    lu.assertEquals(na.FirmwareVersion, '')
    lu.assertEquals(na.DriverName, '')
    lu.assertEquals(na.DriverVersion, '')
    lu.assertEquals(na.RootBDF, huawei.BDFNumber.RootBDF)

    lu.assertEquals(np.Name, '')
    lu.assertEquals(np.LinkStatus, 'N/A')
    lu.assertEquals(np.MACAddress, string.upper(data.MACAddress))
    lu.assertEquals(np.BDF, huawei.BDFNumber.BDF)
    lu.assertEquals(np.DriverName, '')
    lu.assertEquals(np.DriverVersion, '')

    local ipv4_addrs = c_ipv4_address.get_addresses()
    lu.assertEquals(#ipv4_addrs, 0)
    local ipv6_addrs = c_ipv6_address.get_addresses()
    lu.assertEquals(#ipv6_addrs, 0)
end
