package netmap

import (
	"cmp"
	"crypto/rand"
	"encoding/binary"
	"fmt"
	mrand "math/rand"
	"reflect"
	"slices"
	"strconv"
	"strings"
	"testing"

	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/netmap"
	"git.frostfs.info/TrueCloudLab/hrw"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func BenchmarkHRWSort(b *testing.B) {
	const netmapSize = 1000

	vectors := make([]nodes, netmapSize)
	weights := make([]float64, netmapSize)
	for i := range vectors {
		key := make([]byte, 33)
		rand.Read(key)

		var node NodeInfo
		node.SetPrice(1)
		node.SetCapacity(100)
		node.SetPublicKey(key)

		vectors[i] = nodes{node}
		weights[i] = float64(mrand.Uint32()%10) / 10.0
	}

	pivot := mrand.Uint64()
	b.Run("sort by index, no weight", func(b *testing.B) {
		realNodes := make([]nodes, netmapSize)
		b.ResetTimer()
		for range b.N {
			b.StopTimer()
			copy(realNodes, vectors)
			b.StartTimer()

			hrw.SortSliceByIndex(realNodes, pivot)
		}
	})
	b.Run("sort by value, no weight", func(b *testing.B) {
		realNodes := make([]nodes, netmapSize)
		b.ResetTimer()
		for range b.N {
			b.StopTimer()
			copy(realNodes, vectors)
			b.StartTimer()

			hrw.SortHasherSliceByValue(realNodes, pivot)
		}
	})
	b.Run("only sort by index", func(b *testing.B) {
		realNodes := make([]nodes, netmapSize)
		b.ResetTimer()
		for range b.N {
			b.StopTimer()
			copy(realNodes, vectors)
			b.StartTimer()

			hrw.SortSliceByWeightIndex(realNodes, weights, pivot)
		}
	})
	b.Run("sort by value", func(b *testing.B) {
		realNodes := make([]nodes, netmapSize)
		b.ResetTimer()
		for range b.N {
			b.StopTimer()
			copy(realNodes, vectors)
			b.StartTimer()

			hrw.SortHasherSliceByWeightValue(realNodes, weights, pivot)
		}
	})
	b.Run("sort by ID, then by index (deterministic)", func(b *testing.B) {
		realNodes := make([]nodes, netmapSize)
		b.ResetTimer()
		for range b.N {
			b.StopTimer()
			copy(realNodes, vectors)
			b.StartTimer()

			slices.SortFunc(vectors, func(vi, vj nodes) int {
				return cmp.Compare(vi[0].Hash(), vj[0].Hash())
			})
			hrw.SortSliceByWeightIndex(realNodes, weights, pivot)
		}
	})
}

func BenchmarkPolicyHRWType(b *testing.B) {
	const netmapSize = 100

	p := newPlacementPolicy(1,
		[]ReplicaDescriptor{
			newReplica(1, "loc1"),
			newReplica(1, "loc2"),
		},
		[]Selector{
			newSelector("loc1", "Location", 1, "loc1", (*Selector).SelectSame),
			newSelector("loc2", "Location", 1, "loc2", (*Selector).SelectSame),
		},
		[]Filter{
			newFilter("loc1", "Location", "Shanghai", netmap.EQ),
			newFilter("loc2", "Location", "Shanghai", netmap.NE),
		})

	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)
	}

	var nm NetMap
	nm.SetNodes(nodes)

	b.ResetTimer()
	for range b.N {
		_, err := nm.ContainerNodes(p, []byte{1})
		if err != nil {
			b.Fatal()
		}
	}
}

func TestPlacementPolicy_DeterministicOrder(t *testing.T) {
	const netmapSize = 100

	p := newPlacementPolicy(1,
		[]ReplicaDescriptor{
			newReplica(1, "loc1"),
			newReplica(1, "loc2"),
		},
		[]Selector{
			newSelector("loc1", "Location", 1, "loc1", (*Selector).SelectSame),
			newSelector("loc2", "Location", 1, "loc2", (*Selector).SelectSame),
		},
		[]Filter{
			newFilter("loc1", "Location", "Shanghai", netmap.EQ),
			newFilter("loc2", "Location", "Shanghai", netmap.NE),
		})

	nodeList := make([]NodeInfo, netmapSize)
	for i := range nodeList {
		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.
		nodeList[i] = nodeInfoFromAttributes("Location", loc, "Price", "1", "Capacity", "10")
		pub := make([]byte, 33)
		pub[0] = byte(i)
		nodeList[i].SetPublicKey(pub)
	}

	var nm NetMap
	nm.SetNodes(nodeList)

	getIndices := func(t *testing.T) (uint64, uint64) {
		v, err := nm.ContainerNodes(p, []byte{1})
		require.NoError(t, err)

		nss := make([]nodes, len(v))
		for i := range v {
			nss[i] = v[i]
		}

		ns := flattenNodes(nss)
		require.Equal(t, 2, len(ns))
		return ns[0].Hash(), ns[1].Hash()
	}

	a, b := getIndices(t)
	for range 10 {
		x, y := getIndices(t)
		require.Equal(t, a, x)
		require.Equal(t, b, y)
	}
}

