github.com/kerryoscer/gqlgen@v0.17.29/codegen/field_test.go (about)

     1  package codegen
     2  
     3  import (
     4  	"go/ast"
     5  	"go/importer"
     6  	"go/parser"
     7  	"go/token"
     8  	"go/types"
     9  	"testing"
    10  
    11  	"github.com/kerryoscer/gqlgen/codegen/config"
    12  	"github.com/stretchr/testify/require"
    13  	ast2 "github.com/vektah/gqlparser/v2/ast"
    14  )
    15  
    16  func TestFindField(t *testing.T) {
    17  	input := `
    18  package test
    19  
    20  type Std struct {
    21  	Name string
    22  	Value int
    23  }
    24  type Anon struct {
    25  	Name string
    26  	Tags
    27  }
    28  type Tags struct {
    29  	Bar string ` + "`" + `gqlgen:"foo"` + "`" + `
    30  	Foo int    ` + "`" + `gqlgen:"bar"` + "`" + `
    31  }
    32  type Amb struct {
    33  	Bar string ` + "`" + `gqlgen:"foo"` + "`" + `
    34  	Foo int    ` + "`" + `gqlgen:"foo"` + "`" + `
    35  }
    36  type Embed struct {
    37  	Std
    38  	Test string
    39  }
    40  `
    41  	scope, err := parseScope(input, "test")
    42  	require.NoError(t, err)
    43  
    44  	std := scope.Lookup("Std").Type().(*types.Named)
    45  	anon := scope.Lookup("Anon").Type().(*types.Named)
    46  	tags := scope.Lookup("Tags").Type().(*types.Named)
    47  	amb := scope.Lookup("Amb").Type().(*types.Named)
    48  	embed := scope.Lookup("Embed").Type().(*types.Named)
    49  
    50  	tests := []struct {
    51  		Name        string
    52  		Named       *types.Named
    53  		Field       string
    54  		Tag         string
    55  		Expected    string
    56  		ShouldError bool
    57  	}{
    58  		{"Finds a field by name with no tag", std, "name", "", "Name", false},
    59  		{"Finds a field by name when passed tag but tag not used", std, "name", "gqlgen", "Name", false},
    60  		{"Ignores tags when not passed a tag", tags, "foo", "", "Foo", false},
    61  		{"Picks field with tag over field name when passed a tag", tags, "foo", "gqlgen", "Bar", false},
    62  		{"Errors when ambigious", amb, "foo", "gqlgen", "", true},
    63  		{"Finds a field that is in embedded struct", anon, "bar", "", "Bar", false},
    64  		{"Finds field that is not in embedded struct", embed, "test", "", "Test", false},
    65  	}
    66  
    67  	for _, tt := range tests {
    68  		b := builder{Config: &config.Config{StructTag: tt.Tag}}
    69  		target, err := b.findBindTarget(tt.Named, tt.Field)
    70  		if tt.ShouldError {
    71  			require.Nil(t, target, tt.Name)
    72  			require.Error(t, err, tt.Name)
    73  		} else {
    74  			require.NoError(t, err, tt.Name)
    75  			require.Equal(t, tt.Expected, target.Name(), tt.Name)
    76  		}
    77  	}
    78  }
    79  
    80  func parseScope(input interface{}, packageName string) (*types.Scope, error) {
    81  	// test setup to parse the types
    82  	fset := token.NewFileSet()
    83  	f, err := parser.ParseFile(fset, "test.go", input, 0)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	conf := types.Config{Importer: importer.Default()}
    89  	pkg, err := conf.Check(packageName, fset, []*ast.File{f}, nil)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	return pkg.Scope(), nil
    95  }
    96  
    97  func TestEqualFieldName(t *testing.T) {
    98  	tt := []struct {
    99  		Name     string
   100  		Source   string
   101  		Target   string
   102  		Expected bool
   103  	}{
   104  		{Name: "words with same case", Source: "test", Target: "test", Expected: true},
   105  		{Name: "words different case", Source: "test", Target: "tEsT", Expected: true},
   106  		{Name: "different words", Source: "foo", Target: "bar", Expected: false},
   107  		{Name: "separated with underscore", Source: "the_test", Target: "TheTest", Expected: true},
   108  		{Name: "empty values", Source: "", Target: "", Expected: true},
   109  	}
   110  
   111  	for _, tc := range tt {
   112  		t.Run(tc.Name, func(t *testing.T) {
   113  			result := equalFieldName(tc.Source, tc.Target)
   114  			require.Equal(t, tc.Expected, result)
   115  		})
   116  	}
   117  }
   118  
   119  func TestField_CallArgs(t *testing.T) {
   120  	tt := []struct {
   121  		Name string
   122  		Field
   123  		Expected string
   124  	}{
   125  		{
   126  			Name: "Field with method that has context, and three args (string, interface, named interface)",
   127  			Field: Field{
   128  				MethodHasContext: true,
   129  				Args: []*FieldArgument{
   130  					{
   131  						ArgumentDefinition: &ast2.ArgumentDefinition{
   132  							Name: "test",
   133  						},
   134  						TypeReference: &config.TypeReference{
   135  							GO: (&types.Interface{}).Complete(),
   136  						},
   137  					},
   138  					{
   139  						ArgumentDefinition: &ast2.ArgumentDefinition{
   140  							Name: "test2",
   141  						},
   142  						TypeReference: &config.TypeReference{
   143  							GO: types.NewNamed(
   144  								types.NewTypeName(token.NoPos, nil, "TestInterface", nil),
   145  								(&types.Interface{}).Complete(),
   146  								nil,
   147  							),
   148  						},
   149  					},
   150  					{
   151  						ArgumentDefinition: &ast2.ArgumentDefinition{
   152  							Name: "test3",
   153  						},
   154  						TypeReference: &config.TypeReference{
   155  							GO: types.Typ[types.String],
   156  						},
   157  					},
   158  				},
   159  			},
   160  			Expected: `ctx, ` + `
   161  				func () interface{} {
   162  					if fc.Args["test"] == nil {
   163  						return nil
   164  					}
   165  					return fc.Args["test"].(interface{})
   166  				}(), fc.Args["test2"].(TestInterface), fc.Args["test3"].(string)`,
   167  		},
   168  		{
   169  			Name: "Resolver field that isn't root object with single int argument",
   170  			Field: Field{
   171  				Object: &Object{
   172  					Root: false,
   173  				},
   174  				IsResolver: true,
   175  				Args: []*FieldArgument{
   176  					{
   177  						ArgumentDefinition: &ast2.ArgumentDefinition{
   178  							Name: "test",
   179  						},
   180  						TypeReference: &config.TypeReference{
   181  							GO: types.Typ[types.Int],
   182  						},
   183  					},
   184  				},
   185  			},
   186  			Expected: `rctx, obj, fc.Args["test"].(int)`,
   187  		},
   188  	}
   189  
   190  	for _, tc := range tt {
   191  		t.Run(tc.Name, func(t *testing.T) {
   192  			require.Equal(t, tc.CallArgs(), tc.Expected)
   193  		})
   194  	}
   195  }