frostfs-sdk-go/netmap/selector_test.go
Evgenii Stratonikov 3edaf9ecb6 netmap: sort buckets by HRW value
If weights for some buckets are equal, result depends on the
initial positions of buckets in slice. Thus we sort buckets by value
using hash of the first node as a have for the whole bucket.
This is ok, because buckets are already sorted by HRW.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2021-12-28 15:59:35 +03:00

707 lines
20 KiB
Go

package netmap
import (
"errors"
"fmt"
"math/rand"
"sort"
"strconv"
"testing"
"github.com/nspcc-dev/hrw"
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
testv2 "github.com/nspcc-dev/neofs-api-go/v2/netmap/test"
"github.com/stretchr/testify/require"
)
func BenchmarkHRWSort(b *testing.B) {
const netmapSize = 1000
nodes := make([]Nodes, netmapSize)
weights := make([]float64, netmapSize)
for i := range nodes {
nodes[i] = Nodes{{
ID: rand.Uint64(),
Index: i,
Capacity: 100,
Price: 1,
AttrMap: nil,
}}
weights[i] = float64(rand.Uint32()%10) / 10.0
}
pivot := rand.Uint64()
b.Run("sort by index, no weight", func(b *testing.B) {
realNodes := make([]Nodes, netmapSize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
copy(realNodes, nodes)
b.StartTimer()
hrw.SortSliceByIndex(realNodes, pivot)
}
})
b.Run("sort by value, no weight", func(b *testing.B) {
realNodes := make([]Nodes, netmapSize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
copy(realNodes, nodes)
b.StartTimer()
hrw.SortSliceByValue(realNodes, pivot)
}
})
b.Run("only sort by index", func(b *testing.B) {
realNodes := make([]Nodes, netmapSize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
copy(realNodes, nodes)
b.StartTimer()
hrw.SortSliceByWeightIndex(realNodes, weights, pivot)
}
})
b.Run("sort by value", func(b *testing.B) {
realNodes := make([]Nodes, netmapSize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
copy(realNodes, nodes)
b.StartTimer()
hrw.SortSliceByWeightValue(realNodes, weights, pivot)
}
})
b.Run("sort by ID, then by index (deterministic)", func(b *testing.B) {
realNodes := make([]Nodes, netmapSize)
b.ResetTimer()
for i := 0; i < b.N; i++ {
b.StopTimer()
copy(realNodes, nodes)
b.StartTimer()
sort.Slice(nodes, func(i, j int) bool {
return nodes[i][0].ID < nodes[j][0].ID
})
hrw.SortSliceByWeightIndex(realNodes, weights, pivot)
}
})
}
func BenchmarkPolicyHRWType(b *testing.B) {
const netmapSize = 100
p := newPlacementPolicy(1,
[]*Replica{
newReplica(1, "loc1"),
newReplica(1, "loc2")},
[]*Selector{
newSelector("loc1", "Location", ClauseSame, 1, "loc1"),
newSelector("loc2", "Location", ClauseSame, 1, "loc2")},
[]*Filter{
newFilter("loc1", "Location", "Shanghai", OpEQ),
newFilter("loc2", "Location", "Shanghai", OpNE),
})
nodes := make([]NodeInfo, netmapSize)
for i := range nodes {
var loc string
switch i % 20 {
case 0:
loc = "Shanghai"
default:
loc = strconv.Itoa(i % 20)
}
// Having the same price and capacity ensures equal weights for all nodes.
// This way placement is more dependent on the initial order.
nodes[i] = nodeInfoFromAttributes("Location", loc, "Price", "1", "Capacity", "10")
pub := make([]byte, 33)
pub[0] = byte(i)
nodes[i].SetPublicKey(pub)
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(b, err)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, err := nm.GetContainerNodes(p, []byte{1})
if err != nil {
b.Fatal()
}
}
}
func TestPlacementPolicy_DeterministicOrder(t *testing.T) {
const netmapSize = 100
p := newPlacementPolicy(1,
[]*Replica{
newReplica(1, "loc1"),
newReplica(1, "loc2")},
[]*Selector{
newSelector("loc1", "Location", ClauseSame, 1, "loc1"),
newSelector("loc2", "Location", ClauseSame, 1, "loc2")},
[]*Filter{
newFilter("loc1", "Location", "Shanghai", OpEQ),
newFilter("loc2", "Location", "Shanghai", OpNE),
})
nodes := make([]NodeInfo, netmapSize)
for i := range nodes {
var loc string
switch i % 20 {
case 0:
loc = "Shanghai"
default:
loc = strconv.Itoa(i % 20)
}
// Having the same price and capacity ensures equal weights for all nodes.
// This way placement is more dependent on the initial order.
nodes[i] = nodeInfoFromAttributes("Location", loc, "Price", "1", "Capacity", "10")
pub := make([]byte, 33)
pub[0] = byte(i)
nodes[i].SetPublicKey(pub)
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
getIndices := func(t *testing.T) (int, int) {
v, err := nm.GetContainerNodes(p, []byte{1})
require.NoError(t, err)
ns := v.Flatten()
require.Equal(t, 2, len(ns))
return ns[0].Index, ns[1].Index
}
a, b := getIndices(t)
for i := 0; i < 10; i++ {
x, y := getIndices(t)
require.Equal(t, a, x)
require.Equal(t, b, y)
}
}
func TestPlacementPolicy_UnspecifiedClause(t *testing.T) {
p := newPlacementPolicy(1,
[]*Replica{newReplica(1, "X")},
[]*Selector{
newSelector("X", "", ClauseDistinct, 4, "*"),
},
nil,
)
nodes := []NodeInfo{
nodeInfoFromAttributes("ID", "1", "Country", "RU", "City", "St.Petersburg", "SSD", "0"),
nodeInfoFromAttributes("ID", "2", "Country", "RU", "City", "St.Petersburg", "SSD", "1"),
nodeInfoFromAttributes("ID", "3", "Country", "RU", "City", "Moscow", "SSD", "1"),
nodeInfoFromAttributes("ID", "4", "Country", "RU", "City", "Moscow", "SSD", "1"),
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
v, err := nm.GetContainerNodes(p, nil)
require.NoError(t, err)
require.Equal(t, 4, len(v.Flatten()))
}
func TestPlacementPolicy_Minimal(t *testing.T) {
nodes := []NodeInfo{
nodeInfoFromAttributes("City", "Saint-Petersburg"),
nodeInfoFromAttributes("City", "Moscow"),
nodeInfoFromAttributes("City", "Berlin"),
nodeInfoFromAttributes("City", "Paris"),
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
runTest := func(t *testing.T, rep uint32, expectError bool) {
p := newPlacementPolicy(0,
[]*Replica{newReplica(rep, "")},
nil, nil)
v, err := nm.GetContainerNodes(p, nil)
if expectError {
require.Error(t, err)
return
}
require.NoError(t, err)
count := int(rep * defaultCBF)
if count > len(nm.Nodes) {
count = len(nm.Nodes)
}
require.EqualValues(t, count, len(v.Flatten()))
}
t.Run("REP 1", func(t *testing.T) {
runTest(t, 1, false)
})
t.Run("REP 3", func(t *testing.T) {
runTest(t, 3, false)
})
t.Run("REP 5", func(t *testing.T) {
runTest(t, 5, true)
})
}
// Issue #215.
func TestPlacementPolicy_MultipleREP(t *testing.T) {
p := newPlacementPolicy(1,
[]*Replica{
newReplica(1, "LOC_SPB_PLACE"),
newReplica(1, "LOC_MSK_PLACE"),
},
[]*Selector{
newSelector("LOC_SPB_PLACE", "", ClauseUnspecified, 1, "LOC_SPB"),
newSelector("LOC_MSK_PLACE", "", ClauseUnspecified, 1, "LOC_MSK"),
},
[]*Filter{
newFilter("LOC_SPB", "City", "Saint-Petersburg", OpEQ),
newFilter("LOC_MSK", "City", "Moscow", OpEQ),
},
)
nodes := []NodeInfo{
nodeInfoFromAttributes("City", "Saint-Petersburg"),
nodeInfoFromAttributes("City", "Moscow"),
nodeInfoFromAttributes("City", "Berlin"),
nodeInfoFromAttributes("City", "Paris"),
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
v, err := nm.GetContainerNodes(p, nil)
require.NoError(t, err)
rs := v.Replicas()
require.Equal(t, 2, len(rs))
require.Equal(t, 1, len(rs[0]))
require.Equal(t, "Saint-Petersburg", rs[0][0].Attribute("City"))
require.Equal(t, 1, len(rs[1]))
require.Equal(t, "Moscow", rs[1][0].Attribute("City"))
}
func TestPlacementPolicy_DefaultCBF(t *testing.T) {
p := newPlacementPolicy(0,
[]*Replica{
newReplica(1, "EU"),
},
[]*Selector{
newSelector("EU", "Location", ClauseSame, 1, "*"),
},
nil)
nodes := []NodeInfo{
nodeInfoFromAttributes("Location", "Europe", "Country", "RU", "City", "St.Petersburg"),
nodeInfoFromAttributes("Location", "Europe", "Country", "RU", "City", "Moscow"),
nodeInfoFromAttributes("Location", "Europe", "Country", "DE", "City", "Berlin"),
nodeInfoFromAttributes("Location", "Europe", "Country", "FR", "City", "Paris"),
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
v, err := nm.GetContainerNodes(p, nil)
require.NoError(t, err)
require.Equal(t, defaultCBF, len(v.Flatten()))
}
func TestPlacementPolicy_GetPlacementVectors(t *testing.T) {
p := newPlacementPolicy(2,
[]*Replica{
newReplica(1, "SPB"),
newReplica(2, "Americas"),
},
[]*Selector{
newSelector("SPB", "City", ClauseSame, 1, "SPBSSD"),
newSelector("Americas", "City", ClauseDistinct, 2, "Americas"),
},
[]*Filter{
newFilter("SPBSSD", "", "", OpAND,
newFilter("", "Country", "RU", OpEQ),
newFilter("", "City", "St.Petersburg", OpEQ),
newFilter("", "SSD", "1", OpEQ)),
newFilter("Americas", "", "", OpOR,
newFilter("", "Continent", "NA", OpEQ),
newFilter("", "Continent", "SA", OpEQ)),
})
nodes := []NodeInfo{
nodeInfoFromAttributes("ID", "1", "Country", "RU", "City", "St.Petersburg", "SSD", "0"),
nodeInfoFromAttributes("ID", "2", "Country", "RU", "City", "St.Petersburg", "SSD", "1"),
nodeInfoFromAttributes("ID", "3", "Country", "RU", "City", "Moscow", "SSD", "1"),
nodeInfoFromAttributes("ID", "4", "Country", "RU", "City", "Moscow", "SSD", "1"),
nodeInfoFromAttributes("ID", "5", "Country", "RU", "City", "St.Petersburg", "SSD", "1"),
nodeInfoFromAttributes("ID", "6", "Continent", "NA", "City", "NewYork"),
nodeInfoFromAttributes("ID", "7", "Continent", "AF", "City", "Cairo"),
nodeInfoFromAttributes("ID", "8", "Continent", "AF", "City", "Cairo"),
nodeInfoFromAttributes("ID", "9", "Continent", "SA", "City", "Lima"),
nodeInfoFromAttributes("ID", "10", "Continent", "AF", "City", "Cairo"),
nodeInfoFromAttributes("ID", "11", "Continent", "NA", "City", "NewYork"),
nodeInfoFromAttributes("ID", "12", "Continent", "NA", "City", "LosAngeles"),
nodeInfoFromAttributes("ID", "13", "Continent", "SA", "City", "Lima"),
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
v, err := nm.GetContainerNodes(p, nil)
require.NoError(t, err)
require.Equal(t, 2, len(v.Replicas()))
require.Equal(t, 6, len(v.Flatten()))
require.Equal(t, 2, len(v.Replicas()[0]))
ids := map[string]struct{}{}
for _, ni := range v.Replicas()[0] {
require.Equal(t, "RU", ni.Attribute("Country"))
require.Equal(t, "St.Petersburg", ni.Attribute("City"))
require.Equal(t, "1", ni.Attribute("SSD"))
ids[ni.Attribute("ID")] = struct{}{}
}
require.Equal(t, len(v.Replicas()[0]), len(ids), "not all nodes we distinct")
require.Equal(t, 4, len(v.Replicas()[1])) // 2 cities * 2 HRWB
ids = map[string]struct{}{}
for _, ni := range v.Replicas()[1] {
require.Contains(t, []string{"NA", "SA"}, ni.Attribute("Continent"))
ids[ni.Attribute("ID")] = struct{}{}
}
require.Equal(t, len(v.Replicas()[1]), len(ids), "not all nodes we distinct")
}
func TestPlacementPolicy_LowerBound(t *testing.T) {
p := newPlacementPolicy(
2, // backup factor
[]*Replica{
newReplica(1, "X"),
},
[]*Selector{
newSelector("X", "Country", ClauseSame, 2, "*"),
},
nil, // filters
)
nodes := []NodeInfo{
nodeInfoFromAttributes("ID", "1", "Country", "DE"),
nodeInfoFromAttributes("ID", "2", "Country", "DE"),
nodeInfoFromAttributes("ID", "3", "Country", "DE"),
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
v, err := nm.GetContainerNodes(p, nil)
require.NoError(t, err)
require.Equal(t, 3, len(v.Flatten()))
}
func TestIssue213(t *testing.T) {
p := newPlacementPolicy(1,
[]*Replica{
newReplica(4, ""),
},
[]*Selector{
newSelector("", "", ClauseDistinct, 4, "LOC_EU"),
},
[]*Filter{
newFilter("LOC_EU", "Location", "Europe", OpEQ),
})
nodes := []NodeInfo{
nodeInfoFromAttributes("Location", "Europe", "Country", "Russia", "City", "Moscow"),
nodeInfoFromAttributes("Location", "Europe", "Country", "Russia", "City", "Saint-Petersburg"),
nodeInfoFromAttributes("Location", "Europe", "Country", "Sweden", "City", "Stockholm"),
nodeInfoFromAttributes("Location", "Europe", "Country", "Finalnd", "City", "Helsinki"),
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
v, err := nm.GetContainerNodes(p, nil)
require.NoError(t, err)
require.Equal(t, 4, len(v.Flatten()))
}
func TestPlacementPolicy_ProcessSelectors(t *testing.T) {
p := newPlacementPolicy(2, nil,
[]*Selector{
newSelector("SameRU", "City", ClauseSame, 2, "FromRU"),
newSelector("DistinctRU", "City", ClauseDistinct, 2, "FromRU"),
newSelector("Good", "Country", ClauseDistinct, 2, "Good"),
newSelector("Main", "Country", ClauseDistinct, 3, "*"),
},
[]*Filter{
newFilter("FromRU", "Country", "Russia", OpEQ),
newFilter("Good", "Rating", "4", OpGE),
})
nodes := []NodeInfo{
nodeInfoFromAttributes("Country", "Russia", "Rating", "1", "City", "SPB"),
nodeInfoFromAttributes("Country", "Germany", "Rating", "5", "City", "Berlin"),
nodeInfoFromAttributes("Country", "Russia", "Rating", "6", "City", "Moscow"),
nodeInfoFromAttributes("Country", "France", "Rating", "4", "City", "Paris"),
nodeInfoFromAttributes("Country", "France", "Rating", "1", "City", "Lyon"),
nodeInfoFromAttributes("Country", "Russia", "Rating", "5", "City", "SPB"),
nodeInfoFromAttributes("Country", "Russia", "Rating", "7", "City", "Moscow"),
nodeInfoFromAttributes("Country", "Germany", "Rating", "3", "City", "Darmstadt"),
nodeInfoFromAttributes("Country", "Germany", "Rating", "7", "City", "Frankfurt"),
nodeInfoFromAttributes("Country", "Russia", "Rating", "9", "City", "SPB"),
nodeInfoFromAttributes("Country", "Russia", "Rating", "9", "City", "SPB"),
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
c := NewContext(nm)
c.setCBF(p.ContainerBackupFactor())
require.NoError(t, c.processFilters(p))
require.NoError(t, c.processSelectors(p))
for _, s := range p.Selectors() {
sel := c.Selections[s.Name()]
s := c.Selectors[s.Name()]
bucketCount, nodesInBucket := GetNodesCount(p, s)
nodesInBucket *= int(c.cbf)
targ := fmt.Sprintf("selector '%s'", s.Name())
require.Equal(t, bucketCount, len(sel), targ)
for _, res := range sel {
require.Equal(t, nodesInBucket, len(res), targ)
for j := range res {
require.True(t, c.applyFilter(s.Filter(), res[j]), targ)
}
}
}
}
func TestPlacementPolicy_ProcessSelectorsHRW(t *testing.T) {
p := newPlacementPolicy(1, nil,
[]*Selector{
newSelector("Main", "Country", ClauseDistinct, 3, "*"),
}, nil)
// bucket weight order: RU > DE > FR
nodes := []NodeInfo{
nodeInfoFromAttributes("Country", "Germany", AttrPrice, "2", AttrCapacity, "10000"),
nodeInfoFromAttributes("Country", "Germany", AttrPrice, "4", AttrCapacity, "1"),
nodeInfoFromAttributes("Country", "France", AttrPrice, "3", AttrCapacity, "10"),
nodeInfoFromAttributes("Country", "Russia", AttrPrice, "2", AttrCapacity, "10000"),
nodeInfoFromAttributes("Country", "Russia", AttrPrice, "1", AttrCapacity, "10000"),
nodeInfoFromAttributes("Country", "Russia", AttrCapacity, "10000"),
nodeInfoFromAttributes("Country", "France", AttrPrice, "100", AttrCapacity, "1"),
nodeInfoFromAttributes("Country", "France", AttrPrice, "7", AttrCapacity, "10000"),
nodeInfoFromAttributes("Country", "Russia", AttrPrice, "2", AttrCapacity, "1"),
}
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
c := NewContext(nm)
c.setPivot([]byte("containerID"))
c.setCBF(p.ContainerBackupFactor())
c.weightFunc = newWeightFunc(newMaxNorm(10000), newReverseMinNorm(1))
c.aggregator = func() aggregator {
return new(maxAgg)
}
require.NoError(t, c.processFilters(p))
require.NoError(t, c.processSelectors(p))
cnt := c.Selections["Main"]
expected := []Nodes{
{{Index: 4, Capacity: 10000, Price: 1}}, // best RU
{{Index: 0, Capacity: 10000, Price: 2}}, // best DE
{{Index: 7, Capacity: 10000, Price: 7}}, // best FR
}
require.Equal(t, len(expected), len(cnt))
for i := range expected {
require.Equal(t, len(expected[i]), len(cnt[i]))
require.Equal(t, expected[i][0].Index, cnt[i][0].Index)
require.Equal(t, expected[i][0].Capacity, cnt[i][0].Capacity)
require.Equal(t, expected[i][0].Price, cnt[i][0].Price)
}
res, err := nm.GetPlacementVectors(containerNodes(cnt), []byte("objectID"))
require.NoError(t, err)
require.Equal(t, res, cnt)
}
func newMaxNorm(max float64) normalizer {
return &maxNorm{max: max}
}
func TestPlacementPolicy_ProcessSelectorsInvalid(t *testing.T) {
testCases := []struct {
name string
p *PlacementPolicy
err error
}{
{
"MissingSelector",
newPlacementPolicy(2, nil,
[]*Selector{nil},
[]*Filter{}),
ErrMissingField,
},
{
"InvalidFilterReference",
newPlacementPolicy(1, nil,
[]*Selector{newSelector("MyStore", "Country", ClauseDistinct, 1, "FromNL")},
[]*Filter{newFilter("FromRU", "Country", "Russia", OpEQ)}),
ErrFilterNotFound,
},
{
"NotEnoughNodes (backup factor)",
newPlacementPolicy(2, nil,
[]*Selector{newSelector("MyStore", "Country", ClauseDistinct, 2, "FromRU")},
[]*Filter{newFilter("FromRU", "Country", "Russia", OpEQ)}),
ErrNotEnoughNodes,
},
{
"NotEnoughNodes (buckets)",
newPlacementPolicy(1, nil,
[]*Selector{newSelector("MyStore", "Country", ClauseDistinct, 2, "FromRU")},
[]*Filter{newFilter("FromRU", "Country", "Russia", OpEQ)}),
ErrNotEnoughNodes,
},
}
nodes := []NodeInfo{
nodeInfoFromAttributes("Country", "Russia"),
nodeInfoFromAttributes("Country", "Germany"),
nodeInfoFromAttributes(),
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
nm, err := NewNetmap(NodesFromInfo(nodes))
require.NoError(t, err)
c := NewContext(nm)
c.setCBF(tc.p.ContainerBackupFactor())
require.NoError(t, c.processFilters(tc.p))
err = c.processSelectors(tc.p)
require.True(t, errors.Is(err, tc.err), "got: %v", err)
})
}
}
func testSelector() *Selector {
s := new(Selector)
s.SetName("name")
s.SetCount(3)
s.SetFilter("filter")
s.SetAttribute("attribute")
s.SetClause(ClauseDistinct)
return s
}
func TestSelector_Name(t *testing.T) {
s := NewSelector()
name := "some name"
s.SetName(name)
require.Equal(t, name, s.Name())
}
func TestSelector_Count(t *testing.T) {
s := NewSelector()
c := uint32(3)
s.SetCount(c)
require.Equal(t, c, s.Count())
}
func TestSelector_Clause(t *testing.T) {
s := NewSelector()
c := ClauseSame
s.SetClause(c)
require.Equal(t, c, s.Clause())
}
func TestSelector_Attribute(t *testing.T) {
s := NewSelector()
a := "some attribute"
s.SetAttribute(a)
require.Equal(t, a, s.Attribute())
}
func TestSelector_Filter(t *testing.T) {
s := NewSelector()
f := "some filter"
s.SetFilter(f)
require.Equal(t, f, s.Filter())
}
func TestSelectorEncoding(t *testing.T) {
s := newSelector("name", "atte", ClauseSame, 1, "filter")
t.Run("binary", func(t *testing.T) {
data, err := s.Marshal()
require.NoError(t, err)
s2 := NewSelector()
require.NoError(t, s2.Unmarshal(data))
require.Equal(t, s, s2)
})
t.Run("json", func(t *testing.T) {
data, err := s.MarshalJSON()
require.NoError(t, err)
s2 := NewSelector()
require.NoError(t, s2.UnmarshalJSON(data))
require.Equal(t, s, s2)
})
}
func TestSelector_ToV2(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var x *Selector
require.Nil(t, x.ToV2())
})
}
func TestNewSelectorFromV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) {
var x *netmap.Selector
require.Nil(t, NewSelectorFromV2(x))
})
t.Run("from non-nil", func(t *testing.T) {
sV2 := testv2.GenerateSelector(false)
s := NewSelectorFromV2(sV2)
require.Equal(t, sV2, s.ToV2())
})
}
func TestNewSelector(t *testing.T) {
t.Run("default values", func(t *testing.T) {
s := NewSelector()
// check initial values
require.Zero(t, s.Count())
require.Equal(t, ClauseUnspecified, s.Clause())
require.Empty(t, s.Attribute())
require.Empty(t, s.Name())
require.Empty(t, s.Filter())
// convert to v2 message
sV2 := s.ToV2()
require.Zero(t, sV2.GetCount())
require.Equal(t, netmap.UnspecifiedClause, sV2.GetClause())
require.Empty(t, sV2.GetAttribute())
require.Empty(t, sV2.GetName())
require.Empty(t, sV2.GetFilter())
})
}