func TestPlacementPolicy_ProcessSelectors(t *testing.T) {
	p := newPlacementPolicy(2, nil,
		[]Selector{
			newSelector("SameRU", "City", 2, "FromRU", (*Selector).SelectSame),
			newSelector("DistinctRU", "City", 2, "FromRU", (*Selector).SelectDistinct),
			newSelector("Good", "Country", 2, "Good", (*Selector).SelectDistinct),
			newSelector("Main", "Country", 3, "*", (*Selector).SelectDistinct),
		},
		[]Filter{
			newFilter("FromRU", "Country", "Russia", netmap.EQ),
			newFilter("Good", "Rating", "4", netmap.GE),
		})
	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"),
	}

	var nm NetMap
	nm.SetNodes(nodes)
	c := newContext(nm)
	c.setCBF(p.backupFactor)
	require.NoError(t, c.processFilters(p))
	require.NoError(t, c.processSelectors(p))

	for _, s := range p.selectors {
		sel := c.selections[s.GetName()]
		s := c.processedSelectors[s.GetName()]
		bucketCount, nodesInBucket := calcNodesCount(*s)
		nodesInBucket *= int(c.cbf)
		targ := fmt.Sprintf("selector '%s'", s.GetName())
		require.Equal(t, bucketCount, len(sel), targ)
		fName := s.GetFilter()
		for _, res := range sel {
			require.Equal(t, nodesInBucket, len(res), targ)
			for j := range res {
				require.True(t, fName == mainFilterName || c.match(c.processedFilters[fName], res[j]), targ)
			}
		}
	}
}

func TestPlacementPolicy_Like(t *testing.T) {
	nodes := []NodeInfo{
		nodeInfoFromAttributes("Country", "Russia"),
		nodeInfoFromAttributes("Country", "Germany"),
		nodeInfoFromAttributes("Country", "Belarus"),
	}

	var nm NetMap
	nm.SetNodes(nodes)

	t.Run("LIKE all", func(t *testing.T) {
		ssNamed := []Selector{newSelector("X", "Country", 4, "FromRU", (*Selector).SelectDistinct)}
		fsNamed := []Filter{newFilter("FromRU", "Country", "*", netmap.LIKE)}
		rsNamed := []ReplicaDescriptor{newReplica(4, "X")}
		pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed)

		n, err := nm.ContainerNodes(pNamed, []byte{1})
		require.NoError(t, err)

		require.Equal(t, 3, len(n[0]))
		for _, n := range n[0] {
			require.True(t, strings.Contains("GermanyRussiaBelarus", n.Attribute("Country")))
		}
	})

	t.Run("LIKE no wildcard", func(t *testing.T) {
		ssNamed := []Selector{newSelector("X", "Country", 1, "FromRU", (*Selector).SelectDistinct)}
		fsNamed := []Filter{newFilter("FromRU", "Country", "Russia", netmap.LIKE)}
		rsNamed := []ReplicaDescriptor{newReplica(1, "X")}
		pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed)

		n, err := nm.ContainerNodes(pNamed, []byte{1})
		require.NoError(t, err)

		require.Equal(t, 1, len(n[0]))
		for _, n := range n[0] {
			require.True(t, n.Attribute("Country") == "Russia")
		}
	})

	t.Run("LIKE prefix", func(t *testing.T) {
		ssNamed := []Selector{newSelector("X", "Country", 1, "FromRU", (*Selector).SelectDistinct)}
		fsNamed := []Filter{newFilter("FromRU", "Country", "Ge*", netmap.LIKE)}
		rsNamed := []ReplicaDescriptor{newReplica(1, "X")}
		pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed)

		n, err := nm.ContainerNodes(pNamed, []byte{1})
		require.NoError(t, err)

		require.Equal(t, 1, len(n[0]))
		for _, n := range n[0] {
			require.True(t, n.Attribute("Country") == "Germany")
		}
	})

	t.Run("LIKE suffix", func(t *testing.T) {
		ssNamed := []Selector{newSelector("X", "Country", 1, "FromRU", (*Selector).SelectDistinct)}
		fsNamed := []Filter{newFilter("FromRU", "Country", "*sia", netmap.LIKE)}
		rsNamed := []ReplicaDescriptor{newReplica(1, "X")}
		pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed)

		n, err := nm.ContainerNodes(pNamed, []byte{1})
		require.NoError(t, err)

		require.Equal(t, 1, len(n[0]))
		for _, n := range n[0] {
			require.True(t, n.Attribute("Country") == "Russia")
		}
	})

	t.Run("LIKE middle", func(t *testing.T) {
		ssNamed := []Selector{newSelector("X", "Country", 2, "FromRU", (*Selector).SelectDistinct)}
		fsNamed := []Filter{newFilter("FromRU", "Country", "*us*", netmap.LIKE)}
		rsNamed := []ReplicaDescriptor{newReplica(2, "X")}
		pNamed := newPlacementPolicy(0, rsNamed, ssNamed, fsNamed)

		n, err := nm.ContainerNodes(pNamed, []byte{1})
		require.NoError(t, err)

		require.Equal(t, 2, len(n[0]))
		for _, n := range n[0] {
			require.True(t, strings.Contains("RussiaBelarus", n.Attribute("Country")))
		}
	})
}

