github.com/geneva/gqlgen@v0.17.7-0.20230801155730-7b9317164836/plugin/modelgen/models_test.go (about)

     1  package modelgen
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/parser"
     8  	"go/token"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"reflect"
    13  	"sort"
    14  	"strings"
    15  	"testing"
    16  
    17  	"github.com/geneva/gqlgen/codegen/config"
    18  	"github.com/geneva/gqlgen/graphql"
    19  	"github.com/geneva/gqlgen/plugin/modelgen/internal/extrafields"
    20  	"github.com/geneva/gqlgen/plugin/modelgen/out"
    21  	"github.com/geneva/gqlgen/plugin/modelgen/out_enable_model_json_omitempty_tag_false"
    22  	"github.com/geneva/gqlgen/plugin/modelgen/out_enable_model_json_omitempty_tag_nil"
    23  	"github.com/geneva/gqlgen/plugin/modelgen/out_enable_model_json_omitempty_tag_true"
    24  	"github.com/geneva/gqlgen/plugin/modelgen/out_nullable_input_omittable"
    25  	"github.com/geneva/gqlgen/plugin/modelgen/out_struct_pointers"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  )
    29  
    30  func TestModelGeneration(t *testing.T) {
    31  	cfg, err := config.LoadConfig("testdata/gqlgen.yml")
    32  	require.NoError(t, err)
    33  	require.NoError(t, cfg.Init())
    34  	p := Plugin{
    35  		MutateHook: mutateHook,
    36  		FieldHook:  DefaultFieldMutateHook,
    37  	}
    38  	require.NoError(t, p.MutateConfig(cfg))
    39  	require.NoError(t, goBuild(t, "./out/"))
    40  
    41  	require.True(t, cfg.Models.UserDefined("MissingTypeNotNull"))
    42  	require.True(t, cfg.Models.UserDefined("MissingTypeNullable"))
    43  	require.True(t, cfg.Models.UserDefined("MissingEnum"))
    44  	require.True(t, cfg.Models.UserDefined("MissingUnion"))
    45  	require.True(t, cfg.Models.UserDefined("MissingInterface"))
    46  	require.True(t, cfg.Models.UserDefined("TypeWithDescription"))
    47  	require.True(t, cfg.Models.UserDefined("EnumWithDescription"))
    48  	require.True(t, cfg.Models.UserDefined("InterfaceWithDescription"))
    49  	require.True(t, cfg.Models.UserDefined("UnionWithDescription"))
    50  	require.True(t, cfg.Models.UserDefined("RenameFieldTest"))
    51  	require.True(t, cfg.Models.UserDefined("ExtraFieldsTest"))
    52  
    53  	t.Run("no pointer pointers", func(t *testing.T) {
    54  		generated, err := os.ReadFile("./out/generated.go")
    55  		require.NoError(t, err)
    56  		require.NotContains(t, string(generated), "**")
    57  	})
    58  
    59  	t.Run("description is generated", func(t *testing.T) {
    60  		node, err := parser.ParseFile(token.NewFileSet(), "./out/generated.go", nil, parser.ParseComments)
    61  		require.NoError(t, err)
    62  		for _, commentGroup := range node.Comments {
    63  			text := commentGroup.Text()
    64  			words := strings.Split(text, " ")
    65  			require.True(t, len(words) > 1, "expected description %q to have more than one word", text)
    66  		}
    67  	})
    68  
    69  	t.Run("tags are applied", func(t *testing.T) {
    70  		file, err := os.ReadFile("./out/generated.go")
    71  		require.NoError(t, err)
    72  
    73  		fileText := string(file)
    74  
    75  		expectedTags := []string{
    76  			`json:"missing2" database:"MissingTypeNotNullmissing2"`,
    77  			`json:"name,omitempty" database:"MissingInputname"`,
    78  			`json:"missing2,omitempty" database:"MissingTypeNullablemissing2"`,
    79  			`json:"name,omitempty" database:"TypeWithDescriptionname"`,
    80  		}
    81  
    82  		for _, tag := range expectedTags {
    83  			require.True(t, strings.Contains(fileText, tag), "\nexpected:\n"+tag+"\ngot\n"+fileText)
    84  		}
    85  	})
    86  
    87  	t.Run("field hooks are applied", func(t *testing.T) {
    88  		file, err := os.ReadFile("./out/generated.go")
    89  		require.NoError(t, err)
    90  
    91  		fileText := string(file)
    92  
    93  		expectedTags := []string{
    94  			`json:"name,omitempty" anotherTag:"tag"`,
    95  			`json:"enum,omitempty" yetAnotherTag:"12"`,
    96  			`json:"noVal,omitempty" yaml:"noVal" repeated:"true"`,
    97  			`json:"repeated,omitempty" someTag:"value" repeated:"true"`,
    98  		}
    99  
   100  		for _, tag := range expectedTags {
   101  			require.True(t, strings.Contains(fileText, tag), "\nexpected:\n"+tag+"\ngot\n"+fileText)
   102  		}
   103  	})
   104  
   105  	t.Run("concrete types implement interface", func(t *testing.T) {
   106  		var _ out.FooBarer = out.FooBarr{}
   107  	})
   108  
   109  	t.Run("implemented interfaces", func(t *testing.T) {
   110  		pkg, err := parseAst("out")
   111  		require.NoError(t, err)
   112  
   113  		path := filepath.Join("out", "generated.go")
   114  		generated := pkg.Files[path]
   115  
   116  		type field struct {
   117  			typ  string
   118  			name string
   119  		}
   120  		cases := []struct {
   121  			name       string
   122  			wantFields []field
   123  		}{
   124  			{
   125  				name: "A",
   126  				wantFields: []field{
   127  					{
   128  						typ:  "method",
   129  						name: "IsA",
   130  					},
   131  					{
   132  						typ:  "method",
   133  						name: "GetA",
   134  					},
   135  				},
   136  			},
   137  			{
   138  				name: "B",
   139  				wantFields: []field{
   140  					{
   141  						typ:  "method",
   142  						name: "IsB",
   143  					},
   144  					{
   145  						typ:  "method",
   146  						name: "GetB",
   147  					},
   148  				},
   149  			},
   150  			{
   151  				name: "C",
   152  				wantFields: []field{
   153  					{
   154  						typ:  "method",
   155  						name: "IsA",
   156  					},
   157  					{
   158  						typ:  "method",
   159  						name: "IsC",
   160  					},
   161  					{
   162  						typ:  "method",
   163  						name: "GetA",
   164  					},
   165  					{
   166  						typ:  "method",
   167  						name: "GetC",
   168  					},
   169  				},
   170  			},
   171  			{
   172  				name: "D",
   173  				wantFields: []field{
   174  					{
   175  						typ:  "method",
   176  						name: "IsA",
   177  					},
   178  					{
   179  						typ:  "method",
   180  						name: "IsB",
   181  					},
   182  					{
   183  						typ:  "method",
   184  						name: "IsD",
   185  					},
   186  					{
   187  						typ:  "method",
   188  						name: "GetA",
   189  					},
   190  					{
   191  						typ:  "method",
   192  						name: "GetB",
   193  					},
   194  					{
   195  						typ:  "method",
   196  						name: "GetD",
   197  					},
   198  				},
   199  			},
   200  		}
   201  		for _, tc := range cases {
   202  			tc := tc
   203  			t.Run(tc.name, func(t *testing.T) {
   204  				typeSpec, ok := generated.Scope.Lookup(tc.name).Decl.(*ast.TypeSpec)
   205  				require.True(t, ok)
   206  
   207  				fields := typeSpec.Type.(*ast.InterfaceType).Methods.List
   208  				for i, want := range tc.wantFields {
   209  					if want.typ == "ident" {
   210  						ident, ok := fields[i].Type.(*ast.Ident)
   211  						require.True(t, ok)
   212  						assert.Equal(t, want.name, ident.Name)
   213  					}
   214  					if want.typ == "method" {
   215  						require.GreaterOrEqual(t, 1, len(fields[i].Names))
   216  						name := fields[i].Names[0].Name
   217  						assert.Equal(t, want.name, name)
   218  					}
   219  				}
   220  			})
   221  		}
   222  	})
   223  
   224  	t.Run("implemented interfaces type CDImplemented", func(t *testing.T) {
   225  		pkg, err := parseAst("out")
   226  		require.NoError(t, err)
   227  
   228  		path := filepath.Join("out", "generated.go")
   229  		generated := pkg.Files[path]
   230  
   231  		wantMethods := []string{
   232  			"IsA",
   233  			"IsB",
   234  			"IsC",
   235  			"IsD",
   236  		}
   237  
   238  		gots := make([]string, 0, len(wantMethods))
   239  		for _, decl := range generated.Decls {
   240  			if funcDecl, ok := decl.(*ast.FuncDecl); ok {
   241  				switch funcDecl.Name.Name {
   242  				case "IsA", "IsB", "IsC", "IsD":
   243  					gots = append(gots, funcDecl.Name.Name)
   244  					require.Len(t, funcDecl.Recv.List, 1)
   245  					recvIdent, ok := funcDecl.Recv.List[0].Type.(*ast.Ident)
   246  					require.True(t, ok)
   247  					require.Equal(t, "CDImplemented", recvIdent.Name)
   248  				}
   249  			}
   250  		}
   251  
   252  		sort.Strings(gots)
   253  		require.Equal(t, wantMethods, gots)
   254  	})
   255  
   256  	t.Run("cyclical struct fields become pointers", func(t *testing.T) {
   257  		require.Nil(t, out.CyclicalA{}.FieldOne)
   258  		require.Nil(t, out.CyclicalA{}.FieldTwo)
   259  		require.Nil(t, out.CyclicalA{}.FieldThree)
   260  		require.NotNil(t, out.CyclicalA{}.FieldFour)
   261  		require.Nil(t, out.CyclicalB{}.FieldOne)
   262  		require.Nil(t, out.CyclicalB{}.FieldTwo)
   263  		require.Nil(t, out.CyclicalB{}.FieldThree)
   264  		require.Nil(t, out.CyclicalB{}.FieldFour)
   265  		require.NotNil(t, out.CyclicalB{}.FieldFive)
   266  	})
   267  
   268  	t.Run("non-cyclical struct fields become pointers", func(t *testing.T) {
   269  		require.NotNil(t, out.NotCyclicalB{}.FieldOne)
   270  		require.Nil(t, out.NotCyclicalB{}.FieldTwo)
   271  	})
   272  
   273  	t.Run("recursive struct fields become pointers", func(t *testing.T) {
   274  		require.Nil(t, out.Recursive{}.FieldOne)
   275  		require.Nil(t, out.Recursive{}.FieldTwo)
   276  		require.Nil(t, out.Recursive{}.FieldThree)
   277  		require.NotNil(t, out.Recursive{}.FieldFour)
   278  	})
   279  
   280  	t.Run("overridden struct field names use same capitalization as config", func(t *testing.T) {
   281  		require.NotNil(t, out.RenameFieldTest{}.GOODnaME)
   282  	})
   283  
   284  	t.Run("nullable input fields can be made omittable with goField", func(t *testing.T) {
   285  		require.IsType(t, out.MissingInput{}.NullString, graphql.Omittable[*string]{})
   286  		require.IsType(t, out.MissingInput{}.NullEnum, graphql.Omittable[*out.MissingEnum]{})
   287  		require.IsType(t, out.MissingInput{}.NullObject, graphql.Omittable[*out.ExistingInput]{})
   288  	})
   289  
   290  	t.Run("extra fields are present", func(t *testing.T) {
   291  		var m out.ExtraFieldsTest
   292  
   293  		require.IsType(t, m.FieldInt, int64(0))
   294  		require.IsType(t, m.FieldInternalType, extrafields.Type{})
   295  		require.IsType(t, m.FieldStringPtr, new(string))
   296  		require.IsType(t, m.FieldIntSlice, []int64{})
   297  	})
   298  }
   299  
   300  func TestModelGenerationStructFieldPointers(t *testing.T) {
   301  	cfg, err := config.LoadConfig("testdata/gqlgen_struct_field_pointers.yml")
   302  	require.NoError(t, err)
   303  	require.NoError(t, cfg.Init())
   304  	p := Plugin{
   305  		MutateHook: mutateHook,
   306  		FieldHook:  DefaultFieldMutateHook,
   307  	}
   308  	require.NoError(t, p.MutateConfig(cfg))
   309  
   310  	t.Run("no pointer pointers", func(t *testing.T) {
   311  		generated, err := os.ReadFile("./out_struct_pointers/generated.go")
   312  		require.NoError(t, err)
   313  		require.NotContains(t, string(generated), "**")
   314  	})
   315  
   316  	t.Run("cyclical struct fields become pointers", func(t *testing.T) {
   317  		require.Nil(t, out_struct_pointers.CyclicalA{}.FieldOne)
   318  		require.Nil(t, out_struct_pointers.CyclicalA{}.FieldTwo)
   319  		require.Nil(t, out_struct_pointers.CyclicalA{}.FieldThree)
   320  		require.NotNil(t, out_struct_pointers.CyclicalA{}.FieldFour)
   321  		require.Nil(t, out_struct_pointers.CyclicalB{}.FieldOne)
   322  		require.Nil(t, out_struct_pointers.CyclicalB{}.FieldTwo)
   323  		require.Nil(t, out_struct_pointers.CyclicalB{}.FieldThree)
   324  		require.Nil(t, out_struct_pointers.CyclicalB{}.FieldFour)
   325  		require.NotNil(t, out_struct_pointers.CyclicalB{}.FieldFive)
   326  	})
   327  
   328  	t.Run("non-cyclical struct fields do not become pointers", func(t *testing.T) {
   329  		require.NotNil(t, out_struct_pointers.NotCyclicalB{}.FieldOne)
   330  		require.NotNil(t, out_struct_pointers.NotCyclicalB{}.FieldTwo)
   331  	})
   332  
   333  	t.Run("recursive struct fields become pointers", func(t *testing.T) {
   334  		require.Nil(t, out_struct_pointers.Recursive{}.FieldOne)
   335  		require.Nil(t, out_struct_pointers.Recursive{}.FieldTwo)
   336  		require.Nil(t, out_struct_pointers.Recursive{}.FieldThree)
   337  		require.NotNil(t, out_struct_pointers.Recursive{}.FieldFour)
   338  	})
   339  
   340  	t.Run("no getters", func(t *testing.T) {
   341  		generated, err := os.ReadFile("./out_struct_pointers/generated.go")
   342  		require.NoError(t, err)
   343  		require.NotContains(t, string(generated), "func (this")
   344  	})
   345  }
   346  
   347  func TestModelGenerationNullableInputOmittable(t *testing.T) {
   348  	cfg, err := config.LoadConfig("testdata/gqlgen_nullable_input_omittable.yml")
   349  	require.NoError(t, err)
   350  	require.NoError(t, cfg.Init())
   351  	p := Plugin{
   352  		MutateHook: mutateHook,
   353  		FieldHook:  DefaultFieldMutateHook,
   354  	}
   355  	require.NoError(t, p.MutateConfig(cfg))
   356  
   357  	t.Run("nullable input fields are omittable", func(t *testing.T) {
   358  		require.IsType(t, out_nullable_input_omittable.MissingInput{}.Name, graphql.Omittable[*string]{})
   359  		require.IsType(t, out_nullable_input_omittable.MissingInput{}.Enum, graphql.Omittable[*out_nullable_input_omittable.MissingEnum]{})
   360  		require.IsType(t, out_nullable_input_omittable.MissingInput{}.NullString, graphql.Omittable[*string]{})
   361  		require.IsType(t, out_nullable_input_omittable.MissingInput{}.NullEnum, graphql.Omittable[*out_nullable_input_omittable.MissingEnum]{})
   362  		require.IsType(t, out_nullable_input_omittable.MissingInput{}.NullObject, graphql.Omittable[*out_nullable_input_omittable.ExistingInput]{})
   363  	})
   364  
   365  	t.Run("non-nullable input fields are not omittable", func(t *testing.T) {
   366  		require.IsType(t, out_nullable_input_omittable.MissingInput{}.NonNullString, "")
   367  	})
   368  }
   369  
   370  func TestModelGenerationOmitemptyConfig(t *testing.T) {
   371  	suites := []struct {
   372  		n       string
   373  		cfg     string
   374  		enabled bool
   375  		t       any
   376  	}{
   377  		{
   378  			n:       "nil",
   379  			cfg:     "gqlgen_enable_model_json_omitempty_tag_nil.yml",
   380  			enabled: true,
   381  			t:       out_enable_model_json_omitempty_tag_nil.OmitEmptyJSONTagTest{},
   382  		},
   383  		{
   384  			n:       "true",
   385  			cfg:     "gqlgen_enable_model_json_omitempty_tag_true.yml",
   386  			enabled: true,
   387  			t:       out_enable_model_json_omitempty_tag_true.OmitEmptyJSONTagTest{},
   388  		},
   389  		{
   390  			n:       "false",
   391  			cfg:     "gqlgen_enable_model_json_omitempty_tag_false.yml",
   392  			enabled: false,
   393  			t:       out_enable_model_json_omitempty_tag_false.OmitEmptyJSONTagTest{},
   394  		},
   395  	}
   396  
   397  	for _, s := range suites {
   398  		t.Run(s.n, func(t *testing.T) {
   399  			cfg, err := config.LoadConfig(fmt.Sprintf("testdata/%s", s.cfg))
   400  			require.NoError(t, err)
   401  			require.NoError(t, cfg.Init())
   402  			p := Plugin{
   403  				MutateHook: mutateHook,
   404  				FieldHook:  DefaultFieldMutateHook,
   405  			}
   406  			require.NoError(t, p.MutateConfig(cfg))
   407  			rt := reflect.TypeOf(s.t)
   408  
   409  			// ensure non-nullable fields are never omitempty
   410  			sfn, ok := rt.FieldByName("ValueNonNil")
   411  			require.True(t, ok)
   412  			require.Equal(t, "ValueNonNil", sfn.Tag.Get("json"))
   413  
   414  			// test nullable fields for configured omitempty
   415  			sf, ok := rt.FieldByName("Value")
   416  			require.True(t, ok)
   417  
   418  			var expected string
   419  			if s.enabled {
   420  				expected = "Value,omitempty"
   421  			} else {
   422  				expected = "Value"
   423  			}
   424  			require.Equal(t, expected, sf.Tag.Get("json"))
   425  		})
   426  	}
   427  }
   428  
   429  func mutateHook(b *ModelBuild) *ModelBuild {
   430  	for _, model := range b.Models {
   431  		for _, field := range model.Fields {
   432  			field.Tag += ` database:"` + model.Name + field.Name + `"`
   433  		}
   434  	}
   435  
   436  	return b
   437  }
   438  
   439  func parseAst(path string) (*ast.Package, error) {
   440  	// test setup to parse the types
   441  	fset := token.NewFileSet()
   442  	pkgs, err := parser.ParseDir(fset, path, nil, parser.AllErrors)
   443  	if err != nil {
   444  		return nil, err
   445  	}
   446  	return pkgs["out"], nil
   447  }
   448  
   449  func goBuild(t *testing.T, path string) error {
   450  	t.Helper()
   451  	cmd := exec.Command("go", "build", path)
   452  	out, err := cmd.CombinedOutput()
   453  	if err != nil {
   454  		return errors.New(string(out))
   455  	}
   456  
   457  	return nil
   458  }
   459  
   460  func TestRemoveDuplicate(t *testing.T) {
   461  	type args struct {
   462  		t string
   463  	}
   464  	tests := []struct {
   465  		name      string
   466  		args      args
   467  		want      string
   468  		wantPanic bool
   469  	}{
   470  		{
   471  			name: "Duplicate Test with 1",
   472  			args: args{
   473  				t: "json:\"name\"",
   474  			},
   475  			want: "json:\"name\"",
   476  		},
   477  		{
   478  			name: "Duplicate Test with 2",
   479  			args: args{
   480  				t: "json:\"name\" json:\"name2\"",
   481  			},
   482  			want: "json:\"name2\"",
   483  		},
   484  		{
   485  			name: "Duplicate Test with 3",
   486  			args: args{
   487  				t: "json:\"name\" json:\"name2\" json:\"name3\"",
   488  			},
   489  			want: "json:\"name3\"",
   490  		},
   491  		{
   492  			name: "Duplicate Test with 3 and 1 unrelated",
   493  			args: args{
   494  				t: "json:\"name\" something:\"name2\" json:\"name3\"",
   495  			},
   496  			want: "something:\"name2\" json:\"name3\"",
   497  		},
   498  		{
   499  			name: "Duplicate Test with 3 and 2 unrelated",
   500  			args: args{
   501  				t: "something:\"name1\" json:\"name\" something:\"name2\" json:\"name3\"",
   502  			},
   503  			want: "something:\"name2\" json:\"name3\"",
   504  		},
   505  		{
   506  			name: "Test tag value with leading empty space",
   507  			args: args{
   508  				t: "json:\"name, name2\"",
   509  			},
   510  			want:      "json:\"name, name2\"",
   511  			wantPanic: true,
   512  		},
   513  		{
   514  			name: "Test tag value with trailing empty space",
   515  			args: args{
   516  				t: "json:\"name,name2 \"",
   517  			},
   518  			want:      "json:\"name,name2 \"",
   519  			wantPanic: true,
   520  		},
   521  		{
   522  			name: "Test tag value with space in between",
   523  			args: args{
   524  				t: "gorm:\"unique;not null\"",
   525  			},
   526  			want:      "gorm:\"unique;not null\"",
   527  			wantPanic: false,
   528  		},
   529  		{
   530  			name: "Test mix use of gorm and json tags",
   531  			args: args{
   532  				t: "gorm:\"unique;not null\" json:\"name,name2\"",
   533  			},
   534  			want:      "gorm:\"unique;not null\" json:\"name,name2\"",
   535  			wantPanic: false,
   536  		},
   537  	}
   538  	for _, tt := range tests {
   539  		t.Run(tt.name, func(t *testing.T) {
   540  			if tt.wantPanic {
   541  				assert.Panics(t, func() { removeDuplicateTags(tt.args.t) }, "The code did not panic")
   542  			} else {
   543  				if got := removeDuplicateTags(tt.args.t); got != tt.want {
   544  					t.Errorf("removeDuplicate() = %v, want %v", got, tt.want)
   545  				}
   546  			}
   547  		})
   548  	}
   549  }
   550  
   551  func Test_containsInvalidSpace(t *testing.T) {
   552  	type args struct {
   553  		valuesString string
   554  	}
   555  	tests := []struct {
   556  		name string
   557  		args args
   558  		want bool
   559  	}{
   560  		{
   561  			name: "Test tag value with leading empty space",
   562  			args: args{
   563  				valuesString: "name, name2",
   564  			},
   565  			want: true,
   566  		},
   567  		{
   568  			name: "Test tag value with trailing empty space",
   569  			args: args{
   570  				valuesString: "name ,name2",
   571  			},
   572  			want: true,
   573  		},
   574  		{
   575  			name: "Test tag value with valid empty space in words",
   576  			args: args{
   577  				valuesString: "accept this,name2",
   578  			},
   579  			want: false,
   580  		},
   581  	}
   582  	for _, tt := range tests {
   583  		t.Run(tt.name, func(t *testing.T) {
   584  			assert.Equalf(t, tt.want, containsInvalidSpace(tt.args.valuesString), "containsInvalidSpace(%v)", tt.args.valuesString)
   585  		})
   586  	}
   587  }
   588  
   589  func Test_splitTagsBySpace(t *testing.T) {
   590  	type args struct {
   591  		tagsString string
   592  	}
   593  	tests := []struct {
   594  		name string
   595  		args args
   596  		want []string
   597  	}{
   598  		{
   599  			name: "multiple tags, single value",
   600  			args: args{
   601  				tagsString: "json:\"name\" something:\"name2\" json:\"name3\"",
   602  			},
   603  			want: []string{"json:\"name\"", "something:\"name2\"", "json:\"name3\""},
   604  		},
   605  		{
   606  			name: "multiple tag, multiple values",
   607  			args: args{
   608  				tagsString: "json:\"name\" something:\"name2\" json:\"name3,name4\"",
   609  			},
   610  			want: []string{"json:\"name\"", "something:\"name2\"", "json:\"name3,name4\""},
   611  		},
   612  		{
   613  			name: "single tag, single value",
   614  			args: args{
   615  				tagsString: "json:\"name\"",
   616  			},
   617  			want: []string{"json:\"name\""},
   618  		},
   619  		{
   620  			name: "single tag, multiple values",
   621  			args: args{
   622  				tagsString: "json:\"name,name2\"",
   623  			},
   624  			want: []string{"json:\"name,name2\""},
   625  		},
   626  		{
   627  			name: "space in value",
   628  			args: args{
   629  				tagsString: "gorm:\"not nul,name2\"",
   630  			},
   631  			want: []string{"gorm:\"not nul,name2\""},
   632  		},
   633  	}
   634  	for _, tt := range tests {
   635  		t.Run(tt.name, func(t *testing.T) {
   636  			assert.Equalf(t, tt.want, splitTagsBySpace(tt.args.tagsString), "splitTagsBySpace(%v)", tt.args.tagsString)
   637  		})
   638  	}
   639  }
   640  
   641  func TestCustomTemplate(t *testing.T) {
   642  	cfg, err := config.LoadConfig("testdata/gqlgen_custom_model_template.yml")
   643  	require.NoError(t, err)
   644  	require.NoError(t, cfg.Init())
   645  	p := Plugin{
   646  		MutateHook: mutateHook,
   647  		FieldHook:  DefaultFieldMutateHook,
   648  	}
   649  	require.NoError(t, p.MutateConfig(cfg))
   650  }