...

Source file src/github.com/99designs/gqlgen/plugin/modelgen/models_test.go

Documentation: github.com/99designs/gqlgen/plugin/modelgen

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

View as plain text