func TestPlacementPolicy_Unique(t *testing.T) {
	p := newPlacementPolicy(2,
		[]ReplicaDescriptor{
			newReplica(1, "S"),
			newReplica(1, "S"),
		},
		[]Selector{
			newSelector("S", "City", 1, "*", (*Selector).SelectSame),
		},
		[]Filter{})
	p.unique = true

	var nodes []NodeInfo
	for i, city := range []string{"Moscow", "Berlin", "Shenzhen"} {
		for j := range 3 {
			node := nodeInfoFromAttributes("City", city)
			node.SetPublicKey(binary.BigEndian.AppendUint16(nil, uint16(i*4+j)))
			nodes = append(nodes, node)
		}
	}

	var nm NetMap
	nm.SetNodes(nodes)

	v, err := nm.ContainerNodes(p, nil)
	require.NoError(t, err)
	for i, vi := range v {
		for _, ni := range vi {
			for j := range i {
				for _, nj := range v[j] {
					require.NotEqual(t, ni.hash, nj.hash)
				}
			}
		}
	}
}

func TestPlacementPolicy_SingleOmitNames(t *testing.T) {
	nodes := []NodeInfo{
		nodeInfoFromAttributes("ID", "1", "Country", "Russia", "City", "SPB"),
		nodeInfoFromAttributes("ID", "2", "Country", "Germany", "City", "Berlin"),
		nodeInfoFromAttributes("ID", "3", "Country", "Russia", "City", "Moscow"),
		nodeInfoFromAttributes("ID", "4", "Country", "France", "City", "Paris"),
		nodeInfoFromAttributes("ID", "5", "Country", "France", "City", "Lyon"),
		nodeInfoFromAttributes("ID", "6", "Country", "Russia", "City", "SPB"),
		nodeInfoFromAttributes("ID", "7", "Country", "Russia", "City", "Moscow"),
		nodeInfoFromAttributes("ID", "8", "Country", "Germany", "City", "Darmstadt"),
		nodeInfoFromAttributes("ID", "9", "Country", "Germany", "City", "Frankfurt"),
		nodeInfoFromAttributes("ID", "10", "Country", "Russia", "City", "SPB"),
		nodeInfoFromAttributes("ID", "11", "Country", "Russia", "City", "Moscow"),
		nodeInfoFromAttributes("ID", "12", "Country", "Germany", "City", "London"),
	}
	for i := range nodes {
		pub := make([]byte, 33)
		rand.Read(pub)
		nodes[i].SetPublicKey(pub)
	}

	var nm NetMap
	nm.SetNodes(nodes)

	for _, unique := range []bool{false, true} {
		t.Run(fmt.Sprintf("unique=%t", unique), func(t *testing.T) {
			ssNamed := []Selector{newSelector("X", "City", 2, "FromRU", (*Selector).SelectDistinct)}
			fsNamed := []Filter{newFilter("FromRU", "Country", "Russia", netmap.EQ)}
			rsNamed := []ReplicaDescriptor{newReplica(1, "X")}
			pNamed := newPlacementPolicy(3, rsNamed, ssNamed, fsNamed)
			pNamed.unique = unique

			vNamed, err := nm.ContainerNodes(pNamed, []byte{1})
			require.NoError(t, err)

			ssUnnamed := []Selector{newSelector("", "City", 2, "FromRU", (*Selector).SelectDistinct)}
			fsUnnamed := []Filter{newFilter("FromRU", "Country", "Russia", netmap.EQ)}
			rsUnnamed := []ReplicaDescriptor{newReplica(1, "")}
			pUnnamed := newPlacementPolicy(3, rsUnnamed, ssUnnamed, fsUnnamed)
			pUnnamed.unique = unique

			vUnnamed, err := nm.ContainerNodes(pUnnamed, []byte{1})
			require.NoError(t, err)

			require.Equal(t, vNamed, vUnnamed)
		})
	}
}

