package policy import ( "errors" "testing" "github.com/nspcc-dev/neofs-api-go/v2/netmap" "github.com/stretchr/testify/require" ) func TestSimple(t *testing.T) { q := `REP 3` expected := new(netmap.PlacementPolicy) expected.SetFilters([]*netmap.Filter{}) expected.SetSelectors([]*netmap.Selector{}) expected.SetReplicas([]*netmap.Replica{newReplica("", 3)}) r, err := Parse(q) require.NoError(t, err) require.Equal(t, expected, r) } func TestSimpleWithHRWB(t *testing.T) { q := `REP 3 CBF 4` expected := new(netmap.PlacementPolicy) expected.SetFilters([]*netmap.Filter{}) expected.SetSelectors([]*netmap.Selector{}) expected.SetReplicas([]*netmap.Replica{newReplica("", 3)}) expected.SetContainerBackupFactor(4) r, err := Parse(q) require.NoError(t, err) require.Equal(t, expected, r) } func TestFromSelect(t *testing.T) { q := `REP 1 IN SPB SELECT 1 IN City FROM * AS SPB` expected := new(netmap.PlacementPolicy) expected.SetFilters([]*netmap.Filter{}) expected.SetSelectors([]*netmap.Selector{ newSelector(1, netmap.UnspecifiedClause, "City", "*", "SPB"), }) expected.SetReplicas([]*netmap.Replica{newReplica("SPB", 1)}) r, err := Parse(q) require.NoError(t, err) require.Equal(t, expected, r) } func TestFromSelectClause(t *testing.T) { q := `REP 4 SELECT 3 IN Country FROM * SELECT 2 IN SAME City FROM * SELECT 1 IN DISTINCT Continent FROM *` expected := new(netmap.PlacementPolicy) expected.SetFilters([]*netmap.Filter{}) expected.SetSelectors([]*netmap.Selector{ newSelector(3, netmap.UnspecifiedClause, "Country", "*", ""), newSelector(2, netmap.Same, "City", "*", ""), newSelector(1, netmap.Distinct, "Continent", "*", ""), }) expected.SetReplicas([]*netmap.Replica{newReplica("", 4)}) r, err := Parse(q) require.NoError(t, err) require.Equal(t, expected, r) } func TestSimpleFilter(t *testing.T) { q := `REP 1 SELECT 1 IN City FROM Good FILTER Rating GT 7 AS Good` expected := new(netmap.PlacementPolicy) expected.SetReplicas([]*netmap.Replica{newReplica("", 1)}) expected.SetSelectors([]*netmap.Selector{ newSelector(1, netmap.UnspecifiedClause, "City", "Good", ""), }) expected.SetFilters([]*netmap.Filter{ newFilter("Good", "Rating", "7", netmap.GT), }) r, err := Parse(q) require.NoError(t, err) require.Equal(t, expected, r) } func TestFilterReference(t *testing.T) { q := `REP 1 SELECT 2 IN City FROM Good FILTER Country EQ "RU" AS FromRU FILTER @FromRU AND Rating GT 7 AS Good` expected := new(netmap.PlacementPolicy) expected.SetReplicas([]*netmap.Replica{newReplica("", 1)}) expected.SetSelectors([]*netmap.Selector{ newSelector(2, netmap.UnspecifiedClause, "City", "Good", ""), }) expected.SetFilters([]*netmap.Filter{ newFilter("FromRU", "Country", "RU", netmap.EQ), newFilter("Good", "", "", netmap.AND, newFilter("FromRU", "", "", netmap.UnspecifiedOperation), newFilter("", "Rating", "7", netmap.GT)), }) r, err := Parse(q) require.NoError(t, err) require.Equal(t, expected, r) } func TestFilterOps(t *testing.T) { q := `REP 1 SELECT 2 IN City FROM Good FILTER A GT 1 AND B GE 2 AND C LT 3 AND D LE 4 AND E EQ 5 AND F NE 6 AS Good` expected := new(netmap.PlacementPolicy) expected.SetReplicas([]*netmap.Replica{newReplica("", 1)}) expected.SetSelectors([]*netmap.Selector{ newSelector(2, netmap.UnspecifiedClause, "City", "Good", ""), }) expected.SetFilters([]*netmap.Filter{ newFilter("Good", "", "", netmap.AND, newFilter("", "A", "1", netmap.GT), newFilter("", "B", "2", netmap.GE), newFilter("", "C", "3", netmap.LT), newFilter("", "D", "4", netmap.LE), newFilter("", "E", "5", netmap.EQ), newFilter("", "F", "6", netmap.NE)), }) r, err := Parse(q) require.NoError(t, err) require.Equal(t, expected, r) } func TestWithFilterPrecedence(t *testing.T) { q := `REP 7 IN SPB SELECT 1 IN City FROM SPBSSD AS SPB FILTER City EQ "SPB" AND SSD EQ true OR City EQ "SPB" AND Rating GE 5 AS SPBSSD` expected := new(netmap.PlacementPolicy) expected.SetReplicas([]*netmap.Replica{newReplica("SPB", 7)}) expected.SetSelectors([]*netmap.Selector{ newSelector(1, netmap.UnspecifiedClause, "City", "SPBSSD", "SPB"), }) expected.SetFilters([]*netmap.Filter{ newFilter("SPBSSD", "", "", netmap.OR, newFilter("", "", "", netmap.AND, newFilter("", "City", "SPB", netmap.EQ), newFilter("", "SSD", "true", netmap.EQ)), newFilter("", "", "", netmap.AND, newFilter("", "City", "SPB", netmap.EQ), newFilter("", "Rating", "5", netmap.GE))), }) r, err := Parse(q) require.NoError(t, err) require.Equal(t, expected, r) } func TestValidation(t *testing.T) { t.Run("MissingSelector", func(t *testing.T) { q := `REP 3 IN RU` _, err := Parse(q) require.True(t, errors.Is(err, ErrUnknownSelector), "got: %v", err) }) t.Run("MissingFilter", func(t *testing.T) { q := `REP 3 SELECT 1 IN City FROM MissingFilter` _, err := Parse(q) require.True(t, errors.Is(err, ErrUnknownFilter), "got: %v", err) }) t.Run("UnknownOp", func(t *testing.T) { q := `REP 3 SELECT 1 IN City FROM F FILTER Country KEK RU AS F` _, err := Parse(q) require.True(t, errors.Is(err, ErrUnknownOp), "got: %v", err) }) t.Run("TypoInREP", func(t *testing.T) { q := `REK 3` _, err := Parse(q) require.Error(t, err) }) t.Run("InvalidFilterName", func(t *testing.T) { q := `REP 3 SELECT 1 IN City FROM F FILTER Good AND Country EQ RU AS F FILTER Rating EQ 5 AS Good` _, err := Parse(q) require.Error(t, err) }) t.Run("InvalidNumberInREP", func(t *testing.T) { q := `REP 0` _, err := Parse(q) require.True(t, errors.Is(err, ErrInvalidNumber), "got: %v", err) }) t.Run("InvalidNumberInREP", func(t *testing.T) { q := `REP 1 IN Good SELECT 0 IN City FROM *` _, err := Parse(q) require.True(t, errors.Is(err, ErrInvalidNumber), "got: %v", err) }) } func newFilter(name, key, value string, op netmap.Operation, sub ...*netmap.Filter) *netmap.Filter { f := new(netmap.Filter) f.SetName(name) f.SetKey(key) f.SetValue(value) f.SetOp(op) f.SetFilters(sub) return f } func newReplica(s string, c uint32) *netmap.Replica { r := new(netmap.Replica) r.SetSelector(s) r.SetCount(c) return r } func newSelector(count uint32, c netmap.Clause, attr, f, name string) *netmap.Selector { s := new(netmap.Selector) s.SetCount(count) s.SetClause(c) s.SetAttribute(attr) s.SetFilter(f) s.SetName(name) return s }