1 package validator_test
2
3 import (
4 "fmt"
5 "io/ioutil"
6 "os"
7 "path/filepath"
8 "regexp"
9 "sort"
10 "strconv"
11 "strings"
12 "testing"
13
14 "github.com/stretchr/testify/require"
15 "github.com/vektah/gqlparser"
16 "github.com/vektah/gqlparser/ast"
17 "github.com/vektah/gqlparser/gqlerror"
18 "gopkg.in/yaml.v2"
19 )
20
21 type Spec struct {
22 Name string
23 Rule string
24 Schema string
25 Query string
26 Errors gqlerror.List
27 }
28
29 type Deviation struct {
30 Rule string
31 Errors []*gqlerror.Error
32 Skip string
33
34 pattern *regexp.Regexp
35 }
36
37 func TestValidation(t *testing.T) {
38 var rawSchemas []string
39 readYaml("./imported/spec/schemas.yml", &rawSchemas)
40
41 var deviations []*Deviation
42 readYaml("./imported/deviations.yml", &deviations)
43 for _, d := range deviations {
44 d.pattern = regexp.MustCompile("^" + d.Rule + "$")
45 }
46
47 var schemas []*ast.Schema
48 for i, schema := range rawSchemas {
49 schema, err := gqlparser.LoadSchema(&ast.Source{Input: schema, Name: fmt.Sprintf("schemas.yml[%d]", i)})
50 if err != nil {
51 panic(err)
52 }
53 schemas = append(schemas, schema)
54 }
55
56 err := filepath.Walk("./", func(path string, info os.FileInfo, err error) error {
57 if info.IsDir() || !strings.HasSuffix(path, ".spec.yml") {
58 return nil
59 }
60
61 runSpec(t, schemas, deviations, path)
62 return nil
63 })
64 require.NoError(t, err)
65 }
66
67 func runSpec(t *testing.T, schemas []*ast.Schema, deviations []*Deviation, filename string) {
68 ruleName := strings.TrimSuffix(filepath.Base(filename), ".spec.yml")
69
70 var specs []Spec
71 readYaml(filename, &specs)
72 t.Run(ruleName, func(t *testing.T) {
73 for _, spec := range specs {
74 if len(spec.Errors) == 0 {
75 spec.Errors = nil
76 }
77 t.Run(spec.Name, func(t *testing.T) {
78 for _, deviation := range deviations {
79 if deviation.pattern.MatchString(ruleName + "/" + spec.Name) {
80 if deviation.Skip != "" {
81 t.Skip(deviation.Skip)
82 }
83 if deviation.Errors != nil {
84 spec.Errors = deviation.Errors
85 }
86 }
87 }
88
89
90 var schema *ast.Schema
91 if idx, err := strconv.Atoi(spec.Schema); err != nil {
92 var gqlErr *gqlerror.Error
93 schema, gqlErr = gqlparser.LoadSchema(&ast.Source{Input: spec.Schema, Name: spec.Name})
94 if gqlErr != nil {
95 t.Fatal(err)
96 }
97 } else {
98 schema = schemas[idx]
99 }
100 _, err := gqlparser.LoadQuery(schema, spec.Query)
101 var finalErrors gqlerror.List
102 for _, err := range err {
103
104 if spec.Rule != "" && err.Rule != spec.Rule {
105 continue
106 }
107 finalErrors = append(finalErrors, err)
108 }
109
110 for i := range spec.Errors {
111 spec.Errors[i].Rule = spec.Rule
112
113
114 spec.Errors[i].Message = strings.Replace(spec.Errors[i].Message, "; Did you mean", ". Did you mean", -1)
115 }
116 sort.Slice(spec.Errors, func(i, j int) bool {
117 return strings.Compare(spec.Errors[i].Message, spec.Errors[j].Message) > 0
118 })
119 sort.Slice(finalErrors, func(i, j int) bool {
120 return strings.Compare(finalErrors[i].Message, finalErrors[j].Message) > 0
121 })
122
123 if len(finalErrors) != len(spec.Errors) {
124 t.Errorf("wrong number of errors returned\ngot:\n%s\nwant:\n%s", finalErrors.Error(), spec.Errors)
125 } else {
126 for i := range spec.Errors {
127 expected := spec.Errors[i]
128 actual := finalErrors[i]
129 if actual.Rule != spec.Rule {
130 continue
131 }
132 var errLocs []string
133 if expected.Message != actual.Message {
134 errLocs = append(errLocs, "message mismatch")
135 }
136 if len(expected.Locations) > 0 && len(actual.Locations) == 0 {
137 errLocs = append(errLocs, "missing location")
138 }
139 if len(expected.Locations) > 0 && len(actual.Locations) > 0 {
140 found := false
141 for _, loc := range expected.Locations {
142 if actual.Locations[0].Line == loc.Line {
143 found = true
144 break
145 }
146 }
147
148 if !found {
149 errLocs = append(errLocs, "line")
150 }
151 }
152
153 if len(errLocs) > 0 {
154 t.Errorf("%s\ngot: %s\nwant: %s", strings.Join(errLocs, ", "), finalErrors[i].Error(), spec.Errors[i].Error())
155 }
156 }
157 }
158
159 if t.Failed() {
160 t.Logf("name: '%s'", spec.Name)
161 t.Log("\nquery:", spec.Query)
162 }
163 })
164 }
165 })
166 }
167
168 func readYaml(filename string, result interface{}) {
169 b, err := ioutil.ReadFile(filename)
170 if err != nil {
171 panic(err)
172 }
173 err = yaml.Unmarshal(b, result)
174 if err != nil {
175 panic(fmt.Errorf("unable to load %s: %s", filename, err.Error()))
176 }
177 }
178
View as plain text