func TestPlacementPolicy_MultiREP(t *testing.T) {
	nodes := []NodeInfo{
		nodeInfoFromAttributes("ID", "1", "Country", "Russia", "City", "SPB"),
		nodeInfoFromAttributes("ID", "2", "Country", "Germany", "City", "Berlin"),
		nodeInfoFromAttributes("ID", "3", "Country", "Russia", "City", "Moscow"),
		nodeInfoFromAttributes("ID", "4", "Country", "France", "City", "Paris"),
		nodeInfoFromAttributes("ID", "5", "Country", "France", "City", "Lyon"),
		nodeInfoFromAttributes("ID", "6", "Country", "Russia", "City", "SPB"),
		nodeInfoFromAttributes("ID", "7", "Country", "Russia", "City", "Moscow"),
		nodeInfoFromAttributes("ID", "8", "Country", "Germany", "City", "Darmstadt"),
		nodeInfoFromAttributes("ID", "9", "Country", "Germany", "City", "Frankfurt"),
		nodeInfoFromAttributes("ID", "10", "Country", "Russia", "City", "SPB"),
		nodeInfoFromAttributes("ID", "11", "Country", "Russia", "City", "Moscow"),
		nodeInfoFromAttributes("ID", "12", "Country", "Germany", "City", "London"),
	}
	for i := range nodes {
		pub := make([]byte, 33)
		rand.Read(pub)
		nodes[i].SetPublicKey(pub)
	}

	var nm NetMap
	nm.SetNodes(nodes)

	ss := []Selector{newSelector("SameRU", "City", 2, "FromRU", (*Selector).SelectDistinct)}
	fs := []Filter{newFilter("FromRU", "Country", "Russia", netmap.EQ)}

	for _, unique := range []bool{false, true} {
		for _, additional := range []int{0, 1, 2} {
			t.Run(fmt.Sprintf("unique=%t, additional=%d", unique, additional), func(t *testing.T) {
				rs := []ReplicaDescriptor{newReplica(1, "SameRU")}
				for range additional {
					rs = append(rs, newReplica(1, ""))
				}

				p := newPlacementPolicy(3, rs, ss, fs)
				p.unique = unique

				v, err := nm.ContainerNodes(p, []byte{1})
				require.NoError(t, err)
				require.Equal(t, 1+additional, len(v))
				require.Equal(t, 6, len(v[0]))

				for i := 1; i < additional; i++ {
					require.Equal(t, 3, len(v[i]))
					if !unique {
						require.Equal(t, v[1], v[i])
					}
				}

				if unique {
					seen := make(map[string]bool)
					for i := range v {
						for j := range v[i] {
							attr := v[i][j].Attribute("ID")
							require.NotEmpty(t, attr)
							require.False(t, seen[attr])

							seen[attr] = true
						}
					}
				}
			})
		}
	}
}

func TestPlacementPolicy_ProcessSelectorsExceptForNodes(t *testing.T) {
	p := newPlacementPolicy(1, nil,
		[]Selector{
			newSelector("ExceptRU", "City", 2, "ExceptRU", (*Selector).SelectSame),
		},
		[]Filter{
			newFilter("ExceptRU", "", "", netmap.NOT,
				newFilter("", "", "", netmap.AND,
					newFilter("", "City", "Lyon", netmap.EQ),
					newFilter("", "Rating", "10", netmap.LE),
				),
			),
		})
	nodes := []NodeInfo{
		nodeInfoFromAttributes("Country", "Germany", "Rating", "1", "City", "Berlin"),
		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", "France", "Rating", "5", "City", "Lyon"),
		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"),
	}

	var nm NetMap
	nm.SetNodes(nodes)
	c := newContext(nm)
	c.setCBF(p.backupFactor)
	require.NoError(t, c.processFilters(p))
	require.NoError(t, c.processSelectors(p))

	for _, s := range p.selectors {
		sel := c.selections[s.GetName()]
		s := c.processedSelectors[s.GetName()]
		bucketCount, nodesInBucket := calcNodesCount(*s)
		nodesInBucket *= int(c.cbf)
		targ := fmt.Sprintf("selector '%s'", s.GetName())
		require.Equal(t, bucketCount, len(sel), targ)
		fName := s.GetFilter()
		for _, res := range sel {
			require.Equal(t, nodesInBucket, len(res), targ)
			for j := range res {
				require.True(t, fName == mainFilterName || c.match(c.processedFilters[fName], res[j]), targ)
			}
		}
	}
}

