Skip to content

Commit 4299e11

Browse files
committed
ensure all parse-time errors show line/col in YAML
To give users the most useful information about a syntax or parse-time error in their gdt tests, make sure that all parse-time error functions accept a `*yaml.Node` parameter that we can grab line/column information.` Signed-off-by: Jay Pipes <[email protected]>
1 parent 13c1ecd commit 4299e11

File tree

5 files changed

+49
-24
lines changed

5 files changed

+49
-24
lines changed

assertion/json/errors.go

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99

1010
gdterrors "github.com/gdt-dev/gdt/errors"
11+
"gopkg.in/yaml.v3"
1112
)
1213

1314
var (
@@ -78,32 +79,51 @@ var (
7879

7980
// UnsupportedJSONSchemaReference returns ErrUnsupportedJSONSchemaReference for
8081
// a supplied URL.
81-
func UnsupportedJSONSchemaReference(url string) error {
82-
return fmt.Errorf("%w: %s", ErrUnsupportedJSONSchemaReference, url)
82+
func UnsupportedJSONSchemaReference(url string, node *yaml.Node) error {
83+
return fmt.Errorf(
84+
"%w: %s at line %d, column %d",
85+
ErrUnsupportedJSONSchemaReference, url, node.Line, node.Column,
86+
)
8387
}
8488

8589
// JSONSchemaFileNotFound returns ErrJSONSchemaFileNotFound for a supplied
8690
// path.
87-
func JSONSchemaFileNotFound(path string) error {
88-
return fmt.Errorf("%w: %s", ErrJSONSchemaFileNotFound, path)
91+
func JSONSchemaFileNotFound(path string, node *yaml.Node) error {
92+
return fmt.Errorf(
93+
"%w: %s at line %d, column %d",
94+
ErrJSONSchemaFileNotFound, path, node.Line, node.Column,
95+
)
8996
}
9097

9198
// JSONUnmarshalError returns an ErrFailure when JSON content cannot be
9299
// decoded.
93-
func JSONUnmarshalError(err error) error {
94-
return fmt.Errorf("%w: %s", ErrJSONUnmarshalError, err)
100+
func JSONUnmarshalError(err error, node *yaml.Node) error {
101+
if node != nil {
102+
return fmt.Errorf(
103+
"%w: %s at line %d, column %d",
104+
ErrJSONUnmarshalError, err, node.Line, node.Column,
105+
)
106+
} else {
107+
return fmt.Errorf("%w: %s", ErrJSONUnmarshalError, err)
108+
}
95109
}
96110

97111
// JSONPathInvalid returns an ErrParse when a JSONPath expression could not be
98112
// parsed.
99-
func JSONPathInvalid(path string, err error) error {
100-
return fmt.Errorf("%w: %s: %s", ErrJSONPathInvalid, path, err)
113+
func JSONPathInvalid(path string, err error, node *yaml.Node) error {
114+
return fmt.Errorf(
115+
"%w: %s: %s at line %d, column %d",
116+
ErrJSONPathInvalid, path, err, node.Line, node.Column,
117+
)
101118
}
102119

103120
// JSONPathInvalidNoRoot returns an ErrJSONPathInvalidNoRoot when a JSONPath
104121
// expression does not start with '$'.
105-
func JSONPathInvalidNoRoot(path string) error {
106-
return fmt.Errorf("%w: %s", ErrJSONPathInvalidNoRoot, path)
122+
func JSONPathInvalidNoRoot(path string, node *yaml.Node) error {
123+
return fmt.Errorf(
124+
"%w: %s at line %d, column %d",
125+
ErrJSONPathInvalidNoRoot, path, node.Line, node.Column,
126+
)
107127
}
108128

109129
// JSONPathNotFound returns an ErrFailure when a JSONPath expression could not

assertion/json/json.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func (e *Expect) UnmarshalYAML(node *yaml.Node) error {
7575
schemaURL := valNode.Value
7676
if strings.HasPrefix(schemaURL, "http://") || strings.HasPrefix(schemaURL, "https://") {
7777
// TODO(jaypipes): Support network lookups?
78-
return UnsupportedJSONSchemaReference(schemaURL)
78+
return UnsupportedJSONSchemaReference(schemaURL, valNode)
7979
}
8080
// Convert relative filepaths to absolute filepaths rooted in the context's
8181
// testdir after stripping any "file://" scheme prefix
@@ -84,7 +84,7 @@ func (e *Expect) UnmarshalYAML(node *yaml.Node) error {
8484

8585
f, err := os.Open(schemaURL)
8686
if err != nil {
87-
return JSONSchemaFileNotFound(schemaURL)
87+
return JSONSchemaFileNotFound(schemaURL, valNode)
8888
}
8989
defer f.Close()
9090
if runtime.GOOS == "windows" {
@@ -105,10 +105,10 @@ func (e *Expect) UnmarshalYAML(node *yaml.Node) error {
105105
}
106106
for path, _ := range paths {
107107
if len(path) == 0 || path[0] != '$' {
108-
return JSONPathInvalidNoRoot(path)
108+
return JSONPathInvalidNoRoot(path, valNode)
109109
}
110110
if _, err := lang.NewEvaluable(path); err != nil {
111-
return JSONPathInvalid(path, err)
111+
return JSONPathInvalid(path, err, valNode)
112112
}
113113
}
114114
e.Paths = paths
@@ -122,10 +122,10 @@ func (e *Expect) UnmarshalYAML(node *yaml.Node) error {
122122
}
123123
for pathFormat, _ := range pathFormats {
124124
if len(pathFormat) == 0 || pathFormat[0] != '$' {
125-
return JSONPathInvalidNoRoot(pathFormat)
125+
return JSONPathInvalidNoRoot(pathFormat, valNode)
126126
}
127127
if _, err := lang.NewEvaluable(pathFormat); err != nil {
128-
return JSONPathInvalid(pathFormat, err)
128+
return JSONPathInvalid(pathFormat, err, valNode)
129129
}
130130
}
131131
e.PathFormats = pathFormats
@@ -228,7 +228,7 @@ func (a *assertions) pathsOK() bool {
228228
}
229229
v := interface{}(nil)
230230
if err := json.Unmarshal(a.content, &v); err != nil {
231-
a.Fail(JSONUnmarshalError(err))
231+
a.Fail(JSONUnmarshalError(err, nil))
232232
a.terminal = true
233233
return false
234234
}
@@ -299,7 +299,7 @@ func (a *assertions) pathFormatsOK() bool {
299299
}
300300
v := interface{}(nil)
301301
if e := json.Unmarshal(a.content, &v); e != nil {
302-
a.Fail(JSONUnmarshalError(e))
302+
a.Fail(JSONUnmarshalError(e, nil))
303303
a.terminal = true
304304
return false
305305
}

errors/parse.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,9 @@ func UnknownSourceType(source interface{}) error {
151151
}
152152

153153
// FileNotFound returns ErrFileNotFound for a given file path
154-
func FileNotFound(path string) error {
155-
return fmt.Errorf("%w: %s", ErrFileNotFound, path)
154+
func FileNotFound(path string, node *yaml.Node) error {
155+
return fmt.Errorf(
156+
"%w: %s at line %d, column %d",
157+
ErrFileNotFound, path, node.Line, node.Column,
158+
)
156159
}

plugin/exec/errors.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ func ExecEmpty(node *yaml.Node) error {
3535

3636
// ExecInvalidShellParse returns an ErrExecInvalid with the error from
3737
// shlex.Split
38-
func ExecInvalidShellParse(err error) error {
38+
func ExecInvalidShellParse(err error, node *yaml.Node) error {
3939
return fmt.Errorf(
40-
"%w: cannot parse shell args: %s",
41-
ErrExecInvalid, err,
40+
"%w: cannot parse shell args: %s at line %d, column %d",
41+
ErrExecInvalid, err, node.Line, node.Column,
4242
)
4343
}
4444

plugin/exec/parse.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ func (s *Spec) UnmarshalYAML(node *yaml.Node) error {
3737
if node.Kind != yaml.MappingNode {
3838
return errors.ExpectedMapAt(node)
3939
}
40+
var execValNode *yaml.Node
4041
// maps/structs are stored in a top-level Node.Content field which is a
4142
// concatenated slice of Node pointers in pairs of key/values.
4243
for i := 0; i < len(node.Content); i += 2 {
@@ -59,6 +60,7 @@ func (s *Spec) UnmarshalYAML(node *yaml.Node) error {
5960
if valNode.Kind != yaml.ScalarNode {
6061
return errors.ExpectedScalarAt(valNode)
6162
}
63+
execValNode = valNode
6264
s.Exec = strings.TrimSpace(valNode.Value)
6365
if s.Exec == "" {
6466
return ExecEmpty(valNode)
@@ -94,7 +96,7 @@ func (s *Spec) UnmarshalYAML(node *yaml.Node) error {
9496
if s.Shell != "" {
9597
_, err := shlex.Split(s.Exec)
9698
if err != nil {
97-
return ExecInvalidShellParse(err)
99+
return ExecInvalidShellParse(err, execValNode)
98100
}
99101
}
100102
return nil

0 commit comments

Comments
 (0)