From 1728bf29b8903e8ae09cd576aef7735866c68730 Mon Sep 17 00:00:00 2001 From: Fabio Bozzo Date: Wed, 23 Oct 2024 16:37:35 +0200 Subject: [PATCH] add more parsing tests --- pkg/policy/selector/parsing.go | 6 +- pkg/policy/selector/parsing_test.go | 207 +++++++++++++++++++++++++++ pkg/policy/selector/selector_test.go | 74 +++++++++- 3 files changed, 285 insertions(+), 2 deletions(-) diff --git a/pkg/policy/selector/parsing.go b/pkg/policy/selector/parsing.go index cad7feb..a432ec0 100644 --- a/pkg/policy/selector/parsing.go +++ b/pkg/policy/selector/parsing.go @@ -61,7 +61,11 @@ func Parse(str string) (Selector, error) { // explicit field, ["abcd"] case strings.HasPrefix(lookup, "\"") && strings.HasSuffix(lookup, "\""): - sel = append(sel, segment{str: tok, optional: opt, field: lookup[1 : len(lookup)-1]}) + fieldName := lookup[1 : len(lookup)-1] + if strings.Contains(fieldName, ":") { + return nil, newParseError(fmt.Sprintf("invalid segment: %s", seg), str, col, tok) + } + sel = append(sel, segment{str: tok, optional: opt, field: fieldName}) // slice [3:5] or [:5] or [3:], also negative numbers case sliceRegex.MatchString(lookup): diff --git a/pkg/policy/selector/parsing_test.go b/pkg/policy/selector/parsing_test.go index 1dac6b6..e693b80 100644 --- a/pkg/policy/selector/parsing_test.go +++ b/pkg/policy/selector/parsing_test.go @@ -412,6 +412,213 @@ func TestParse(t *testing.T) { require.NotNil(t, err) fmt.Println(err) }) + + t.Run("index out of range", func(t *testing.T) { + sel, err := Parse(".[1000]") + require.NoError(t, err) + require.Equal(t, 2, len(sel)) + require.True(t, sel[0].Identity()) + require.False(t, sel[0].Optional()) + require.False(t, sel[0].Iterator()) + require.Empty(t, sel[0].Slice()) + require.Empty(t, sel[0].Field()) + require.Empty(t, sel[0].Index()) + require.False(t, sel[1].Identity()) + require.False(t, sel[1].Optional()) + require.False(t, sel[1].Iterator()) + require.Empty(t, sel[1].Slice()) + require.Empty(t, sel[1].Field()) + require.Equal(t, sel[1].Index(), 1000) + }) + + t.Run("negative index out of range", func(t *testing.T) { + sel, err := Parse(".[-1000]") + require.NoError(t, err) + require.Equal(t, 2, len(sel)) + require.True(t, sel[0].Identity()) + require.False(t, sel[0].Optional()) + require.False(t, sel[0].Iterator()) + require.Empty(t, sel[0].Slice()) + require.Empty(t, sel[0].Field()) + require.Empty(t, sel[0].Index()) + require.False(t, sel[1].Identity()) + require.False(t, sel[1].Optional()) + require.False(t, sel[1].Iterator()) + require.Empty(t, sel[1].Slice()) + require.Empty(t, sel[1].Field()) + require.Equal(t, sel[1].Index(), -1000) + }) + + t.Run("slice with negative start and positive end", func(t *testing.T) { + sel, err := Parse(".[0:-2]") + require.NoError(t, err) + require.Equal(t, 2, len(sel)) + require.True(t, sel[0].Identity()) + require.False(t, sel[0].Optional()) + require.False(t, sel[0].Iterator()) + require.Empty(t, sel[0].Slice()) + require.Empty(t, sel[0].Field()) + require.Empty(t, sel[0].Index()) + require.False(t, sel[1].Identity()) + require.False(t, sel[1].Optional()) + require.False(t, sel[1].Iterator()) + require.Equal(t, sel[1].Slice(), []int64{0, -2}) + require.Empty(t, sel[1].Field()) + require.Empty(t, sel[1].Index()) + }) + + t.Run("slice with start greater than end", func(t *testing.T) { + sel, err := Parse(".[5:2]") + require.NoError(t, err) + require.Equal(t, 2, len(sel)) + require.True(t, sel[0].Identity()) + require.False(t, sel[0].Optional()) + require.False(t, sel[0].Iterator()) + require.Empty(t, sel[0].Slice()) + require.Empty(t, sel[0].Field()) + require.Empty(t, sel[0].Index()) + require.False(t, sel[1].Identity()) + require.False(t, sel[1].Optional()) + require.False(t, sel[1].Iterator()) + require.Equal(t, sel[1].Slice(), []int64{5, 2}) + require.Empty(t, sel[1].Field()) + require.Empty(t, sel[1].Index()) + }) + + t.Run("slice on string", func(t *testing.T) { + sel, err := Parse(`.["foo"].[1:3]`) + require.NoError(t, err) + require.Equal(t, 4, len(sel)) + require.True(t, sel[0].Identity()) + require.False(t, sel[0].Optional()) + require.False(t, sel[0].Iterator()) + require.Empty(t, sel[0].Slice()) + require.Empty(t, sel[0].Field()) + require.Empty(t, sel[0].Index()) + require.False(t, sel[1].Identity()) + require.False(t, sel[1].Optional()) + require.False(t, sel[1].Iterator()) + require.Empty(t, sel[1].Slice()) + require.Equal(t, sel[1].Field(), "foo") + require.Empty(t, sel[1].Index()) + require.True(t, sel[2].Identity()) + require.False(t, sel[2].Optional()) + require.False(t, sel[2].Iterator()) + require.Empty(t, sel[2].Slice()) + require.Empty(t, sel[2].Field()) + require.Empty(t, sel[2].Index()) + require.False(t, sel[3].Identity()) + require.False(t, sel[3].Optional()) + require.False(t, sel[3].Iterator()) + require.Equal(t, sel[3].Slice(), []int64{1, 3}) + require.Empty(t, sel[3].Field()) + require.Empty(t, sel[3].Index()) + }) + + t.Run("index on string", func(t *testing.T) { + sel, err := Parse(`.["foo"].[1]`) + require.NoError(t, err) + require.Equal(t, 4, len(sel)) + require.True(t, sel[0].Identity()) + require.False(t, sel[0].Optional()) + require.False(t, sel[0].Iterator()) + require.Empty(t, sel[0].Slice()) + require.Empty(t, sel[0].Field()) + require.Empty(t, sel[0].Index()) + require.False(t, sel[1].Identity()) + require.False(t, sel[1].Optional()) + require.False(t, sel[1].Iterator()) + require.Empty(t, sel[1].Slice()) + require.Equal(t, sel[1].Field(), "foo") + require.Empty(t, sel[1].Index()) + require.True(t, sel[2].Identity()) + require.False(t, sel[2].Optional()) + require.False(t, sel[2].Iterator()) + require.Empty(t, sel[2].Slice()) + require.Empty(t, sel[2].Field()) + require.Empty(t, sel[2].Index()) + require.False(t, sel[3].Identity()) + require.False(t, sel[3].Optional()) + require.False(t, sel[3].Iterator()) + require.Empty(t, sel[3].Slice()) + require.Empty(t, sel[3].Field()) + require.Equal(t, sel[3].Index(), 1) + }) + + t.Run("slice on array", func(t *testing.T) { + sel, err := Parse(`.[1:3]`) + require.NoError(t, err) + require.Equal(t, 2, len(sel)) + require.True(t, sel[0].Identity()) + require.False(t, sel[0].Optional()) + require.False(t, sel[0].Iterator()) + require.Empty(t, sel[0].Slice()) + require.Empty(t, sel[0].Field()) + require.Empty(t, sel[0].Index()) + require.False(t, sel[1].Identity()) + require.False(t, sel[1].Optional()) + require.False(t, sel[1].Iterator()) + require.Equal(t, sel[1].Slice(), []int64{1, 3}) + require.Empty(t, sel[1].Field()) + require.Empty(t, sel[1].Index()) + }) + + t.Run("index on array", func(t *testing.T) { + sel, err := Parse(`.[1]`) + require.NoError(t, err) + require.Equal(t, 2, len(sel)) + require.True(t, sel[0].Identity()) + require.False(t, sel[0].Optional()) + require.False(t, sel[0].Iterator()) + require.Empty(t, sel[0].Slice()) + require.Empty(t, sel[0].Field()) + require.Empty(t, sel[0].Index()) + require.False(t, sel[1].Identity()) + require.False(t, sel[1].Optional()) + require.False(t, sel[1].Iterator()) + require.Empty(t, sel[1].Slice()) + require.Empty(t, sel[1].Field()) + require.Equal(t, sel[1].Index(), 1) + }) + + t.Run("invalid slice on object", func(t *testing.T) { + _, err := Parse(`.["foo":"bar"]`) + require.Error(t, err) + require.Contains(t, err.Error(), "invalid segment") + }) + + t.Run("index on object", func(t *testing.T) { + sel, err := Parse(`.["foo"]`) + require.NoError(t, err) + require.Equal(t, 2, len(sel)) + require.True(t, sel[0].Identity()) + require.False(t, sel[0].Optional()) + require.False(t, sel[0].Iterator()) + require.Empty(t, sel[0].Slice()) + require.Empty(t, sel[0].Field()) + require.Empty(t, sel[0].Index()) + require.False(t, sel[1].Identity()) + require.False(t, sel[1].Optional()) + require.False(t, sel[1].Iterator()) + require.Empty(t, sel[1].Slice()) + require.Equal(t, sel[1].Field(), "foo") + require.Empty(t, sel[1].Index()) + }) + + t.Run("slice with non-integer start", func(t *testing.T) { + _, err := Parse(".[foo:3]") + require.Error(t, err) + }) + + t.Run("slice with non-integer end", func(t *testing.T) { + _, err := Parse(".[1:bar]") + require.Error(t, err) + }) + + t.Run("index with non-integer", func(t *testing.T) { + _, err := Parse(".[foo]") + require.Error(t, err) + }) } func printSegments(s Selector) { diff --git a/pkg/policy/selector/selector_test.go b/pkg/policy/selector/selector_test.go index fe53070..a096409 100644 --- a/pkg/policy/selector/selector_test.go +++ b/pkg/policy/selector/selector_test.go @@ -171,7 +171,79 @@ func TestSelect(t *testing.T) { require.Equal(t, alice.Interests[1].Name, iname) }) - // TODO: fully test slicing + negative numbers + t.Run("slice on string", func(t *testing.T) { + sel, err := Parse(`["foo"].[1:3]`) + require.NoError(t, err) + + node := basicnode.NewString("hello") + res, err := sel.Select(node) + require.NoError(t, err) + require.NotEmpty(t, res) + + str, err := res.AsString() + require.NoError(t, err) + require.Equal(t, "el", str) + }) + + t.Run("index on string", func(t *testing.T) { + sel, err := Parse(`["foo"].[2]`) + require.NoError(t, err) + + node := basicnode.NewString("hello") + res, err := sel.Select(node) + require.NoError(t, err) + require.NotEmpty(t, res) + + str, err := res.AsString() + require.NoError(t, err) + require.Equal(t, "l", str) + }) + + //t.Run("out of bounds slicing", func(t *testing.T) { + // sel, err := Parse(`.[10:20]`) + // require.NoError(t, err) + // + // node, err := qp.BuildList(basicnode.Prototype.Any, 3, func(la datamodel.ListAssembler) { + // qp.ListEntry(la, qp.Int(1)) + // qp.ListEntry(la, qp.Int(2)) + // qp.ListEntry(la, qp.Int(3)) + // }) + // require.NoError(t, err) + // + // res, err := sel.Select(node) + // require.NoError(t, err) + // require.NotEmpty(t, res) + // + // bytes, err := res.AsBytes() + // require.NoError(t, err) + // + // list, err := qp.DecodeList(basicnode.Prototype.Any, bytes) + // require.NoError(t, err) + // require.Equal(t, 0, list.Length()) + //}) + // + //t.Run("backward slicing", func(t *testing.T) { + // sel, err := Parse(`.[5:2]`) + // require.NoError(t, err) + // + // node, err := qp.BuildList(basicnode.Prototype.Any, 3, func(la datamodel.ListAssembler) { + // qp.ListEntry(la, qp.Int(1)) + // qp.ListEntry(la, qp.Int(2)) + // qp.ListEntry(la, qp.Int(3)) + // }) + // require.NoError(t, err) + // + // res, err := sel.Select(node) + // require.NoError(t, err) + // require.NotEmpty(t, res) + // + // bytes, err := res.AsBytes() + // require.NoError(t, err) + // + // list, err := qp.DecodeList(basicnode.Prototype.Any, bytes) + // require.NoError(t, err) + // require.Equal(t, 0, list.Length()) + //}) } // func TestMatch(t *testing.T) {