func TestPlacementPolicy_NonAsciiAttributes(t *testing.T) {
	p := newPlacementPolicy(
		1,
		[]ReplicaDescriptor{
			newReplica(2, "Nodes"),
			newReplica(2, "Nodes"),
		},
		[]Selector{
			newSelector("Nodes", "Цвет", 2, "Colorful", (*Selector).SelectSame),
		},
		[]Filter{
			newFilter("Colorful", "", "", netmap.OR,
				newFilter("", "Цвет", "Красный", netmap.EQ),
				newFilter("", "Цвет", "Синий", netmap.EQ),
			),
		},
	)
	p.SetUnique(true)

	nodes := []NodeInfo{
		nodeInfoFromAttributes("Цвет", "Красный", "Форма", "Треугольник"),
		nodeInfoFromAttributes("Цвет", "Красный", "Форма", "Круг"),
		nodeInfoFromAttributes("Цвет", "Синий", "Форма", "Треугольник"),
		nodeInfoFromAttributes("Цвет", "Синий", "Форма", "Круг"),
		nodeInfoFromAttributes("Свойство", "Мягкий", "Форма", "Треугольник"),
		nodeInfoFromAttributes("Свойство", "Теплый", "Форма", "Круг"),
	}
	for i := range nodes {
		nodes[i].SetPublicKey([]byte{byte(i)})
	}

	redNodes := nodes[:2]
	blueNodes := nodes[2:4]

	var nm NetMap
	nm.SetNodes(nodes)

	pivot := make([]byte, 42)
	_, _ = rand.Read(pivot)

	nodesPerReplica, err := nm.ContainerNodes(p, pivot)
	require.NoError(t, err)
	require.Len(t, nodesPerReplica, 2)

	for i := range nodesPerReplica {
		slices.SortFunc(nodesPerReplica[i], func(n1, n2 NodeInfo) int {
			pk1, pk2 := string(n1.PublicKey()), string(n2.PublicKey())
			return cmp.Compare(pk1, pk2)
		})
	}

	redMatchFirst := reflect.DeepEqual(redNodes, nodesPerReplica[0])
	blueMatchFirst := reflect.DeepEqual(blueNodes, nodesPerReplica[0])

	redMatchSecond := reflect.DeepEqual(redNodes, nodesPerReplica[1])
	blueMatchSecond := reflect.DeepEqual(blueNodes, nodesPerReplica[1])

	assert.True(t, redMatchFirst && blueMatchSecond || blueMatchFirst && redMatchSecond)
}

func TestSelector_SetName(t *testing.T) {
	const name = "some name"
	var s Selector

	require.Zero(t, s.m.GetName())

	s.SetName(name)
	require.Equal(t, name, s.m.GetName())
}

func TestSelector_SetNumberOfNodes(t *testing.T) {
	const num = 3
	var s Selector

	require.Zero(t, s.m.GetCount())

	s.SetNumberOfNodes(num)

	require.EqualValues(t, num, s.m.GetCount())
}

func TestSelectorClauses(t *testing.T) {
	var s Selector

	require.Equal(t, netmap.UnspecifiedClause, s.m.GetClause())

	s.SelectDistinct()
	require.Equal(t, netmap.Distinct, s.m.GetClause())

	s.SelectSame()
	require.Equal(t, netmap.Same, s.m.GetClause())
}

func TestSelector_SelectByBucketAttribute(t *testing.T) {
	const attr = "some attribute"
	var s Selector

	require.Zero(t, s.m.GetAttribute())

	s.SelectByBucketAttribute(attr)
	require.Equal(t, attr, s.m.GetAttribute())
}

func TestSelector_SetFilterName(t *testing.T) {
	const fName = "some filter"
	var s Selector

	require.Zero(t, s.m.GetFilter())

	s.SetFilterName(fName)
	require.Equal(t, fName, s.m.GetFilter())
}