1 package config
2
3 import (
4 "errors"
5 "io/fs"
6 "os"
7 "path/filepath"
8 "runtime"
9 "strings"
10 "testing"
11
12 "github.com/stretchr/testify/assert"
13 "github.com/stretchr/testify/require"
14 "github.com/vektah/gqlparser/v2"
15 "github.com/vektah/gqlparser/v2/ast"
16
17 "github.com/99designs/gqlgen/internal/code"
18 )
19
20 func TestLoadConfig(t *testing.T) {
21 t.Run("config does not exist", func(t *testing.T) {
22 _, err := LoadConfig("doesnotexist.yml")
23 require.Error(t, err)
24 })
25 }
26
27 func TestReadConfig(t *testing.T) {
28 t.Run("empty config", func(t *testing.T) {
29 _, err := ReadConfig(strings.NewReader(""))
30 require.EqualError(t, err, "unable to parse config: EOF")
31 })
32
33 t.Run("malformed config", func(t *testing.T) {
34 cfgFile, _ := os.Open("testdata/cfg/malformedconfig.yml")
35 _, err := ReadConfig(cfgFile)
36 require.EqualError(t, err, "unable to parse config: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `asdf` into config.Config")
37 })
38
39 t.Run("unknown keys", func(t *testing.T) {
40 cfgFile, _ := os.Open("testdata/cfg/unknownkeys.yml")
41 _, err := ReadConfig(cfgFile)
42 require.EqualError(t, err, "unable to parse config: yaml: unmarshal errors:\n line 2: field unknown not found in type config.Config")
43 })
44
45 t.Run("globbed filenames", func(t *testing.T) {
46 cfgFile, _ := os.Open("testdata/cfg/glob.yml")
47 c, err := ReadConfig(cfgFile)
48 require.NoError(t, err)
49
50 if runtime.GOOS == "windows" {
51 require.Equal(t, c.SchemaFilename[0], `testdata\cfg\glob\bar\bar with spaces.graphql`)
52 require.Equal(t, c.SchemaFilename[1], `testdata\cfg\glob\foo\foo.graphql`)
53 } else {
54 require.Equal(t, c.SchemaFilename[0], "testdata/cfg/glob/bar/bar with spaces.graphql")
55 require.Equal(t, c.SchemaFilename[1], "testdata/cfg/glob/foo/foo.graphql")
56 }
57 })
58
59 t.Run("unwalkable path", func(t *testing.T) {
60 cfgFile, _ := os.Open("testdata/cfg/unwalkable.yml")
61 _, err := ReadConfig(cfgFile)
62 if runtime.GOOS == "windows" {
63 require.EqualError(t, err, "failed to walk schema at root not_walkable/: CreateFile not_walkable/: The system cannot find the file specified.")
64 } else {
65 require.EqualError(t, err, "failed to walk schema at root not_walkable/: lstat not_walkable/: no such file or directory")
66 }
67 })
68 }
69
70 func TestLoadConfigFromDefaultLocation(t *testing.T) {
71 testDir, err := os.Getwd()
72 require.NoError(t, err)
73 var cfg *Config
74
75 t.Run("will find closest match", func(t *testing.T) {
76 err = os.Chdir(filepath.Join(testDir, "testdata", "cfg", "subdir"))
77 require.NoError(t, err)
78
79 cfg, err = LoadConfigFromDefaultLocations()
80 require.NoError(t, err)
81 require.Equal(t, StringList{"inner"}, cfg.SchemaFilename)
82 })
83
84 t.Run("will find config in parent dirs", func(t *testing.T) {
85 err = os.Chdir(filepath.Join(testDir, "testdata", "cfg", "otherdir"))
86 require.NoError(t, err)
87
88 cfg, err = LoadConfigFromDefaultLocations()
89 require.NoError(t, err)
90 require.Equal(t, StringList{"outer"}, cfg.SchemaFilename)
91 })
92
93 t.Run("will return error if config doesn't exist", func(t *testing.T) {
94 err = os.Chdir(testDir)
95 require.NoError(t, err)
96
97 cfg, err = LoadConfigFromDefaultLocations()
98 require.True(t, errors.Is(err, fs.ErrNotExist))
99 })
100 }
101
102 func TestLoadDefaultConfig(t *testing.T) {
103 testDir, err := os.Getwd()
104 require.NoError(t, err)
105 var cfg *Config
106
107 t.Run("will find the schema", func(t *testing.T) {
108 err = os.Chdir(filepath.Join(testDir, "testdata", "defaultconfig"))
109 require.NoError(t, err)
110
111 cfg, err = LoadDefaultConfig()
112 require.NoError(t, err)
113 require.NotEmpty(t, cfg.Sources)
114 })
115
116 t.Run("will return error if schema doesn't exist", func(t *testing.T) {
117 err = os.Chdir(testDir)
118 require.NoError(t, err)
119
120 cfg, err = LoadDefaultConfig()
121 require.True(t, errors.Is(err, fs.ErrNotExist))
122 })
123 }
124
125 func TestReferencedPackages(t *testing.T) {
126 t.Run("valid", func(t *testing.T) {
127 tm := TypeMap{
128 "Foo": {Model: StringList{"github.com/test.Foo"}},
129 "Bar": {Model: StringList{"github.com/test.Bar"}},
130 "Baz": {Model: StringList{"github.com/otherpkg.Baz"}},
131 "Map": {Model: StringList{"map[string]interface{}"}},
132 "SkipResolver": {
133 Fields: map[string]TypeMapField{
134 "field": {Resolver: false},
135 },
136 },
137 }
138
139 pkgs := tm.ReferencedPackages()
140
141 assert.Equal(t, []string{"github.com/test", "github.com/otherpkg"}, pkgs)
142 })
143 }
144
145 func TestConfigCheck(t *testing.T) {
146 for _, execLayout := range []ExecLayout{ExecLayoutSingleFile, ExecLayoutFollowSchema} {
147 t.Run(string(execLayout), func(t *testing.T) {
148 t.Run("invalid config format due to conflicting package names", func(t *testing.T) {
149 config := Config{
150 Exec: ExecConfig{Layout: execLayout, Filename: "generated/exec.go", DirName: "generated", Package: "graphql"},
151 Model: PackageConfig{Filename: "generated/models.go"},
152 }
153
154 require.EqualError(t, config.check(), "exec and model define the same import path (github.com/99designs/gqlgen/codegen/config/generated) with different package names (graphql vs generated)")
155 })
156
157 t.Run("federation must be in exec package", func(t *testing.T) {
158 config := Config{
159 Exec: ExecConfig{Layout: execLayout, Filename: "generated/exec.go", DirName: "generated"},
160 Federation: PackageConfig{Filename: "anotherpkg/federation.go"},
161 }
162
163 require.EqualError(t, config.check(), "federation and exec must be in the same package")
164 })
165
166 t.Run("federation must have same package name as exec", func(t *testing.T) {
167 config := Config{
168 Exec: ExecConfig{Layout: execLayout, Filename: "generated/exec.go", DirName: "generated"},
169 Federation: PackageConfig{Filename: "generated/federation.go", Package: "federation"},
170 }
171
172 require.EqualError(t, config.check(), "exec and federation define the same import path (github.com/99designs/gqlgen/codegen/config/generated) with different package names (generated vs federation)")
173 })
174
175 t.Run("deprecated federated flag raises an error", func(t *testing.T) {
176 config := Config{
177 Exec: ExecConfig{Layout: execLayout, Filename: "generated/exec.go", DirName: "generated"},
178 Federated: true,
179 }
180
181 require.EqualError(t, config.check(), "federated has been removed, instead use\nfederation:\n filename: path/to/federated.go")
182 })
183 })
184 }
185 }
186
187 func TestAutobinding(t *testing.T) {
188 t.Run("valid paths", func(t *testing.T) {
189 cfg := Config{
190 Models: TypeMap{},
191 AutoBind: []string{
192 "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/chat",
193 "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/scalars/model",
194 },
195 Packages: code.NewPackages(),
196 }
197
198 cfg.Schema = gqlparser.MustLoadSchema(&ast.Source{Name: "TestAutobinding.schema", Input: `
199 scalar Banned
200 type Message { id: ID }
201 `})
202
203 require.NoError(t, cfg.autobind())
204
205 require.Equal(t, "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/scalars/model.Banned", cfg.Models["Banned"].Model[0])
206 require.Equal(t, "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/chat.Message", cfg.Models["Message"].Model[0])
207 })
208
209 t.Run("normalized type names", func(t *testing.T) {
210 cfg := Config{
211 Models: TypeMap{},
212 AutoBind: []string{
213 "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/chat",
214 "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/scalars/model",
215 },
216 Packages: code.NewPackages(),
217 }
218
219 cfg.Schema = gqlparser.MustLoadSchema(&ast.Source{Name: "TestAutobinding.schema", Input: `
220 scalar Banned
221 type Message { id: ID }
222 enum ProductSKU { ProductSkuTrial }
223 type ChatAPI { id: ID }
224 `})
225
226 require.NoError(t, cfg.autobind())
227
228 require.Equal(t, "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/scalars/model.Banned", cfg.Models["Banned"].Model[0])
229 require.Equal(t, "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/chat.Message", cfg.Models["Message"].Model[0])
230 require.Equal(t, "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/chat.ProductSku", cfg.Models["ProductSKU"].Model[0])
231 require.Equal(t, "github.com/99designs/gqlgen/codegen/config/testdata/autobinding/chat.ChatAPI", cfg.Models["ChatAPI"].Model[0])
232 })
233
234 t.Run("with file path", func(t *testing.T) {
235 cfg := Config{
236 Models: TypeMap{},
237 AutoBind: []string{
238 "../chat",
239 },
240 Packages: code.NewPackages(),
241 }
242
243 cfg.Schema = gqlparser.MustLoadSchema(&ast.Source{Name: "TestAutobinding.schema", Input: `
244 scalar Banned
245 type Message { id: ID }
246 `})
247
248 require.EqualError(t, cfg.autobind(), "unable to load ../chat - make sure you're using an import path to a package that exists")
249 })
250 }
251
View as plain text