From 2d16ee397a336aa5245bdd06773b47d914b0e9d7 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 20 Sep 2024 17:57:07 +0400 Subject: [PATCH] netmap: Improve testing and increase coverage Signed-off-by: Leonard Lyubich --- netmap/network_info_test.go | 637 +++++++++++++++++++++++++----------- netmap/node_info_test.go | 630 ++++++++++++++++++++++++++++++++--- 2 files changed, 1027 insertions(+), 240 deletions(-) diff --git a/netmap/network_info_test.go b/netmap/network_info_test.go index 87db6fa2..9fce0cc4 100644 --- a/netmap/network_info_test.go +++ b/netmap/network_info_test.go @@ -1,263 +1,522 @@ package netmap_test import ( - "encoding/binary" - "math" "testing" - netmapv2 "github.com/nspcc-dev/neofs-api-go/v2/netmap" + apinetmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" "github.com/nspcc-dev/neofs-sdk-go/netmap" - netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test" "github.com/stretchr/testify/require" ) +const ( + anyValidCurrentEpoch = uint64(10200868596141730080) + anyValidMagicNumber = uint64(4418809875917597199) + anyValidMSPerBlock = int64(6618240263362299360) + anyValidAuditFee = uint64(439242513058661347) + anyValidStoragePrice = uint64(17664525256563393128) + anyValidNamedContainerFee = uint64(8341161107066979354) + anyValidContainerFee = uint64(6947563960217184460) + anyValidEigenTrustAlpha = 0.0508058412675794 + anyValidEigenTrustIterations = uint64(13357133751475762684) + anyValidEpochDuration = uint64(15624600918358785057) + anyValidHomoHashDisabled = true + anyValidIRCandidateFee = uint64(10380497368037027314) + anyValidMaintenanceModeAllowed = true + anyValidMaxObjectSize = uint64(1434410563277613155) + anyValidWithdrawalFee = uint64(4895019021563966975) +) + +var ( + anyValidBinAuditFee = []byte{227, 131, 28, 9, 189, 128, 24, 6} + anyValidBinStoragePrice = []byte{104, 130, 96, 83, 33, 0, 37, 245} + anyValidBinNamedContainerFee = []byte{26, 4, 8, 97, 213, 193, 193, 115} + anyValidBinContainerFee = []byte{204, 40, 13, 175, 156, 180, 106, 96} + anyValidBinEigenTrustAlpha = []byte{101, 74, 97, 37, 57, 3, 170, 63} + anyValidBinEigenTrustIterations = []byte{252, 245, 23, 186, 96, 18, 94, 185} + anyValidBinEpochDuration = []byte{33, 124, 4, 168, 192, 187, 213, 216} + anyValidBinHomoHashDisabled = []byte{1} + anyValidBinIRCandidateFee = []byte{242, 109, 185, 165, 91, 239, 14, 144} + anyValidBinMaintenanceModeAllowed = []byte{1} + anyValidBinMaxObjectSize = []byte{99, 232, 58, 182, 206, 12, 232, 19} + anyValidBinWithdrawalFee = []byte{255, 181, 25, 125, 221, 153, 238, 67} +) + +// set by init. +var validNetworkInfo netmap.NetworkInfo + +func init() { + validNetworkInfo.SetCurrentEpoch(anyValidCurrentEpoch) + validNetworkInfo.SetMagicNumber(anyValidMagicNumber) + validNetworkInfo.SetMsPerBlock(anyValidMSPerBlock) + validNetworkInfo.SetRawNetworkParameter("k1", []byte("v1")) + validNetworkInfo.SetRawNetworkParameter("k2", []byte("v2")) + validNetworkInfo.SetAuditFee(anyValidAuditFee) + validNetworkInfo.SetStoragePrice(anyValidStoragePrice) + validNetworkInfo.SetNamedContainerFee(anyValidNamedContainerFee) + validNetworkInfo.SetContainerFee(anyValidContainerFee) + validNetworkInfo.SetEigenTrustAlpha(anyValidEigenTrustAlpha) + validNetworkInfo.SetNumberOfEigenTrustIterations(anyValidEigenTrustIterations) + validNetworkInfo.SetEpochDuration(anyValidEpochDuration) + validNetworkInfo.DisableHomomorphicHashing() + validNetworkInfo.SetIRCandidateFee(anyValidIRCandidateFee) + validNetworkInfo.AllowMaintenanceMode() + validNetworkInfo.SetMaxObjectSize(anyValidMaxObjectSize) + validNetworkInfo.SetWithdrawalFee(anyValidWithdrawalFee) +} + +var validBinNetworkInfo = []byte{ + 8, 160, 210, 220, 139, 145, 254, 176, 200, 141, 1, 16, 143, 212, 185, 192, 185, 133, 177, 169, 61, 24, 224, 139, 151, 255, + 197, 206, 173, 236, 91, 34, 239, 2, 10, 8, 10, 2, 107, 49, 18, 2, 118, 49, 10, 8, 10, 2, 107, 50, 18, 2, 118, 50, 10, 20, + 10, 8, 65, 117, 100, 105, 116, 70, 101, 101, 18, 8, 227, 131, 28, 9, 189, 128, 24, 6, 10, 27, 10, 15, 66, 97, 115, 105, 99, 73, + 110, 99, 111, 109, 101, 82, 97, 116, 101, 18, 8, 104, 130, 96, 83, 33, 0, 37, 245, 10, 29, 10, 17, 67, 111, 110, 116, 97, 105, 110, + 101, 114, 65, 108, 105, 97, 115, 70, 101, 101, 18, 8, 26, 4, 8, 97, 213, 193, 193, 115, 10, 24, 10, 12, 67, 111, 110, 116, 97, 105, + 110, 101, 114, 70, 101, 101, 18, 8, 204, 40, 13, 175, 156, 180, 106, 96, 10, 27, 10, 15, 69, 105, 103, 101, 110, 84, 114, 117, 115, + 116, 65, 108, 112, 104, 97, 18, 8, 101, 74, 97, 37, 57, 3, 170, 63, 10, 32, 10, 20, 69, 105, 103, 101, 110, 84, 114, 117, 115, 116, + 73, 116, 101, 114, 97, 116, 105, 111, 110, 115, 18, 8, 252, 245, 23, 186, 96, 18, 94, 185, 10, 25, 10, 13, 69, 112, 111, 99, 104, + 68, 117, 114, 97, 116, 105, 111, 110, 18, 8, 33, 124, 4, 168, 192, 187, 213, 216, 10, 31, 10, 26, 72, 111, 109, 111, 109, 111, 114, + 112, 104, 105, 99, 72, 97, 115, 104, 105, 110, 103, 68, 105, 115, 97, 98, 108, 101, 100, 18, 1, 1, 10, 33, 10, 21, 73, 110, 110, + 101, 114, 82, 105, 110, 103, 67, 97, 110, 100, 105, 100, 97, 116, 101, 70, 101, 101, 18, 8, 242, 109, 185, 165, 91, 239, 14, 144, + 10, 27, 10, 22, 77, 97, 105, 110, 116, 101, 110, 97, 110, 99, 101, 77, 111, 100, 101, 65, 108, 108, 111, 119, 101, 100, 18, 1, 1, 10, + 25, 10, 13, 77, 97, 120, 79, 98, 106, 101, 99, 116, 83, 105, 122, 101, 18, 8, 99, 232, 58, 182, 206, 12, 232, 19, 10, 23, + 10, 11, 87, 105, 116, 104, 100, 114, 97, 119, 70, 101, 101, 18, 8, 255, 181, 25, 125, 221, 153, 238, 67, +} + func TestNetworkInfo_CurrentEpoch(t *testing.T) { var x netmap.NetworkInfo - require.Zero(t, x.CurrentEpoch()) const e = 13 - x.SetCurrentEpoch(e) - require.EqualValues(t, e, x.CurrentEpoch()) - var m netmapv2.NetworkInfo - x.WriteToV2(&m) - - require.EqualValues(t, e, m.GetCurrentEpoch()) + const e2 = e + 1 + x.SetCurrentEpoch(e2) + require.EqualValues(t, e2, x.CurrentEpoch()) } func TestNetworkInfo_MagicNumber(t *testing.T) { var x netmap.NetworkInfo - require.Zero(t, x.MagicNumber()) const magic = 321 - x.SetMagicNumber(magic) - require.EqualValues(t, magic, x.MagicNumber()) - var m netmapv2.NetworkInfo - x.WriteToV2(&m) - - require.EqualValues(t, magic, m.GetMagicNumber()) + const magic2 = magic + 1 + x.SetMagicNumber(magic2) + require.EqualValues(t, magic2, x.MagicNumber()) } func TestNetworkInfo_MsPerBlock(t *testing.T) { var x netmap.NetworkInfo - require.Zero(t, x.MsPerBlock()) const ms = 789 - x.SetMsPerBlock(ms) - require.EqualValues(t, ms, x.MsPerBlock()) - var m netmapv2.NetworkInfo - x.WriteToV2(&m) + const ms2 = ms + 1 + x.SetMsPerBlock(ms2) + require.EqualValues(t, ms2, x.MsPerBlock()) +} + +func TestNetworkInfo_SetRawNetworkParameter(t *testing.T) { + var x netmap.NetworkInfo + const k1, v1 = "k1", "v1" + const k2, v2 = "k2", "v2" + + require.Zero(t, x.RawNetworkParameter(k1)) + require.Zero(t, x.RawNetworkParameter(k2)) + x.IterateRawNetworkParameters(func(name string, value []byte) { + t.Fatal("handler must not be called") + }) + + x.SetRawNetworkParameter(k1, []byte(v1)) + x.SetRawNetworkParameter(k2, []byte(v2)) + + require.EqualValues(t, v1, x.RawNetworkParameter(k1)) + require.EqualValues(t, v2, x.RawNetworkParameter(k2)) + var collected [][2]string + x.IterateRawNetworkParameters(func(name string, value []byte) { + collected = append(collected, [2]string{name, string(value)}) + }) + require.ElementsMatch(t, [][2]string{{k1, v1}, {k2, v2}}, collected) - require.EqualValues(t, ms, m.GetMsPerBlock()) } -func testConfigValue(t *testing.T, - getter func(x netmap.NetworkInfo) any, - setter func(x *netmap.NetworkInfo, val any), - val1, val2 any, - v2Key string, v2Val func(val any) []byte, +func testConfigValue[T comparable](t testing.TB, + getter func(netmap.NetworkInfo) T, + setter func(*netmap.NetworkInfo, T), + val1, val2 T, ) { + require.NotEqual(t, val1, val2) var x netmap.NetworkInfo - require.Zero(t, getter(x)) + setter(&x, val1) + require.Equal(t, val1, getter(x)) + setter(&x, val2) + require.Equal(t, val2, getter(x)) +} - checkVal := func(exp any) { - require.EqualValues(t, exp, getter(x)) - - var m netmapv2.NetworkInfo - x.WriteToV2(&m) +func TestNetworkInfo_SetAuditFee(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.AuditFee, (*netmap.NetworkInfo).SetAuditFee, 1, 2) +} - require.EqualValues(t, 1, m.GetNetworkConfig().NumberOfParameters()) - found := false - m.GetNetworkConfig().IterateParameters(func(prm *netmapv2.NetworkParameter) bool { - require.False(t, found) - require.Equal(t, []byte(v2Key), prm.GetKey()) - require.Equal(t, v2Val(exp), prm.GetValue()) - found = true - return false - }) - require.True(t, found) - } +func TestNetworkInfo_SetStoragePrice(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.StoragePrice, (*netmap.NetworkInfo).SetStoragePrice, 1, 2) +} - setter(&x, val1) - checkVal(val1) +func TestNetworkInfo_SetContainerFee(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.ContainerFee, (*netmap.NetworkInfo).SetContainerFee, 1, 2) +} - setter(&x, val2) - checkVal(val2) +func TestNetworkInfo_SetNamedContainerFee(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.NamedContainerFee, (*netmap.NetworkInfo).SetNamedContainerFee, 1, 2) } -func TestNetworkInfo_AuditFee(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.AuditFee() }, - func(info *netmap.NetworkInfo, val any) { info.SetAuditFee(val.(uint64)) }, - uint64(1), uint64(2), - "AuditFee", func(val any) []byte { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, val.(uint64)) - return data - }, - ) +func TestNetworkInfo_SetEigenTrustAlpha(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.EigenTrustAlpha, (*netmap.NetworkInfo).SetEigenTrustAlpha, 0.1, 0.2) + require.Panics(t, func() { new(netmap.NetworkInfo).SetEigenTrustAlpha(-0.5) }) + require.Panics(t, func() { new(netmap.NetworkInfo).SetEigenTrustAlpha(1.5) }) } -func TestNetworkInfo_StoragePrice(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.StoragePrice() }, - func(info *netmap.NetworkInfo, val any) { info.SetStoragePrice(val.(uint64)) }, - uint64(1), uint64(2), - "BasicIncomeRate", func(val any) []byte { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, val.(uint64)) - return data - }, - ) +func TestNetworkInfo_SetNumberOfEigenTrustIterations(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.NumberOfEigenTrustIterations, (*netmap.NetworkInfo).SetNumberOfEigenTrustIterations, 1, 2) } -func TestNetworkInfo_ContainerFee(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.ContainerFee() }, - func(info *netmap.NetworkInfo, val any) { info.SetContainerFee(val.(uint64)) }, - uint64(1), uint64(2), - "ContainerFee", func(val any) []byte { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, val.(uint64)) - return data - }, - ) +func TestNetworkInfo_SetEpochDuration(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.EpochDuration, (*netmap.NetworkInfo).SetEpochDuration, 1, 2) } -func TestNetworkInfo_NamedContainerFee(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.NamedContainerFee() }, - func(info *netmap.NetworkInfo, val any) { info.SetNamedContainerFee(val.(uint64)) }, - uint64(1), uint64(2), - "ContainerAliasFee", func(val any) []byte { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, val.(uint64)) - return data - }, - ) +func TestNetworkInfo_SetIRCandidateFee(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.IRCandidateFee, (*netmap.NetworkInfo).SetIRCandidateFee, 1, 2) } -func TestNetworkInfo_EigenTrustAlpha(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.EigenTrustAlpha() }, - func(info *netmap.NetworkInfo, val any) { info.SetEigenTrustAlpha(val.(float64)) }, - 0.1, 0.2, - "EigenTrustAlpha", func(val any) []byte { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, math.Float64bits(val.(float64))) - return data - }, - ) +func TestNetworkInfo_SetMaxObjectSize(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.IRCandidateFee, (*netmap.NetworkInfo).SetIRCandidateFee, 1, 2) } -func TestNetworkInfo_NumberOfEigenTrustIterations(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.NumberOfEigenTrustIterations() }, - func(info *netmap.NetworkInfo, val any) { info.SetNumberOfEigenTrustIterations(val.(uint64)) }, - uint64(1), uint64(2), - "EigenTrustIterations", func(val any) []byte { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, val.(uint64)) - return data - }, - ) +func TestNetworkInfo_SetWithdrawalFee(t *testing.T) { + testConfigValue(t, netmap.NetworkInfo.WithdrawalFee, (*netmap.NetworkInfo).SetWithdrawalFee, 1, 2) } -func TestNetworkInfo_IRCandidateFee(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.IRCandidateFee() }, - func(info *netmap.NetworkInfo, val any) { info.SetIRCandidateFee(val.(uint64)) }, - uint64(1), uint64(2), - "InnerRingCandidateFee", func(val any) []byte { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, val.(uint64)) - return data - }, - ) +func TestNetworkInfo_DisableHomomorphicHashing(t *testing.T) { + var x netmap.NetworkInfo + require.False(t, x.HomomorphicHashingDisabled()) + x.DisableHomomorphicHashing() + require.True(t, x.HomomorphicHashingDisabled()) } -func TestNetworkInfo_MaxObjectSize(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.MaxObjectSize() }, - func(info *netmap.NetworkInfo, val any) { info.SetMaxObjectSize(val.(uint64)) }, - uint64(1), uint64(2), - "MaxObjectSize", func(val any) []byte { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, val.(uint64)) - return data - }, - ) +func TestNetworkInfo_AllowMaintenanceMode(t *testing.T) { + var x netmap.NetworkInfo + require.False(t, x.MaintenanceModeAllowed()) + x.AllowMaintenanceMode() + require.True(t, x.MaintenanceModeAllowed()) } -func TestNetworkInfo_WithdrawalFee(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.WithdrawalFee() }, - func(info *netmap.NetworkInfo, val any) { info.SetWithdrawalFee(val.(uint64)) }, - uint64(1), uint64(2), - "WithdrawFee", func(val any) []byte { - data := make([]byte, 8) - binary.LittleEndian.PutUint64(data, val.(uint64)) - return data - }, - ) +func setNetworkPrms[T string | []byte](ni *apinetmap.NetworkInfo, els ...T) { + if len(els)%2 != 0 { + panic("must be even") + } + mps := make([]apinetmap.NetworkParameter, len(els)/2) + for i := 0; i < len(els)/2; i++ { + mps[i].SetKey([]byte(els[2*i])) + mps[i].SetValue([]byte(els[2*i+1])) + } + var mc apinetmap.NetworkConfig + mc.SetParameters(mps...) + ni.SetNetworkConfig(&mc) } -func TestNetworkInfo_HomomorphicHashingDisabled(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.HomomorphicHashingDisabled() }, - func(info *netmap.NetworkInfo, val any) { - if val.(bool) { - info.DisableHomomorphicHashing() - } - }, - true, true, // it is impossible to enable hashing - "HomomorphicHashingDisabled", func(val any) []byte { - data := make([]byte, 1) - - if val.(bool) { - data[0] = 1 - } - - return data - }, - ) +func TestNetworkInfo_ReadFromV2(t *testing.T) { + var mps []apinetmap.NetworkParameter + addP := func(k string, v []byte) { + var m apinetmap.NetworkParameter + m.SetKey([]byte(k)) + m.SetValue(v) + mps = append(mps, m) + } + addP("k1", []byte("v1")) + addP("k2", []byte("v2")) + addP("AuditFee", anyValidBinAuditFee) + addP("BasicIncomeRate", anyValidBinStoragePrice) + addP("ContainerAliasFee", anyValidBinNamedContainerFee) + addP("ContainerFee", anyValidBinContainerFee) + addP("EigenTrustAlpha", anyValidBinEigenTrustAlpha) + addP("EigenTrustIterations", anyValidBinEigenTrustIterations) + addP("EpochDuration", anyValidBinEpochDuration) + addP("HomomorphicHashingDisabled", anyValidBinHomoHashDisabled) + addP("InnerRingCandidateFee", anyValidBinIRCandidateFee) + addP("MaintenanceModeAllowed", anyValidBinMaintenanceModeAllowed) + addP("MaxObjectSize", anyValidBinMaxObjectSize) + addP("WithdrawFee", anyValidBinWithdrawalFee) + var mc apinetmap.NetworkConfig + mc.SetParameters(mps...) + var m apinetmap.NetworkInfo + m.SetCurrentEpoch(anyValidCurrentEpoch) + m.SetMagicNumber(anyValidMagicNumber) + m.SetMsPerBlock(anyValidMSPerBlock) + m.SetNetworkConfig(&mc) + + var val netmap.NetworkInfo + require.NoError(t, val.ReadFromV2(m)) + require.EqualValues(t, "v1", val.RawNetworkParameter("k1")) + require.EqualValues(t, "v2", val.RawNetworkParameter("k2")) + require.Equal(t, anyValidCurrentEpoch, val.CurrentEpoch()) + require.Equal(t, anyValidMagicNumber, val.MagicNumber()) + require.Equal(t, anyValidMSPerBlock, val.MsPerBlock()) + require.Equal(t, anyValidAuditFee, val.AuditFee()) + require.Equal(t, anyValidStoragePrice, val.StoragePrice()) + require.Equal(t, anyValidNamedContainerFee, val.NamedContainerFee()) + require.Equal(t, anyValidContainerFee, val.ContainerFee()) + require.Equal(t, anyValidEigenTrustAlpha, val.EigenTrustAlpha()) + require.Equal(t, anyValidEigenTrustIterations, val.NumberOfEigenTrustIterations()) + require.Equal(t, anyValidEpochDuration, val.EpochDuration()) + require.Equal(t, anyValidHomoHashDisabled, val.HomomorphicHashingDisabled()) + require.Equal(t, anyValidIRCandidateFee, val.IRCandidateFee()) + require.Equal(t, anyValidMaintenanceModeAllowed, val.MaintenanceModeAllowed()) + require.Equal(t, anyValidMaxObjectSize, val.MaxObjectSize()) + require.Equal(t, anyValidWithdrawalFee, val.WithdrawalFee()) + + // reset optional fields + mc.SetParameters(mps[0]) + m.SetCurrentEpoch(0) + m.SetMagicNumber(0) + m.SetMsPerBlock(0) + val2 := val + require.NoError(t, val2.ReadFromV2(m)) + require.EqualValues(t, "v1", val.RawNetworkParameter("k1")) + require.Zero(t, val2.RawNetworkParameter("k2")) + require.Zero(t, val2.CurrentEpoch()) + require.Zero(t, val2.MagicNumber()) + require.Zero(t, val2.CurrentEpoch()) + require.Zero(t, val2.AuditFee()) + require.Zero(t, val2.StoragePrice()) + require.Zero(t, val2.NamedContainerFee()) + require.Zero(t, val2.ContainerFee()) + require.Zero(t, val2.EigenTrustAlpha()) + require.Zero(t, val2.NumberOfEigenTrustIterations()) + require.Zero(t, val2.EpochDuration()) + require.Zero(t, val2.HomomorphicHashingDisabled()) + require.Zero(t, val2.IRCandidateFee()) + require.Zero(t, val2.MaintenanceModeAllowed()) + require.Zero(t, val2.MaxObjectSize()) + require.Zero(t, val2.WithdrawalFee()) + + t.Run("invalid", func(t *testing.T) { + for _, tc := range []struct { + name, err string + corrupt func(*apinetmap.NetworkInfo) + }{ + {name: "netconfig/missing", err: "missing network config", + corrupt: func(m *apinetmap.NetworkInfo) { m.SetNetworkConfig(nil) }}, + {name: "netconfig/prms/missing", err: "missing network parameters", + corrupt: func(m *apinetmap.NetworkInfo) { m.SetNetworkConfig(new(apinetmap.NetworkConfig)) }}, + {name: "netconfig/prms/no value", err: "empty attribute value k1", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, "k1", "") }}, + {name: "netconfig/prms/duplicated", err: "duplicated parameter name: k1", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, "k1", "v1", "k2", "v2", "k1", "v3") }}, + {name: "netconfig/prms/eigen trust alpha/overflow", err: "invalid EigenTrustAlpha parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, "EigenTrustAlpha", "123456789") }}, + {name: "netconfig/prms/eigen trust alpha/negative", err: "invalid EigenTrustAlpha parameter: EigenTrust alpha value -0.50 is out of range [0, 1]", + corrupt: func(m *apinetmap.NetworkInfo) { + setNetworkPrms(m, []byte("EigenTrustAlpha"), []byte{0, 0, 0, 0, 0, 0, 224, 191}) + }}, + {name: "netconfig/prms/eigen trust alpha/too big", err: "invalid EigenTrustAlpha parameter: EigenTrust alpha value 1.50 is out of range [0, 1]", + corrupt: func(m *apinetmap.NetworkInfo) { + setNetworkPrms(m, []byte("EigenTrustAlpha"), []byte{0, 0, 0, 0, 0, 0, 248, 63}) + }}, + {name: "netconfig/prms/homo hash disabled/overflow", err: "invalid HomomorphicHashingDisabled parameter: invalid bool parameter contract format too big: integer", + corrupt: func(m *apinetmap.NetworkInfo) { + setNetworkPrms(m, []byte("HomomorphicHashingDisabled"), make([]byte, 33)) + }}, + {name: "netconfig/prms/maintenance allowed/overflow", err: "invalid MaintenanceModeAllowed parameter: invalid bool parameter contract format too big: integer", + corrupt: func(m *apinetmap.NetworkInfo) { + setNetworkPrms(m, []byte("MaintenanceModeAllowed"), make([]byte, 33)) + }}, + {name: "netconfig/prms/audit fee/overflow", err: "invalid AuditFee parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("AuditFee"), make([]byte, 9)) }}, + {name: "netconfig/prms/storage price/overflow", err: "invalid BasicIncomeRate parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("BasicIncomeRate"), make([]byte, 9)) }}, + {name: "netconfig/prms/container fee/overflow", err: "invalid ContainerFee parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("ContainerFee"), make([]byte, 9)) }}, + {name: "netconfig/prms/named container fee/overflow", err: "invalid ContainerAliasFee parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("ContainerAliasFee"), make([]byte, 9)) }}, + {name: "netconfig/prms/eigen trust iterations/overflow", err: "invalid EigenTrustIterations parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("EigenTrustIterations"), make([]byte, 9)) }}, + {name: "netconfig/prms/epoch duration/overflow", err: "invalid EpochDuration parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("EpochDuration"), make([]byte, 9)) }}, + {name: "netconfig/prms/ir candidate fee/overflow", err: "invalid InnerRingCandidateFee parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("InnerRingCandidateFee"), make([]byte, 9)) }}, + {name: "netconfig/prms/max object size/overflow", err: "invalid MaxObjectSize parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("MaxObjectSize"), make([]byte, 9)) }}, + {name: "netconfig/prms/withdrawal fee/overflow", err: "invalid WithdrawFee parameter: invalid uint64 parameter length 9", + corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("WithdrawFee"), make([]byte, 9)) }}, + } { + t.Run(tc.name, func(t *testing.T) { + st := val + var m apinetmap.NetworkInfo + st.WriteToV2(&m) + tc.corrupt(&m) + require.EqualError(t, new(netmap.NetworkInfo).ReadFromV2(m), tc.err) + }) + } + }) } -func TestNetworkInfo_MaintenanceModeAllowed(t *testing.T) { - testConfigValue(t, - func(x netmap.NetworkInfo) any { return x.MaintenanceModeAllowed() }, - func(info *netmap.NetworkInfo, val any) { - if val.(bool) { - info.AllowMaintenanceMode() - } - }, - true, true, - "MaintenanceModeAllowed", func(val any) []byte { - if val.(bool) { - return []byte{1} - } - return []byte{0} - }, - ) +func TestNetworkInfo_WriteToV2(t *testing.T) { + var val netmap.NetworkInfo + var m apinetmap.NetworkInfo + + // zero + val.WriteToV2(&m) + require.Zero(t, m.GetCurrentEpoch()) + require.Zero(t, m.GetMagicNumber()) + require.Zero(t, m.GetMsPerBlock()) + require.Zero(t, m.GetNetworkConfig()) + + // filled + validNetworkInfo.WriteToV2(&m) + require.Equal(t, anyValidCurrentEpoch, m.GetCurrentEpoch()) + require.Equal(t, anyValidMagicNumber, m.GetMagicNumber()) + require.Equal(t, anyValidMSPerBlock, m.GetMsPerBlock()) + mc := m.GetNetworkConfig() + require.NotNil(t, mc) + require.EqualValues(t, 14, mc.NumberOfParameters()) + var collected [][2][]byte + mc.IterateParameters(func(p *apinetmap.NetworkParameter) bool { + collected = append(collected, [2][]byte{p.GetKey(), p.GetValue()}) + return false + }) + require.Len(t, collected, 14) + for i, pair := range [][2]any{ + {"k1", "v1"}, + {"k2", "v2"}, + {"AuditFee", anyValidBinAuditFee}, + {"BasicIncomeRate", anyValidBinStoragePrice}, + {"ContainerAliasFee", anyValidBinNamedContainerFee}, + {"ContainerFee", anyValidBinContainerFee}, + {"EigenTrustAlpha", anyValidBinEigenTrustAlpha}, + {"EigenTrustIterations", anyValidBinEigenTrustIterations}, + {"EpochDuration", anyValidBinEpochDuration}, + {"HomomorphicHashingDisabled", anyValidBinHomoHashDisabled}, + {"InnerRingCandidateFee", anyValidBinIRCandidateFee}, + {"MaintenanceModeAllowed", anyValidBinMaintenanceModeAllowed}, + {"MaxObjectSize", anyValidBinMaxObjectSize}, + {"WithdrawFee", anyValidBinWithdrawalFee}, + } { + require.EqualValues(t, pair[0], collected[i][0]) + require.EqualValues(t, pair[1], collected[i][1]) + } } func TestNetworkInfo_Marshal(t *testing.T) { - v := netmaptest.NetworkInfo() - - var v2 netmap.NetworkInfo - require.NoError(t, v2.Unmarshal(v.Marshal())) + require.Equal(t, validBinNetworkInfo, validNetworkInfo.Marshal()) +} - require.Equal(t, v, v2) +func TestNetworkInfo_Unmarshal(t *testing.T) { + t.Run("invalid", func(t *testing.T) { + t.Run("protobuf", func(t *testing.T) { + err := new(netmap.NetworkInfo).Unmarshal([]byte("Hello, world!")) + require.ErrorContains(t, err, "proto") + require.ErrorContains(t, err, "cannot parse invalid wire-format data") + }) + for _, tc := range []struct { + name string + err string + b []byte + }{ + {name: "netconfig/prms/no value", err: "empty attribute value k1", + b: []byte{34, 6, 10, 4, 10, 2, 107, 49}}, + {name: "netconfig/prms/duplicated", err: "duplicated parameter name: k1", + b: []byte{34, 30, 10, 8, 10, 2, 107, 49, 18, 2, 118, 49, 10, 8, 10, 2, 107, 50, 18, 2, 118, 50, 10, 8, 10, 2, 107, + 49, 18, 2, 118, 51}}, + {name: "netconfig/prms/eigen trust alpha/overflow", err: "invalid EigenTrustAlpha parameter: invalid uint64 parameter length 9", + b: []byte{34, 30, 10, 28, 10, 15, 69, 105, 103, 101, 110, 84, 114, 117, 115, 116, 65, 108, 112, 104, 97, 18, 9, 49, 50, + 51, 52, 53, 54, 55, 56, 57}}, + {name: "netconfig/prms/eigen trust alpha/negative", err: "invalid EigenTrustAlpha parameter: EigenTrust alpha value -0.50 is out of range [0, 1]", + b: []byte{34, 29, 10, 27, 10, 15, 69, 105, 103, 101, 110, 84, 114, 117, 115, 116, 65, 108, 112, 104, 97, 18, 8, 0, 0, + 0, 0, 0, 0, 224, 191}}, + {name: "netconfig/prms/eigen trust alpha/too big", err: "invalid EigenTrustAlpha parameter: EigenTrust alpha value 1.50 is out of range [0, 1]", + b: []byte{34, 29, 10, 27, 10, 15, 69, 105, 103, 101, 110, 84, 114, 117, 115, 116, 65, 108, 112, 104, 97, 18, 8, 0, 0, + 0, 0, 0, 0, 248, 63}}, + {name: "netconfig/prms/homo hash disabled/overflow", err: "invalid HomomorphicHashingDisabled parameter: invalid bool parameter contract format too big: integer", + b: []byte{34, 65, 10, 63, 10, 26, 72, 111, 109, 111, 109, 111, 114, 112, 104, 105, 99, 72, 97, 115, 104, 105, 110, 103, + 68, 105, 115, 97, 98, 108, 101, 100, 18, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "netconfig/prms/maintenance allowed/overflow", err: "invalid MaintenanceModeAllowed parameter: invalid bool parameter contract format too big: integer", + b: []byte{34, 61, 10, 59, 10, 22, 77, 97, 105, 110, 116, 101, 110, 97, 110, 99, 101, 77, 111, 100, 101, 65, 108, 108, 111, + 119, 101, 100, 18, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0}}, + {name: "netconfig/prms/audit fee/overflow", err: "invalid AuditFee parameter: invalid uint64 parameter length 9", + b: []byte{34, 23, 10, 21, 10, 8, 65, 117, 100, 105, 116, 70, 101, 101, 18, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "netconfig/prms/storage price/overflow", err: "invalid BasicIncomeRate parameter: invalid uint64 parameter length 9", + b: []byte{34, 30, 10, 28, 10, 15, 66, 97, 115, 105, 99, 73, 110, 99, 111, 109, 101, 82, 97, 116, 101, 18, 9, 0, 0, 0, 0, + 0, 0, 0, 0, 0}}, + {name: "netconfig/prms/container fee/overflow", err: "invalid ContainerFee parameter: invalid uint64 parameter length 9", + b: []byte{34, 27, 10, 25, 10, 12, 67, 111, 110, 116, 97, 105, 110, 101, 114, 70, 101, 101, 18, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "netconfig/prms/named container fee/overflow", err: "invalid ContainerAliasFee parameter: invalid uint64 parameter length 9", + b: []byte{34, 32, 10, 30, 10, 17, 67, 111, 110, 116, 97, 105, 110, 101, 114, 65, 108, 105, 97, 115, 70, 101, 101, 18, 9, 0, + 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "netconfig/prms/eigen trust iterations/overflow", err: "invalid EigenTrustIterations parameter: invalid uint64 parameter length 9", + b: []byte{34, 35, 10, 33, 10, 20, 69, 105, 103, 101, 110, 84, 114, 117, 115, 116, 73, 116, 101, 114, 97, 116, 105, 111, 110, + 115, 18, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "netconfig/prms/epoch duration/overflow", err: "invalid EpochDuration parameter: invalid uint64 parameter length 9", + b: []byte{34, 28, 10, 26, 10, 13, 69, 112, 111, 99, 104, 68, 117, 114, 97, 116, 105, 111, 110, 18, 9, 0, 0, 0, 0, 0, 0, + 0, 0, 0}}, + {name: "netconfig/prms/ir candidate fee/overflow", err: "invalid InnerRingCandidateFee parameter: invalid uint64 parameter length 9", + b: []byte{34, 36, 10, 34, 10, 21, 73, 110, 110, 101, 114, 82, 105, 110, 103, 67, 97, 110, 100, 105, 100, 97, 116, 101, + 70, 101, 101, 18, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "netconfig/prms/max object size/overflow", err: "invalid MaxObjectSize parameter: invalid uint64 parameter length 9", + b: []byte{34, 28, 10, 26, 10, 13, 77, 97, 120, 79, 98, 106, 101, 99, 116, 83, 105, 122, 101, 18, 9, 0, 0, 0, 0, 0, + 0, 0, 0, 0}}, + {name: "netconfig/prms/withdrawal fee/overflow", err: "invalid WithdrawFee parameter: invalid uint64 parameter length 9", + b: []byte{34, 26, 10, 24, 10, 11, 87, 105, 116, 104, 100, 114, 97, 119, 70, 101, 101, 18, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + } { + t.Run(tc.name, func(t *testing.T) { + require.EqualError(t, new(netmap.NetworkInfo).Unmarshal(tc.b), tc.err) + }) + } + }) + + var val netmap.NetworkInfo + // zero + require.NoError(t, val.Unmarshal(nil)) + require.Zero(t, val.RawNetworkParameter("k1")) + require.Zero(t, val.RawNetworkParameter("k2")) + require.Zero(t, val.CurrentEpoch()) + require.Zero(t, val.MagicNumber()) + require.Zero(t, val.CurrentEpoch()) + require.Zero(t, val.AuditFee()) + require.Zero(t, val.StoragePrice()) + require.Zero(t, val.NamedContainerFee()) + require.Zero(t, val.ContainerFee()) + require.Zero(t, val.EigenTrustAlpha()) + require.Zero(t, val.NumberOfEigenTrustIterations()) + require.Zero(t, val.EpochDuration()) + require.Zero(t, val.HomomorphicHashingDisabled()) + require.Zero(t, val.IRCandidateFee()) + require.Zero(t, val.MaintenanceModeAllowed()) + require.Zero(t, val.MaxObjectSize()) + require.Zero(t, val.WithdrawalFee()) + + // filled + require.NoError(t, val.Unmarshal(validBinNetworkInfo)) + require.EqualValues(t, "v1", val.RawNetworkParameter("k1")) + require.EqualValues(t, "v2", val.RawNetworkParameter("k2")) + require.Equal(t, anyValidCurrentEpoch, val.CurrentEpoch()) + require.Equal(t, anyValidMagicNumber, val.MagicNumber()) + require.Equal(t, anyValidMSPerBlock, val.MsPerBlock()) + require.Equal(t, anyValidAuditFee, val.AuditFee()) + require.Equal(t, anyValidStoragePrice, val.StoragePrice()) + require.Equal(t, anyValidNamedContainerFee, val.NamedContainerFee()) + require.Equal(t, anyValidContainerFee, val.ContainerFee()) + require.Equal(t, anyValidEigenTrustAlpha, val.EigenTrustAlpha()) + require.Equal(t, anyValidEigenTrustIterations, val.NumberOfEigenTrustIterations()) + require.Equal(t, anyValidEpochDuration, val.EpochDuration()) + require.Equal(t, anyValidHomoHashDisabled, val.HomomorphicHashingDisabled()) + require.Equal(t, anyValidIRCandidateFee, val.IRCandidateFee()) + require.Equal(t, anyValidMaintenanceModeAllowed, val.MaintenanceModeAllowed()) + require.Equal(t, anyValidMaxObjectSize, val.MaxObjectSize()) + require.Equal(t, anyValidWithdrawalFee, val.WithdrawalFee()) } diff --git a/netmap/node_info_test.go b/netmap/node_info_test.go index bedd9d16..cab23e00 100644 --- a/netmap/node_info_test.go +++ b/netmap/node_info_test.go @@ -1,88 +1,616 @@ -package netmap +package netmap_test import ( + "strings" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" + apinetmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" + "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/stretchr/testify/require" ) +const ( + anyValidNodePrice = uint64(10993309018040354285) + anyValidNodeCapacity = uint64(9010937245406684209) + anyValidLOCODE = "SE STO" + anyValidCountryCode = "SE" + anyValidCountryName = "Sweden" + anyValidLocationName = "Stockholm" + anyValidSubdivCode = "AB" + anyValidSubdivName = "Stockholms län" + anyValidContinentName = "Europe" + anyValidNodeVersion = "v1.2.3" + anyValidVerifiedNodesDomain = "example.some-org.neofs" +) + +var ( + anyValidPublicKey = []byte{3, 57, 228, 197, 42, 18, 179, 5, 89, 50, 71, 221, 118, 152, 192, 108, 201, 220, 179, 171, 53, 215, + 121, 249, 1, 162, 172, 246, 94, 39, 117, 42, 73} + anyValidNetworkEndpoints = []string{"endpoint1", "endpoint2", "endpoint3"} + anyValidExternalNetworkEndpoints = []string{"ext_endpoint1", "ext_endpoint2", "", "ext_endpoint3"} +) + +// set by init. +var validNodeInfo netmap.NodeInfo + +func init() { + validNodeInfo.SetPublicKey(anyValidPublicKey) + validNodeInfo.SetNetworkEndpoints(anyValidNetworkEndpoints...) + validNodeInfo.SetMaintenance() + validNodeInfo.SetAttribute("k1", "v1") + validNodeInfo.SetAttribute("k2", "v2") + validNodeInfo.SetPrice(anyValidNodePrice) + validNodeInfo.SetCapacity(anyValidNodeCapacity) + validNodeInfo.SetLOCODE(anyValidLOCODE) + validNodeInfo.SetCountryCode(anyValidCountryCode) + validNodeInfo.SetCountryName(anyValidCountryName) + validNodeInfo.SetLocationName(anyValidLocationName) + validNodeInfo.SetSubdivisionCode(anyValidSubdivCode) + validNodeInfo.SetSubdivisionName(anyValidSubdivName) + validNodeInfo.SetContinentName(anyValidContinentName) + validNodeInfo.SetExternalAddresses(anyValidExternalNetworkEndpoints...) + validNodeInfo.SetVersion(anyValidNodeVersion) + validNodeInfo.SetVerifiedNodesDomain(anyValidVerifiedNodesDomain) +} + +// corresponds to validNodeInfo. +var validBinNodeInfo = []byte{ + 10, 33, 3, 57, 228, 197, 42, 18, 179, 5, 89, 50, 71, 221, 118, 152, 192, 108, 201, 220, 179, 171, 53, 215, 121, 249, 1, 162, + 172, 246, 94, 39, 117, 42, 73, 18, 9, 101, 110, 100, 112, 111, 105, 110, 116, 49, 18, 9, 101, 110, 100, 112, 111, 105, 110, 116, 50, + 18, 9, 101, 110, 100, 112, 111, 105, 110, 116, 51, 26, 8, 10, 2, 107, 49, 18, 2, 118, 49, 26, 8, 10, 2, 107, 50, 18, 2, 118, 50, + 26, 29, 10, 5, 80, 114, 105, 99, 101, 18, 20, 49, 48, 57, 57, 51, 51, 48, 57, 48, 49, 56, 48, 52, 48, 51, 53, 52, 50, 56, 53, + 26, 31, 10, 8, 67, 97, 112, 97, 99, 105, 116, 121, 18, 19, 57, 48, 49, 48, 57, 51, 55, 50, 52, 53, 52, 48, 54, 54, 56, 52, 50, + 48, 57, 26, 19, 10, 9, 85, 78, 45, 76, 79, 67, 79, 68, 69, 18, 6, 83, 69, 32, 83, 84, 79, 26, 17, 10, 11, 67, 111, 117, 110, 116, + 114, 121, 67, 111, 100, 101, 18, 2, 83, 69, 26, 17, 10, 7, 67, 111, 117, 110, 116, 114, 121, 18, 6, 83, 119, 101, 100, 101, 110, 26, + 21, 10, 8, 76, 111, 99, 97, 116, 105, 111, 110, 18, 9, 83, 116, 111, 99, 107, 104, 111, 108, 109, 26, 16, 10, 10, 83, 117, 98, 68, 105, + 118, 67, 111, 100, 101, 18, 2, 65, 66, 26, 25, 10, 6, 83, 117, 98, 68, 105, 118, 18, 15, 83, 116, 111, 99, 107, 104, 111, 108, 109, + 115, 32, 108, 195, 164, 110, 26, 19, 10, 9, 67, 111, 110, 116, 105, 110, 101, 110, 116, 18, 6, 69, 117, 114, 111, 112, 101, 26, 58, 10, + 12, 69, 120, 116, 101, 114, 110, 97, 108, 65, 100, 100, 114, 18, 42, 101, 120, 116, 95, 101, 110, 100, 112, 111, 105, 110, 116, 49, + 44, 101, 120, 116, 95, 101, 110, 100, 112, 111, 105, 110, 116, 50, 44, 44, 101, 120, 116, 95, 101, 110, 100, 112, 111, 105, 110, 116, + 51, 26, 17, 10, 7, 86, 101, 114, 115, 105, 111, 110, 18, 6, 118, 49, 46, 50, 46, 51, 26, 45, 10, 19, 86, 101, 114, 105, 102, 105, 101, + 100, 78, 111, 100, 101, 115, 68, 111, 109, 97, 105, 110, 18, 22, 101, 120, 97, 109, 112, 108, 101, 46, 115, 111, 109, 101, 45, 111, + 114, 103, 46, 110, 101, 111, 102, 115, 32, 3, +} + +func TestNodeInfo_SetPublicKey(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.PublicKey()) + + pub := []byte("any_bytes") + n.SetPublicKey(pub) + require.Equal(t, pub, n.PublicKey()) + + otherPub := append(pub, "_other"...) + n.SetPublicKey(otherPub) + require.Equal(t, otherPub, n.PublicKey()) +} + +func TestStringifyPublicKey(t *testing.T) { + var n netmap.NodeInfo + require.Empty(t, netmap.StringifyPublicKey(n)) + + n.SetPublicKey(anyValidPublicKey) + require.Equal(t, "0339e4c52a12b305593247dd7698c06cc9dcb3ab35d779f901a2acf65e27752a49", netmap.StringifyPublicKey(n)) +} + +func TestNodeInfo_SetNetworkEndpoints(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.NumberOfNetworkEndpoints()) + n.IterateNetworkEndpoints(func(string) bool { + t.Fatal("handler must not be called") + return false + }) + + require.EqualValues(t, 3, validNodeInfo.NumberOfNetworkEndpoints()) + var collected []string + validNodeInfo.IterateNetworkEndpoints(func(el string) bool { + collected = append(collected, el) + return false + }) + require.Equal(t, anyValidNetworkEndpoints, collected) +} + +func TestNodeInfo_IterateNetworkEndpoints(t *testing.T) { + var collected []string + validNodeInfo.IterateNetworkEndpoints(func(el string) bool { + collected = append(collected, el) + return len(collected) == 2 + }) + require.Equal(t, anyValidNetworkEndpoints[:2], collected) +} + +func TestIterateNetworkEndpoints(t *testing.T) { + var collected []string + netmap.IterateNetworkEndpoints(validNodeInfo, func(el string) { + collected = append(collected, el) + }) + require.Equal(t, anyValidNetworkEndpoints, collected) +} + +func TestNodeInfo_Hash(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.Hash()) + + require.EqualValues(t, uint64(11151666526377957836), validNodeInfo.Hash()) +} + +func TestNodeInfo_SetPrice(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.Price()) + + n.SetPrice(anyValidNodePrice) + require.EqualValues(t, anyValidNodePrice, n.Price()) + + n.SetPrice(anyValidNodePrice + 1) + require.EqualValues(t, anyValidNodePrice+1, n.Price()) +} + +func TestNodeInfo_SetCapacity(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.Attribute("Capacity")) + + n.SetCapacity(anyValidNodeCapacity) + require.EqualValues(t, "9010937245406684209", n.Attribute("Capacity")) +} + +func TestNodeInfo_SetLOCODE(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.LOCODE()) + + n.SetLOCODE(anyValidLOCODE) + require.Equal(t, anyValidLOCODE, n.LOCODE()) +} + +func TestNodeInfo_SetCountryCode(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.CountryCode()) + + n.SetCountryCode(anyValidCountryCode) + require.Equal(t, anyValidCountryCode, n.CountryCode()) +} + +func TestNodeInfo_SetCountryName(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.CountryName()) + + n.SetCountryName(anyValidCountryName) + require.Equal(t, anyValidCountryName, n.CountryName()) +} + +func TestNodeInfo_SetLocationName(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.LocationName()) + + n.SetLocationName(anyValidLocationName) + require.Equal(t, anyValidLocationName, n.LocationName()) +} + +func TestNodeInfo_SetSubdivisionCode(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.SubdivisionCode()) + + n.SetSubdivisionCode(anyValidSubdivCode) + require.Equal(t, anyValidSubdivCode, n.SubdivisionCode()) +} + +func TestNodeInfo_SetSubdivisionName(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.SubdivisionName()) + + n.SetSubdivisionName(anyValidSubdivName) + require.Equal(t, anyValidSubdivName, n.SubdivisionName()) +} + +func TestNodeInfo_SetContinentName(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.ContinentName()) + + n.SetContinentName(anyValidContinentName) + require.Equal(t, anyValidContinentName, n.ContinentName()) +} + func TestNodeInfo_SetAttribute(t *testing.T) { - var n NodeInfo + var n netmap.NodeInfo + require.Zero(t, n.NumberOfAttributes()) + n.IterateAttributes(func(k, v string) { + t.Fatal("handler must not be called") + }) + require.Panics(t, func() { n.SetAttribute("", "v") }) + require.Panics(t, func() { n.SetAttribute("k", "") }) + + const k1, v1 = "k1", "v1" + const k2, v2 = "k2", "v2" - const key = "some key" - val := "some value" + n.SetAttribute(k1, v1) + n.SetAttribute(k2, v2) + require.Equal(t, v1, n.Attribute(k1)) + require.Equal(t, v2, n.Attribute(k2)) + require.EqualValues(t, 2, n.NumberOfAttributes()) + + var collected []string + n.IterateAttributes(func(k, v string) { + collected = append(collected, k, v) + }) + require.Equal(t, []string{ + k1, v1, + k2, v2, + }, collected) + + n.SetAttribute(k1, v1+"_other") + require.Equal(t, v1+"_other", n.Attribute(k1)) +} - require.Zero(t, n.Attribute(val)) +func TestNodeInfo_SortAttributes(t *testing.T) { + var n netmap.NodeInfo - n.SetAttribute(key, val) - require.Equal(t, val, n.Attribute(key)) + const k1, v1 = "k3", "v3" + const k2, v2 = "k2", "v2" + const k3, v3 = "k1", "v1" + const k4, v4 = "k4", "v4" - val = "some other value" + n.SetAttribute(k1, v1) + n.SetAttribute(k2, v2) + n.SetAttribute(k3, v3) + n.SetAttribute(k4, v4) - n.SetAttribute(key, val) - require.Equal(t, val, n.Attribute(key)) + var collected []string + n.IterateAttributes(func(k, v string) { + collected = append(collected, k, v) + }) + require.Equal(t, []string{ + k1, v1, + k2, v2, + k3, v3, + k4, v4, + }, collected) + + n.SortAttributes() + collected = nil + n.IterateAttributes(func(k, v string) { + collected = append(collected, k, v) + }) + require.Equal(t, []string{ + k3, v3, + k2, v2, + k1, v1, + k4, v4, + }, collected) } -func TestNodeInfo_Status(t *testing.T) { - var n NodeInfo +func testNodeStatusChange(t *testing.T, get func(netmap.NodeInfo) bool, set func(*netmap.NodeInfo), others []func(*netmap.NodeInfo)) { + var n netmap.NodeInfo + require.False(t, get(n)) - require.False(t, n.IsOnline()) - require.False(t, n.IsOffline()) - require.False(t, n.IsMaintenance()) + for _, change := range others { + set(&n) + require.True(t, get(n)) + change(&n) + require.False(t, get(n)) + } +} - n.SetOnline() - require.True(t, n.IsOnline()) - require.False(t, n.IsOffline()) - require.False(t, n.IsMaintenance()) +func TestNodeInfo_SetOffline(t *testing.T) { + testNodeStatusChange(t, netmap.NodeInfo.IsOffline, (*netmap.NodeInfo).SetOffline, []func(*netmap.NodeInfo){ + (*netmap.NodeInfo).SetOnline, + (*netmap.NodeInfo).SetMaintenance, + }) +} - n.SetOffline() - require.True(t, n.IsOffline()) - require.False(t, n.IsOnline()) - require.False(t, n.IsMaintenance()) +func TestNodeInfo_SetOnline(t *testing.T) { + testNodeStatusChange(t, netmap.NodeInfo.IsOnline, (*netmap.NodeInfo).SetOnline, []func(*netmap.NodeInfo){ + (*netmap.NodeInfo).SetOffline, + (*netmap.NodeInfo).SetMaintenance, + }) +} - n.SetMaintenance() - require.True(t, n.IsMaintenance()) - require.False(t, n.IsOnline()) - require.False(t, n.IsOffline()) +func TestNodeInfo_SetMaintenance(t *testing.T) { + testNodeStatusChange(t, netmap.NodeInfo.IsMaintenance, (*netmap.NodeInfo).SetMaintenance, []func(*netmap.NodeInfo){ + (*netmap.NodeInfo).SetOffline, + (*netmap.NodeInfo).SetOnline, + }) } -func TestNodeInfo_ExternalAddr(t *testing.T) { - var n NodeInfo +func TestNodeInfo_SetVersion(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.Attribute("Version")) - require.Empty(t, n.ExternalAddresses()) - require.Panics(t, func() { n.SetExternalAddresses() }) + n.SetVersion(anyValidNodeVersion) + require.Equal(t, anyValidNodeVersion, n.Attribute("Version")) +} - addr := []string{"1", "2", "3"} - n.SetExternalAddresses(addr[0]) - require.Equal(t, addr[:1], n.ExternalAddresses()) +func TestNodeInfo_SetExternalAddresses(t *testing.T) { + var n netmap.NodeInfo + require.Zero(t, n.ExternalAddresses()) - n.SetExternalAddresses(addr[1:]...) - require.Equal(t, addr[1:], n.ExternalAddresses()) + n.SetExternalAddresses(anyValidExternalNetworkEndpoints...) + require.Equal(t, anyValidExternalNetworkEndpoints, n.ExternalAddresses()) } func TestNodeInfo_SetVerifiedNodesDomain(t *testing.T) { - const domain = "example.some-org.neofs" - var n NodeInfo + var n netmap.NodeInfo require.Zero(t, n.VerifiedNodesDomain()) - n.SetVerifiedNodesDomain(domain) - require.Equal(t, domain, n.VerifiedNodesDomain()) + n.SetVerifiedNodesDomain(anyValidVerifiedNodesDomain) + require.Equal(t, anyValidVerifiedNodesDomain, n.VerifiedNodesDomain()) +} + +func setNodeAttributes(ni *apinetmap.NodeInfo, els ...string) { + if len(els)%2 != 0 { + panic("must be even") + } + mas := make([]apinetmap.Attribute, len(els)/2) + for i := 0; i < len(els)/2; i++ { + mas[i].SetKey(els[2*i]) + mas[i].SetValue(els[2*i+1]) + } + ni.SetAttributes(mas) +} + +func TestNodeInfo_ReadFromV2(t *testing.T) { + var mas []apinetmap.Attribute + addAttr := func(k, v string) { + var a apinetmap.Attribute + a.SetKey(k) + a.SetValue(v) + mas = append(mas, a) + } + addAttr("k1", "v1") + addAttr("k2", "v2") + addAttr("Capacity", "9010937245406684209") + addAttr("Price", "10993309018040354285") + addAttr("UN-LOCODE", anyValidLOCODE) + addAttr("CountryCode", anyValidCountryCode) + addAttr("Country", anyValidCountryName) + addAttr("Location", anyValidLocationName) + addAttr("SubDivCode", anyValidSubdivCode) + addAttr("SubDiv", anyValidSubdivName) + addAttr("SubDivName", anyValidSubdivName) + addAttr("Continent", anyValidContinentName) + addAttr("ExternalAddr", strings.Join(anyValidExternalNetworkEndpoints, ",")) + addAttr("Version", anyValidNodeVersion) + addAttr("VerifiedNodesDomain", anyValidVerifiedNodesDomain) + var m apinetmap.NodeInfo + m.SetPublicKey(anyValidPublicKey) + m.SetAddresses(anyValidNetworkEndpoints...) + m.SetAttributes(mas) + + var val netmap.NodeInfo + require.NoError(t, val.ReadFromV2(m)) + require.Equal(t, anyValidPublicKey, val.PublicKey()) + var i int + val.IterateNetworkEndpoints(func(el string) bool { + require.Equal(t, anyValidNetworkEndpoints[i], el) + i++ + return false + }) + require.Len(t, anyValidNetworkEndpoints, i) + for _, checkState := range []func(netmap.NodeInfo) bool{ + netmap.NodeInfo.IsOnline, + netmap.NodeInfo.IsOffline, + netmap.NodeInfo.IsMaintenance, + } { + require.False(t, checkState(val)) + } + require.EqualValues(t, 15, val.NumberOfAttributes()) + require.Equal(t, "v1", val.Attribute("k1")) + require.Equal(t, "v2", val.Attribute("k2")) + require.Equal(t, "9010937245406684209", val.Attribute("Capacity")) + require.Equal(t, anyValidNodePrice, val.Price()) + require.Equal(t, anyValidLOCODE, val.LOCODE()) + require.Equal(t, anyValidCountryCode, val.CountryCode()) + require.Equal(t, anyValidCountryName, val.CountryName()) + require.Equal(t, anyValidLocationName, val.LocationName()) + require.Equal(t, anyValidSubdivCode, val.SubdivisionCode()) + require.Equal(t, anyValidSubdivName, val.SubdivisionName()) + require.Equal(t, anyValidContinentName, val.ContinentName()) + require.Equal(t, anyValidExternalNetworkEndpoints, val.ExternalAddresses()) + require.Equal(t, anyValidNodeVersion, val.Attribute("Version")) + require.Equal(t, anyValidVerifiedNodesDomain, val.VerifiedNodesDomain()) - var msg netmap.NodeInfo - n.WriteToV2(&msg) + for _, tc := range []struct { + st apinetmap.NodeState + check func(netmap.NodeInfo) bool + }{ + {st: apinetmap.Online, check: netmap.NodeInfo.IsOnline}, + {st: apinetmap.Offline, check: netmap.NodeInfo.IsOffline}, + {st: apinetmap.Maintenance, check: netmap.NodeInfo.IsMaintenance}, + } { + m.SetState(tc.st) + require.NoError(t, val.ReadFromV2(m), tc.st) + require.True(t, tc.check(val)) + } - attrFound := false - msgAttrs := msg.GetAttributes() + // reset optional fields + m.SetAttributes(nil) + m.SetState(0) + val2 := val + require.NoError(t, val2.ReadFromV2(m)) + require.Zero(t, val2.NumberOfAttributes()) + val2.IterateAttributes(func(k, v string) { + t.Fatal("handler must not be called") + }) + for _, checkState := range []func(netmap.NodeInfo) bool{ + netmap.NodeInfo.IsOnline, + netmap.NodeInfo.IsOffline, + netmap.NodeInfo.IsMaintenance, + } { + require.False(t, checkState(val2)) + } - for i := range msgAttrs { - if msgAttrs[i].GetKey() == "VerifiedNodesDomain" { - require.False(t, attrFound) - attrFound = true - require.Equal(t, domain, msgAttrs[i].GetValue()) + t.Run("invalid", func(t *testing.T) { + for _, tc := range []struct { + name, err string + corrupt func(info *apinetmap.NodeInfo) + }{ + {name: "public key/nil", err: "missing public key", + corrupt: func(m *apinetmap.NodeInfo) { m.SetPublicKey(nil) }}, + {name: "public key/empty", err: "missing public key", + corrupt: func(m *apinetmap.NodeInfo) { m.SetPublicKey([]byte{}) }}, + {name: "endpoints/empty", err: "missing network endpoints", + corrupt: func(m *apinetmap.NodeInfo) { m.SetAddresses() }}, + {name: "attributes/no key", err: "empty key of the attribute #1", + corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "k1", "v1", "", "v2") }}, + {name: "attributes/no value", err: "empty value of the attribute k2", + corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "k1", "v1", "k2", "") }}, + {name: "attributes/duplicated", err: "duplicated attribute k1", + corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "k1", "v1", "k2", "v2", "k1", "v3") }}, + {name: "attributes/capacity", err: "invalid Capacity attribute: strconv.ParseUint: parsing \"foo\": invalid syntax", + corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "Capacity", "foo") }}, + {name: "attributes/price", err: "invalid Price attribute: strconv.ParseUint: parsing \"foo\": invalid syntax", + corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "Price", "foo") }}, + } { + t.Run(tc.name, func(t *testing.T) { + st := val + var m apinetmap.NodeInfo + st.WriteToV2(&m) + tc.corrupt(&m) + require.EqualError(t, new(netmap.NodeInfo).ReadFromV2(m), tc.err) + }) } + }) +} + +func TestNodeInfo_WriteToV2(t *testing.T) { + var val netmap.NodeInfo + var m apinetmap.NodeInfo + + // zero + val.WriteToV2(&m) + require.Zero(t, m.GetPublicKey()) + require.Zero(t, m.NumberOfAddresses()) + m.IterateAddresses(func(string) bool { t.Fatal("handler must not be called"); return false }) + require.Zero(t, m.GetAttributes()) + require.Zero(t, m.GetState()) + + // filled + validNodeInfo.WriteToV2(&m) + require.Equal(t, anyValidPublicKey, m.GetPublicKey()) + require.EqualValues(t, 3, m.NumberOfAddresses()) + var collected []string + m.IterateAddresses(func(el string) bool { + collected = append(collected, el) + return false + }) + mas := m.GetAttributes() + require.Len(t, mas, 14) + for i, pair := range [][2]string{ + {"k1", "v1"}, + {"k2", "v2"}, + {"Price", "10993309018040354285"}, + {"Capacity", "9010937245406684209"}, + {"UN-LOCODE", anyValidLOCODE}, + {"CountryCode", anyValidCountryCode}, + {"Country", anyValidCountryName}, + {"Location", anyValidLocationName}, + {"SubDivCode", anyValidSubdivCode}, + {"SubDiv", anyValidSubdivName}, + {"Continent", anyValidContinentName}, + {"ExternalAddr", strings.Join(anyValidExternalNetworkEndpoints, ",")}, + {"Version", anyValidNodeVersion}, + {"VerifiedNodesDomain", anyValidVerifiedNodesDomain}, + } { + require.EqualValues(t, pair[0], mas[i].GetKey()) + require.EqualValues(t, pair[1], mas[i].GetValue()) + require.Zero(t, mas[i].GetParents()) + } + + for _, tc := range []struct { + setState func(*netmap.NodeInfo) + exp apinetmap.NodeState + }{ + {setState: (*netmap.NodeInfo).SetOnline, exp: apinetmap.Online}, + {setState: (*netmap.NodeInfo).SetOffline, exp: apinetmap.Offline}, + {setState: (*netmap.NodeInfo).SetMaintenance, exp: apinetmap.Maintenance}, + } { + val2 := validNodeInfo + tc.setState(&val2) + val2.WriteToV2(&m) + require.Equal(t, tc.exp, m.GetState(), tc.exp) } +} + +func TestNodeInfo_Marshal(t *testing.T) { + require.Equal(t, validBinNodeInfo, validNodeInfo.Marshal()) +} + +func TestNodeInfo_Unmarshal(t *testing.T) { + t.Run("invalid", func(t *testing.T) { + t.Run("protobuf", func(t *testing.T) { + err := new(netmap.NodeInfo).Unmarshal([]byte("Hello, world!")) + require.ErrorContains(t, err, "proto") + require.ErrorContains(t, err, "cannot parse invalid wire-format data") + }) + for _, tc := range []struct { + name, err string + b []byte + }{ + {name: "attributes/no key", err: "empty key of the attribute #1", + b: []byte{26, 8, 10, 2, 107, 49, 18, 2, 118, 49, 26, 4, 18, 2, 118, 50}}, + {name: "attributes/no value", err: "empty value of the attribute k2", + b: []byte{26, 8, 10, 2, 107, 49, 18, 2, 118, 49, 26, 4, 10, 2, 107, 50}}, + {name: "attributes/duplicated", err: "duplicated attribute k1", + b: []byte{26, 8, 10, 2, 107, 49, 18, 2, 118, 49, 26, 8, 10, 2, 107, 50, 18, 2, 118, 50, 26, 8, 10, 2, 107, 49, + 18, 2, 118, 51}}, + {name: "attributes/capacity", err: "invalid Capacity attribute: strconv.ParseUint: parsing \"foo\": invalid syntax", + b: []byte{26, 15, 10, 8, 67, 97, 112, 97, 99, 105, 116, 121, 18, 3, 102, 111, 111}}, + {name: "attributes/price", err: "invalid Price attribute: strconv.ParseUint: parsing \"foo\": invalid syntax", + b: []byte{26, 12, 10, 5, 80, 114, 105, 99, 101, 18, 3, 102, 111, 111}}, + } { + t.Run(tc.name, func(t *testing.T) { + require.EqualError(t, new(netmap.NodeInfo).Unmarshal(tc.b), tc.err) + }) + } + }) + + var val netmap.NodeInfo + // zero + require.NoError(t, val.Unmarshal(nil)) + require.Zero(t, val.PublicKey()) + require.Zero(t, val.NumberOfNetworkEndpoints()) + val.IterateNetworkEndpoints(func(el string) bool { t.Fatal("handler must not be called"); return false }) + require.Zero(t, val.NumberOfAttributes()) + val.IterateAttributes(func(k, v string) { t.Fatal("handler must not be called") }) + for _, checkState := range []func(netmap.NodeInfo) bool{ + netmap.NodeInfo.IsOnline, + netmap.NodeInfo.IsOffline, + netmap.NodeInfo.IsMaintenance, + } { + require.False(t, checkState(val)) + } + + // filled + require.NoError(t, val.Unmarshal(validBinNodeInfo)) + require.Equal(t, anyValidPublicKey, val.PublicKey()) + var i int + val.IterateNetworkEndpoints(func(el string) bool { + require.Equal(t, anyValidNetworkEndpoints[i], el) + i++ + return false + }) + require.Len(t, anyValidNetworkEndpoints, i) + require.True(t, val.IsMaintenance()) + require.EqualValues(t, 14, val.NumberOfAttributes()) + require.Equal(t, "v1", val.Attribute("k1")) + require.Equal(t, "v2", val.Attribute("k2")) + require.Equal(t, "9010937245406684209", val.Attribute("Capacity")) + require.Equal(t, anyValidNodePrice, val.Price()) + require.Equal(t, anyValidLOCODE, val.LOCODE()) + require.Equal(t, anyValidCountryCode, val.CountryCode()) + require.Equal(t, anyValidCountryName, val.CountryName()) + require.Equal(t, anyValidLocationName, val.LocationName()) + require.Equal(t, anyValidSubdivCode, val.SubdivisionCode()) + require.Equal(t, anyValidSubdivName, val.SubdivisionName()) + require.Equal(t, anyValidContinentName, val.ContinentName()) + require.Equal(t, anyValidExternalNetworkEndpoints, val.ExternalAddresses()) + require.Equal(t, anyValidNodeVersion, val.Attribute("Version")) + require.Equal(t, anyValidVerifiedNodesDomain, val.VerifiedNodesDomain()) +} + +func TestNodeInfo_MarshalJSON(t *testing.T) { + // TODO +} - require.True(t, attrFound) +func TestNodeInfo_UnmarshalJSON(t *testing.T) { + // TODO }