mirror of
https://github.com/rancher/steve.git
synced 2025-09-27 07:43:59 +00:00
Add parsing of the indirect access operator ('=>').
This commit is contained in:
@@ -27,15 +27,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/rancher/steve/pkg/stores/sqlpartition/selection"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
var (
|
||||
ignoreDetail = cmpopts.IgnoreFields(field.Error{}, "Detail")
|
||||
)
|
||||
|
||||
func TestSelectorParse(t *testing.T) {
|
||||
@@ -54,6 +48,9 @@ func TestSelectorParse(t *testing.T) {
|
||||
"metadata.labels[im.here]",
|
||||
"!metadata.labels[im.not.here]",
|
||||
"metadata.labels[k8s.io/meta-stuff] ~ has-dashes_underscores.dots.only",
|
||||
"metadata.labels[k8s.io/meta-stuff] => [management.cattle.io/v3][tokens][id][metadata.state.name] = active",
|
||||
"name => [management.cattle.io/v3][tokens][id][metadata.state.name] = active",
|
||||
"metadata.annotations[blah] => [management.cattle.io/v3][tokens][id][metadata.state.name] = active",
|
||||
}
|
||||
testBadStrings := []string{
|
||||
"!no-label-absence-test",
|
||||
@@ -77,15 +74,22 @@ func TestSelectorParse(t *testing.T) {
|
||||
"!metadata.labels(im.not.here)",
|
||||
`x="no double quotes allowed"`,
|
||||
`x='no single quotes allowed'`,
|
||||
"metadata.labels[k8s.io/meta-stuff] => not-bracketed = active",
|
||||
"metadata.labels[k8s.io/meta-stuff] => [not][enough][accessors] = active",
|
||||
"metadata.labels[k8s.io/meta-stuff] => [too][many][accessors][by][1] = active",
|
||||
"metadata.labels[k8s.io/meta-stuff] => [missing][an][operator][end-of-string]",
|
||||
"metadata.labels[k8s.io/meta-stuff] => [missing][an][operator][no-following-operator] no-operator",
|
||||
"metadata.labels[k8s.io/meta-stuff] => [missing][a][post-operator][value] >",
|
||||
"metadata.labels[not/followed/by/accessor] => = active",
|
||||
}
|
||||
for _, test := range testGoodStrings {
|
||||
_, err := Parse(test)
|
||||
_, err := Parse(test, "filter")
|
||||
if err != nil {
|
||||
t.Errorf("%v: error %v (%#v)\n", test, err, err)
|
||||
}
|
||||
}
|
||||
for _, test := range testBadStrings {
|
||||
_, err := Parse(test)
|
||||
_, err := Parse(test, "filter")
|
||||
if err == nil {
|
||||
t.Errorf("%v: did not get expected error\n", test)
|
||||
}
|
||||
@@ -115,6 +119,7 @@ func TestLexer(t *testing.T) {
|
||||
{"~", PartialEqualsToken},
|
||||
{"!~", NotPartialEqualsToken},
|
||||
{"||", ErrorToken},
|
||||
{"=>", IndirectAccessToken},
|
||||
}
|
||||
for _, v := range testcases {
|
||||
l := &Lexer{s: v.s, pos: 0}
|
||||
@@ -163,6 +168,9 @@ func TestLexerSequence(t *testing.T) {
|
||||
{"key!~ value", []Token{IdentifierToken, NotPartialEqualsToken, IdentifierToken}},
|
||||
{"key !~value", []Token{IdentifierToken, NotPartialEqualsToken, IdentifierToken}},
|
||||
{"key!~value", []Token{IdentifierToken, NotPartialEqualsToken, IdentifierToken}},
|
||||
{"metadata.labels[k8s.io/meta-stuff] => [management.cattle.io/v3][tokens][id][metadata.state.name] = active",
|
||||
[]Token{IdentifierToken, IndirectAccessToken, IdentifierToken, EqualsToken, IdentifierToken},
|
||||
},
|
||||
}
|
||||
for _, v := range testcases {
|
||||
var tokens []Token
|
||||
@@ -203,6 +211,10 @@ func TestParserLookahead(t *testing.T) {
|
||||
{"key gt 3", []Token{IdentifierToken, GreaterThanToken, IdentifierToken, EndOfStringToken}},
|
||||
{"key lt 4", []Token{IdentifierToken, LessThanToken, IdentifierToken, EndOfStringToken}},
|
||||
{`key = multi-word-string`, []Token{IdentifierToken, EqualsToken, QuotedStringToken, EndOfStringToken}},
|
||||
|
||||
{"metadata.labels[k8s.io/meta-stuff] => [management.cattle.io/v3][tokens][id][metadata.state.name] = active",
|
||||
[]Token{IdentifierToken, IndirectAccessToken, IdentifierToken, EqualsToken, IdentifierToken, EndOfStringToken},
|
||||
},
|
||||
}
|
||||
for _, v := range testcases {
|
||||
p := &Parser{l: &Lexer{s: v.s, pos: 0}, position: 0}
|
||||
@@ -240,6 +252,7 @@ func TestParseOperator(t *testing.T) {
|
||||
{"notin", nil},
|
||||
{"!=", nil},
|
||||
{"!~", nil},
|
||||
{"=>", nil},
|
||||
{"!", fmt.Errorf("found '%s', expected: %v", selection.DoesNotExist, strings.Join(binaryOperators, ", "))},
|
||||
{"exists", fmt.Errorf("found '%s', expected: %v", selection.Exists, strings.Join(binaryOperators, ", "))},
|
||||
{"(", fmt.Errorf("found '%s', expected: %v", "(", strings.Join(binaryOperators, ", "))},
|
||||
@@ -262,30 +275,18 @@ func TestRequirementConstructor(t *testing.T) {
|
||||
Key string
|
||||
Op selection.Operator
|
||||
Vals sets.String
|
||||
WantErr field.ErrorList
|
||||
WantErr string
|
||||
}{
|
||||
{
|
||||
Key: "x1",
|
||||
Op: selection.In,
|
||||
WantErr: field.ErrorList{
|
||||
&field.Error{
|
||||
Type: field.ErrorTypeInvalid,
|
||||
Field: "values",
|
||||
BadValue: []string{},
|
||||
},
|
||||
},
|
||||
Key: "x1",
|
||||
Op: selection.In,
|
||||
WantErr: "values: Invalid value: []string{}: for 'in', 'notin' operators, values set can't be empty",
|
||||
},
|
||||
{
|
||||
Key: "x2",
|
||||
Op: selection.NotIn,
|
||||
Vals: sets.NewString(),
|
||||
WantErr: field.ErrorList{
|
||||
&field.Error{
|
||||
Type: field.ErrorTypeInvalid,
|
||||
Field: "values",
|
||||
BadValue: []string{},
|
||||
},
|
||||
},
|
||||
Key: "x2",
|
||||
Op: selection.NotIn,
|
||||
Vals: sets.NewString(),
|
||||
WantErr: "values: Invalid value: []string{}: for 'in', 'notin' operators, values set can't be empty",
|
||||
},
|
||||
{
|
||||
Key: "x3",
|
||||
@@ -298,16 +299,10 @@ func TestRequirementConstructor(t *testing.T) {
|
||||
Vals: sets.NewString("foo"),
|
||||
},
|
||||
{
|
||||
Key: "x5",
|
||||
Op: selection.Equals,
|
||||
Vals: sets.NewString("foo", "bar"),
|
||||
WantErr: field.ErrorList{
|
||||
&field.Error{
|
||||
Type: field.ErrorTypeInvalid,
|
||||
Field: "values",
|
||||
BadValue: []string{"bar", "foo"},
|
||||
},
|
||||
},
|
||||
Key: "x5",
|
||||
Op: selection.Equals,
|
||||
Vals: sets.NewString("foo", "bar"),
|
||||
WantErr: "values: Invalid value: []string{\"bar\", \"foo\"}: exact-match compatibility requires one single value",
|
||||
},
|
||||
{
|
||||
Key: "x6",
|
||||
@@ -318,16 +313,10 @@ func TestRequirementConstructor(t *testing.T) {
|
||||
Op: selection.DoesNotExist,
|
||||
},
|
||||
{
|
||||
Key: "x8",
|
||||
Op: selection.Exists,
|
||||
Vals: sets.NewString("foo"),
|
||||
WantErr: field.ErrorList{
|
||||
&field.Error{
|
||||
Type: field.ErrorTypeInvalid,
|
||||
Field: "values",
|
||||
BadValue: []string{"foo"},
|
||||
},
|
||||
},
|
||||
Key: "x8",
|
||||
Op: selection.Exists,
|
||||
Vals: sets.NewString("foo"),
|
||||
WantErr: `values: Invalid value: []string{"foo"}: values set must be empty for exists and does not exist`,
|
||||
},
|
||||
{
|
||||
Key: "x9",
|
||||
@@ -350,39 +339,21 @@ func TestRequirementConstructor(t *testing.T) {
|
||||
Vals: sets.NewString("6"),
|
||||
},
|
||||
{
|
||||
Key: "x13",
|
||||
Op: selection.GreaterThan,
|
||||
WantErr: field.ErrorList{
|
||||
&field.Error{
|
||||
Type: field.ErrorTypeInvalid,
|
||||
Field: "values",
|
||||
BadValue: []string{},
|
||||
},
|
||||
},
|
||||
Key: "x13",
|
||||
Op: selection.GreaterThan,
|
||||
WantErr: "values: Invalid value: []string{}: for 'Gt', 'Lt' operators, exactly one value is required",
|
||||
},
|
||||
{
|
||||
Key: "x14",
|
||||
Op: selection.GreaterThan,
|
||||
Vals: sets.NewString("bar"),
|
||||
WantErr: field.ErrorList{
|
||||
&field.Error{
|
||||
Type: field.ErrorTypeInvalid,
|
||||
Field: "values[0]",
|
||||
BadValue: "bar",
|
||||
},
|
||||
},
|
||||
Key: "x14",
|
||||
Op: selection.GreaterThan,
|
||||
Vals: sets.NewString("bar"),
|
||||
WantErr: `values[0]: Invalid value: "bar": for 'Gt', 'Lt' operators, the value must be an integer`,
|
||||
},
|
||||
{
|
||||
Key: "x15",
|
||||
Op: selection.LessThan,
|
||||
Vals: sets.NewString("bar"),
|
||||
WantErr: field.ErrorList{
|
||||
&field.Error{
|
||||
Type: field.ErrorTypeInvalid,
|
||||
Field: "values[0]",
|
||||
BadValue: "bar",
|
||||
},
|
||||
},
|
||||
Key: "x15",
|
||||
Op: selection.LessThan,
|
||||
Vals: sets.NewString("bar"),
|
||||
WantErr: `values[0]: Invalid value: "bar": for 'Gt', 'Lt' operators, the value must be an integer`,
|
||||
},
|
||||
{
|
||||
Key: strings.Repeat("a", 254), //breaks DNS rule that len(key) <= 253
|
||||
@@ -399,21 +370,29 @@ func TestRequirementConstructor(t *testing.T) {
|
||||
Vals: sets.NewString("a b"),
|
||||
},
|
||||
{
|
||||
Key: "x18",
|
||||
Op: "unsupportedOp",
|
||||
WantErr: field.ErrorList{
|
||||
&field.Error{
|
||||
Type: field.ErrorTypeNotSupported,
|
||||
Field: "operator",
|
||||
BadValue: selection.Operator("unsupportedOp"),
|
||||
},
|
||||
},
|
||||
Key: "x18",
|
||||
Op: "unsupportedOp",
|
||||
WantErr: `operator: Unsupported value: "unsupportedOp": supported values: "in", "notin", "=", "==", "!=", "~", "!~", "gt", "lt", "=>", "exists", "!"`,
|
||||
},
|
||||
}
|
||||
for _, rc := range requirementConstructorTests {
|
||||
_, err := NewRequirement(rc.Key, rc.Op, rc.Vals.List())
|
||||
if diff := cmp.Diff(rc.WantErr.ToAggregate(), err, ignoreDetail); diff != "" {
|
||||
t.Errorf("NewRequirement test %v returned unexpected error (-want,+got):\n%s", rc.Key, diff)
|
||||
if rc.WantErr != "" {
|
||||
assert.NotNil(t, err)
|
||||
if err != nil {
|
||||
assert.Equal(t, rc.WantErr, err.Error())
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
_, err = NewIndirectRequirement(rc.Key, []string{"herb", "job", "nice", "reading"}, &rc.Op, rc.Vals.List())
|
||||
if rc.WantErr != "" {
|
||||
assert.NotNil(t, err)
|
||||
if err != nil {
|
||||
assert.Equal(t, rc.WantErr, err.Error())
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user