Skip to content

Commit

Permalink
Merge branch 'main' into release/1.36
Browse files Browse the repository at this point in the history
  • Loading branch information
markphelps authored Jan 23, 2024
2 parents 1e50529 + e594593 commit 57cf944
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 8 deletions.
3 changes: 3 additions & 0 deletions internal/cue/extended.cue
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#Flag: {
description: =~"^.+$"
}
2 changes: 0 additions & 2 deletions internal/cue/testdata/valid.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ flags:
name: flipt
- key: flipt
name: flipt
description: I'm a description.
- key: withAttachmentObject
name: With Attachment Object
attachment:
Expand All @@ -30,7 +29,6 @@ flags:
rollout: 100
- key: boolean
name: Boolean
description: Boolean flag
enabled: false
rollouts:
- description: enabled for internal users
Expand Down
31 changes: 30 additions & 1 deletion internal/cue/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"strconv"

"cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
Expand Down Expand Up @@ -114,6 +115,7 @@ func (v FeaturesValidator) validateSingleDocument(file string, f *ast.File, offs
Validate(cue.All(), cue.Concrete(true))

var errs []error
OUTER:
for _, e := range cueerrors.Errors(err) {
rerr := Error{
Message: e.Error(),
Expand All @@ -122,11 +124,38 @@ func (v FeaturesValidator) validateSingleDocument(file string, f *ast.File, offs
},
}

// if the error has path segments we're going to use that
// to select into the original document
// we parse the slice of the path into selector
selectors := []cue.Selector{}
for _, p := range e.Path() {
if i, err := strconv.ParseInt(p, 10, 64); err == nil {
selectors = append(selectors, cue.Index(int(i)))
continue
}

selectors = append(selectors, cue.Str(p))
}

// next we walk the selector back from the deapest path until
// we select something that exists in the document
for i := len(selectors); i > 0; i-- {
selectors = selectors[:i]
val := yv.LookupPath(cue.MakePath(selectors...))

// if we manage to locate something then we use that
// position in our error message
if pos := val.Pos(); pos.IsValid() {
rerr.Location.Line = pos.Line() + offset
errs = append(errs, rerr)
continue OUTER
}
}

if pos := cueerrors.Positions(e); len(pos) > 0 {
p := pos[len(pos)-1]
rerr.Location.Line = p.Line() + offset
}

errs = append(errs, rerr)
}

Expand Down
4 changes: 2 additions & 2 deletions internal/cue/validate_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import (
)

func FuzzValidate(f *testing.F) {
testcases := []string{"testdata/valid.yml", "testdata/invalid.yml"}
testcases := []string{"testdata/valid.yml", "testdata/invalid.yml", "testdata/valid_v1.yml", "testdata/valid_segments_v2.yml", "testdata/valid_yaml_stream.yml"}

for _, tc := range testcases {
fi, _ := os.ReadFile(tc)
f.Add(fi)
}

f.Fuzz(func(t *testing.T, in []byte) {
validator, err := NewFeaturesValidator()
validator, err := NewFeaturesValidator(WithSchemaExtension(in))
if err != nil {
// only care about errors from Validating
t.Skip()
Expand Down
39 changes: 39 additions & 0 deletions internal/cue/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ func TestValidate_V1_Success(t *testing.T) {
f, err := os.Open("testdata/valid_v1.yaml")
require.NoError(t, err)

defer f.Close()

v, err := NewFeaturesValidator()
require.NoError(t, err)

Expand All @@ -24,6 +26,8 @@ func TestValidate_Latest_Success(t *testing.T) {
f, err := os.Open("testdata/valid.yaml")
require.NoError(t, err)

defer f.Close()

v, err := NewFeaturesValidator()
require.NoError(t, err)

Expand All @@ -35,6 +39,8 @@ func TestValidate_Latest_Segments_V2(t *testing.T) {
f, err := os.Open("testdata/valid_segments_v2.yaml")
require.NoError(t, err)

defer f.Close()

v, err := NewFeaturesValidator()
require.NoError(t, err)

Expand All @@ -46,6 +52,8 @@ func TestValidate_YAML_Stream(t *testing.T) {
f, err := os.Open("testdata/valid_yaml_stream.yaml")
require.NoError(t, err)

defer f.Close()

v, err := NewFeaturesValidator()
require.NoError(t, err)

Expand All @@ -57,6 +65,8 @@ func TestValidate_Failure(t *testing.T) {
f, err := os.Open("testdata/invalid.yaml")
require.NoError(t, err)

defer f.Close()

v, err := NewFeaturesValidator()
require.NoError(t, err)

Expand All @@ -77,6 +87,8 @@ func TestValidate_Failure_YAML_Stream(t *testing.T) {
f, err := os.Open("testdata/invalid_yaml_stream.yaml")
require.NoError(t, err)

defer f.Close()

v, err := NewFeaturesValidator()
require.NoError(t, err)

Expand All @@ -92,3 +104,30 @@ func TestValidate_Failure_YAML_Stream(t *testing.T) {
assert.Equal(t, "testdata/invalid_yaml_stream.yaml", ferr.Location.File)
assert.Equal(t, 59, ferr.Location.Line)
}

func TestValidate_Extended(t *testing.T) {
f, err := os.Open("testdata/valid.yaml")
require.NoError(t, err)

defer f.Close()

extended, err := os.ReadFile("extended.cue")
require.NoError(t, err)

v, err := NewFeaturesValidator(WithSchemaExtension(extended))
require.NoError(t, err)

err = v.Validate("testdata/valid.yaml", f)

errs, ok := Unwrap(err)
require.True(t, ok)

var ferr Error
require.True(t, errors.As(errs[0], &ferr))

assert.Equal(t, `flags.1.description: incomplete value =~"^.+$"`, ferr.Message)
assert.Equal(t, "testdata/valid.yaml", ferr.Location.File)
// location of the start of the boolean flag
// which lacks a description
assert.Equal(t, 30, ferr.Location.Line)
}
6 changes: 3 additions & 3 deletions internal/storage/fs/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ func TestSnapshotFromFS_Invalid(t *testing.T) {
{
path: "testdata/invalid/namespace",
err: errors.Join(
cue.Error{Message: "namespace: 2 errors in empty disjunction:", Location: cue.Location{File: "features.json", Line: 0}},
cue.Error{Message: "namespace: conflicting values 1 and \"default\" (mismatched types int and string)", Location: cue.Location{File: "features.json", Line: 3}},
cue.Error{Message: "namespace: conflicting values 1 and string (mismatched types int and string)", Location: cue.Location{File: "features.json", Line: 3}},
cue.Error{Message: "namespace: 2 errors in empty disjunction:", Location: cue.Location{File: "features.json", Line: 1}},
cue.Error{Message: "namespace: conflicting values 1 and \"default\" (mismatched types int and string)", Location: cue.Location{File: "features.json", Line: 1}},
cue.Error{Message: "namespace: conflicting values 1 and string (mismatched types int and string)", Location: cue.Location{File: "features.json", Line: 1}},
),
},
} {
Expand Down

0 comments on commit 57cf944

Please sign in to comment.