frostfs-node/pkg/policy/query_test.go
Evgenii Stratonikov 001a23eb31 [] Support SQL-like queries for placement
JSON format is rather verbose an inconvenient to be
edited by hand. This commit implements SQL-like
language for representing placement policy.

Signed-off-by: Evgenii Stratonikov <evgeniy@nspcc.ru>
2020-10-02 11:25:35 +03:00

230 lines
6.3 KiB
Go

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
}