github.com/niko0xdev/gqlgen@v0.17.55-0.20240120102243-2ecff98c3e37/codegen/config/config_test.go (about)

     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/niko0xdev/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/niko0xdev/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/niko0xdev/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/niko0xdev/gqlgen/codegen/config/testdata/autobinding/chat",
   193  				"github.com/niko0xdev/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/niko0xdev/gqlgen/codegen/config/testdata/autobinding/scalars/model.Banned", cfg.Models["Banned"].Model[0])
   206  		require.Equal(t, "github.com/niko0xdev/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/niko0xdev/gqlgen/codegen/config/testdata/autobinding/chat",
   214  				"github.com/niko0xdev/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/niko0xdev/gqlgen/codegen/config/testdata/autobinding/scalars/model.Banned", cfg.Models["Banned"].Model[0])
   229  		require.Equal(t, "github.com/niko0xdev/gqlgen/codegen/config/testdata/autobinding/chat.Message", cfg.Models["Message"].Model[0])
   230  		require.Equal(t, "github.com/niko0xdev/gqlgen/codegen/config/testdata/autobinding/chat.ProductSku", cfg.Models["ProductSKU"].Model[0])
   231  		require.Equal(t, "github.com/niko0xdev/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  }