...

Source file src/github.com/99designs/gqlgen/plugin/federation/federation_test.go

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

     1  package federation
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/99designs/gqlgen/codegen"
    10  	"github.com/99designs/gqlgen/codegen/config"
    11  	"github.com/99designs/gqlgen/plugin/federation/fieldset"
    12  )
    13  
    14  func TestWithEntities(t *testing.T) {
    15  	f, cfg := load(t, "testdata/allthethings/gqlgen.yml")
    16  
    17  	require.Equal(t, []string{"ExternalExtension", "Hello", "MoreNesting", "MultiHelloMultiKey", "NestedKey", "VeryNestedKey", "World"}, cfg.Schema.Types["_Entity"].Types)
    18  
    19  	require.Len(t, cfg.Schema.Types["Entity"].Fields, 8)
    20  
    21  	require.Equal(t, "findExternalExtensionByUpc", cfg.Schema.Types["Entity"].Fields[0].Name)
    22  	require.Equal(t, "findHelloByName", cfg.Schema.Types["Entity"].Fields[1].Name)
    23  	// missing on purpose: all @external fields:
    24  	// require.Equal(t, "findMoreNestingByID", cfg.Schema.Types["Entity"].Fields[2].Name)
    25  	require.Equal(t, "findManyMultiHelloMultiKeyByNames", cfg.Schema.Types["Entity"].Fields[2].Name)
    26  	require.Equal(t, "findManyMultiHelloMultiKeyByKey2s", cfg.Schema.Types["Entity"].Fields[3].Name)
    27  	require.Equal(t, "findNestedKeyByIDAndHelloName", cfg.Schema.Types["Entity"].Fields[4].Name)
    28  	require.Equal(t, "findVeryNestedKeyByIDAndHelloNameAndWorldFooAndWorldBarAndMoreWorldFoo", cfg.Schema.Types["Entity"].Fields[5].Name)
    29  	require.Equal(t, "findWorldByFoo", cfg.Schema.Types["Entity"].Fields[6].Name)
    30  	require.Equal(t, "findWorldByBar", cfg.Schema.Types["Entity"].Fields[7].Name)
    31  
    32  	require.NoError(t, f.MutateConfig(cfg))
    33  
    34  	require.Len(t, f.Entities, 7)
    35  
    36  	require.Equal(t, "ExternalExtension", f.Entities[0].Name)
    37  	require.Len(t, f.Entities[0].Resolvers, 1)
    38  	require.Len(t, f.Entities[0].Resolvers[0].KeyFields, 1)
    39  	require.Equal(t, "upc", f.Entities[0].Resolvers[0].KeyFields[0].Definition.Name)
    40  	require.Equal(t, "String", f.Entities[0].Resolvers[0].KeyFields[0].Definition.Type.Name())
    41  
    42  	require.Equal(t, "Hello", f.Entities[1].Name)
    43  	require.Len(t, f.Entities[1].Resolvers, 1)
    44  	require.Len(t, f.Entities[1].Resolvers[0].KeyFields, 1)
    45  	require.Equal(t, "name", f.Entities[1].Resolvers[0].KeyFields[0].Definition.Name)
    46  	require.Equal(t, "String", f.Entities[1].Resolvers[0].KeyFields[0].Definition.Type.Name())
    47  
    48  	require.Equal(t, "MoreNesting", f.Entities[2].Name)
    49  	require.Len(t, f.Entities[2].Resolvers, 0)
    50  
    51  	require.Equal(t, "MultiHelloMultiKey", f.Entities[3].Name)
    52  	require.Len(t, f.Entities[3].Resolvers, 2)
    53  	require.Len(t, f.Entities[3].Resolvers[0].KeyFields, 1)
    54  	require.Len(t, f.Entities[3].Resolvers[1].KeyFields, 1)
    55  	require.Equal(t, "name", f.Entities[3].Resolvers[0].KeyFields[0].Definition.Name)
    56  	require.Equal(t, "String", f.Entities[3].Resolvers[0].KeyFields[0].Definition.Type.Name())
    57  	require.Equal(t, "key2", f.Entities[3].Resolvers[1].KeyFields[0].Definition.Name)
    58  	require.Equal(t, "String", f.Entities[3].Resolvers[1].KeyFields[0].Definition.Type.Name())
    59  
    60  	require.Equal(t, "NestedKey", f.Entities[4].Name)
    61  	require.Len(t, f.Entities[4].Resolvers, 1)
    62  	require.Len(t, f.Entities[4].Resolvers[0].KeyFields, 2)
    63  	require.Equal(t, "id", f.Entities[4].Resolvers[0].KeyFields[0].Definition.Name)
    64  	require.Equal(t, "String", f.Entities[4].Resolvers[0].KeyFields[0].Definition.Type.Name())
    65  	require.Equal(t, "helloName", f.Entities[4].Resolvers[0].KeyFields[1].Definition.Name)
    66  	require.Equal(t, "String", f.Entities[4].Resolvers[0].KeyFields[1].Definition.Type.Name())
    67  
    68  	require.Equal(t, "VeryNestedKey", f.Entities[5].Name)
    69  	require.Len(t, f.Entities[5].Resolvers, 1)
    70  	require.Len(t, f.Entities[5].Resolvers[0].KeyFields, 5)
    71  	require.Equal(t, "id", f.Entities[5].Resolvers[0].KeyFields[0].Definition.Name)
    72  	require.Equal(t, "String", f.Entities[5].Resolvers[0].KeyFields[0].Definition.Type.Name())
    73  	require.Equal(t, "helloName", f.Entities[5].Resolvers[0].KeyFields[1].Definition.Name)
    74  	require.Equal(t, "String", f.Entities[5].Resolvers[0].KeyFields[1].Definition.Type.Name())
    75  	require.Equal(t, "worldFoo", f.Entities[5].Resolvers[0].KeyFields[2].Definition.Name)
    76  	require.Equal(t, "String", f.Entities[5].Resolvers[0].KeyFields[2].Definition.Type.Name())
    77  	require.Equal(t, "worldBar", f.Entities[5].Resolvers[0].KeyFields[3].Definition.Name)
    78  	require.Equal(t, "Int", f.Entities[5].Resolvers[0].KeyFields[3].Definition.Type.Name())
    79  	require.Equal(t, "moreWorldFoo", f.Entities[5].Resolvers[0].KeyFields[4].Definition.Name)
    80  	require.Equal(t, "String", f.Entities[5].Resolvers[0].KeyFields[4].Definition.Type.Name())
    81  
    82  	require.Len(t, f.Entities[5].Requires, 2)
    83  	require.Equal(t, f.Entities[5].Requires[0].Name, "id")
    84  	require.Equal(t, f.Entities[5].Requires[1].Name, "helloSecondary")
    85  
    86  	require.Equal(t, "World", f.Entities[6].Name)
    87  	require.Len(t, f.Entities[6].Resolvers, 2)
    88  	require.Len(t, f.Entities[6].Resolvers[0].KeyFields, 1)
    89  	require.Equal(t, "foo", f.Entities[6].Resolvers[0].KeyFields[0].Definition.Name)
    90  	require.Equal(t, "String", f.Entities[6].Resolvers[0].KeyFields[0].Definition.Type.Name())
    91  	require.Len(t, f.Entities[6].Resolvers[1].KeyFields, 1)
    92  	require.Equal(t, "bar", f.Entities[6].Resolvers[1].KeyFields[0].Definition.Name)
    93  	require.Equal(t, "Int", f.Entities[6].Resolvers[1].KeyFields[0].Definition.Type.Name())
    94  }
    95  
    96  func TestNoEntities(t *testing.T) {
    97  	f, cfg := load(t, "testdata/entities/nokey.yml")
    98  
    99  	err := f.MutateConfig(cfg)
   100  	require.NoError(t, err)
   101  	require.Len(t, f.Entities, 0)
   102  }
   103  
   104  func TestUnusedInterfaceKeyDirective(t *testing.T) {
   105  	f, cfg := load(t, "testdata/interfaces/unused_key.yml")
   106  
   107  	err := f.MutateConfig(cfg)
   108  	require.NoError(t, err)
   109  	require.Len(t, f.Entities, 0)
   110  }
   111  
   112  func TestInterfaceKeyDirective(t *testing.T) {
   113  	f, cfg := load(t, "testdata/interfaces/key.yml")
   114  
   115  	err := f.MutateConfig(cfg)
   116  	require.NoError(t, err)
   117  	require.Len(t, cfg.Schema.Types["_Entity"].Types, 1)
   118  	require.Len(t, f.Entities, 2)
   119  }
   120  
   121  func TestInterfaceExtendsDirective(t *testing.T) {
   122  	require.Panics(t, func() {
   123  		load(t, "testdata/interfaces/extends.yml")
   124  	})
   125  }
   126  
   127  func TestEntityInterfaces(t *testing.T) {
   128  	f, cfg := load(t, "testdata/entityinterfaces/interface.yml")
   129  	err := f.MutateConfig(cfg)
   130  
   131  	require.NoError(t, err)
   132  	require.Len(t, f.Entities[0].Resolvers, 1)
   133  	require.Equal(t, "Hello", f.Entities[0].Name)
   134  	require.NotEmpty(t, f.Entities[1].Resolvers)
   135  }
   136  
   137  func TestCodeGeneration(t *testing.T) {
   138  	f, cfg := load(t, "testdata/allthethings/gqlgen.yml")
   139  
   140  	require.Len(t, cfg.Schema.Types["_Entity"].Types, 7)
   141  	require.Len(t, f.Entities, 7)
   142  
   143  	require.NoError(t, f.MutateConfig(cfg))
   144  
   145  	data, err := codegen.BuildData(cfg)
   146  	if err != nil {
   147  		panic(err)
   148  	}
   149  	require.NoError(t, f.GenerateCode(data))
   150  }
   151  
   152  func TestCodeGenerationFederation2(t *testing.T) {
   153  	f, cfg := load(t, "testdata/federation2/federation2.yml")
   154  	err := f.MutateConfig(cfg)
   155  
   156  	require.NoError(t, err)
   157  	require.Equal(t, "ExternalExtension", f.Entities[0].Name)
   158  	require.Len(t, f.Entities[0].Resolvers, 1)
   159  	require.Equal(t, "Hello", f.Entities[1].Name)
   160  	require.Empty(t, f.Entities[1].Resolvers)
   161  	require.Equal(t, "World", f.Entities[2].Name)
   162  	require.Empty(t, f.Entities[2].Resolvers)
   163  
   164  	data, err := codegen.BuildData(cfg)
   165  	if err != nil {
   166  		panic(err)
   167  	}
   168  	require.NoError(t, f.GenerateCode(data))
   169  }
   170  
   171  // This test is to ensure that the input arguments are not
   172  // changed when cfg.OmitSliceElementPointers is false OR true
   173  func TestMultiWithOmitSliceElemPointersCfg(t *testing.T) {
   174  
   175  	staticRepsString := "reps: [HelloByNamesInput]!"
   176  	t.Run("OmitSliceElementPointers true", func(t *testing.T) {
   177  		f, cfg := load(t, "testdata/multi/multi.yml")
   178  		cfg.OmitSliceElementPointers = true
   179  		err := f.MutateConfig(cfg)
   180  		require.NoError(t, err)
   181  		require.Len(t, cfg.Schema.Types["_Entity"].Types, 1)
   182  		require.Len(t, f.Entities, 1)
   183  
   184  		entityGraphqlGenerated := false
   185  		for _, source := range cfg.Sources {
   186  			if source.Name != "federation/entity.graphql" {
   187  				continue
   188  			}
   189  			entityGraphqlGenerated = true
   190  			require.Contains(t, source.Input, staticRepsString)
   191  		}
   192  		require.True(t, entityGraphqlGenerated)
   193  	})
   194  
   195  	t.Run("OmitSliceElementPointers false", func(t *testing.T) {
   196  		f, cfg := load(t, "testdata/multi/multi.yml")
   197  		cfg.OmitSliceElementPointers = false
   198  		err := f.MutateConfig(cfg)
   199  		require.NoError(t, err)
   200  		require.Len(t, cfg.Schema.Types["_Entity"].Types, 1)
   201  		require.Len(t, f.Entities, 1)
   202  
   203  		entityGraphqlGenerated := false
   204  		for _, source := range cfg.Sources {
   205  			if source.Name != "federation/entity.graphql" {
   206  				continue
   207  			}
   208  			entityGraphqlGenerated = true
   209  			require.Contains(t, source.Input, staticRepsString)
   210  		}
   211  		require.True(t, entityGraphqlGenerated)
   212  	})
   213  }
   214  
   215  func TestHandlesRequiresArgumentCorrectlyIfNoSpace(t *testing.T) {
   216  	requiresFieldSet := fieldset.New("foo bar baz(limit:4)", nil)
   217  	assert.Equal(t, 3, len(requiresFieldSet))
   218  	assert.Equal(t, "Foo", requiresFieldSet[0].ToGo())
   219  	assert.Equal(t, "Bar", requiresFieldSet[1].ToGo())
   220  	assert.Equal(t, "Baz(limit:4)", requiresFieldSet[2].ToGo())
   221  }
   222  
   223  func TestHandlesArgumentGeneration(t *testing.T) {
   224  	e := &Entity{
   225  		Name:      "",
   226  		Def:       nil,
   227  		Resolvers: nil,
   228  		Requires:  nil,
   229  	}
   230  
   231  	raw := "foo bar baz(limit:4)"
   232  	requiresFieldSet := fieldset.New(raw, nil)
   233  	for _, field := range requiresFieldSet {
   234  
   235  		e.Requires = append(e.Requires, &Requires{
   236  			Name:  field.ToGoPrivate(),
   237  			Field: field,
   238  		})
   239  	}
   240  }
   241  
   242  func TestInjectSourceLate(t *testing.T) {
   243  	_, cfg := load(t, "testdata/allthethings/gqlgen.yml")
   244  	entityGraphqlGenerated := false
   245  	for _, source := range cfg.Sources {
   246  		if source.Name != "federation/entity.graphql" {
   247  			continue
   248  		}
   249  		entityGraphqlGenerated = true
   250  		require.Contains(t, source.Input, "union _Entity")
   251  		require.Contains(t, source.Input, "type _Service {")
   252  		require.Contains(t, source.Input, "extend type Query {")
   253  		require.Contains(t, source.Input, "_entities(representations: [_Any!]!): [_Entity]!")
   254  		require.Contains(t, source.Input, "_service: _Service!")
   255  	}
   256  	require.True(t, entityGraphqlGenerated)
   257  
   258  	_, cfg = load(t, "testdata/entities/nokey.yml")
   259  	entityGraphqlGenerated = false
   260  	for _, source := range cfg.Sources {
   261  		if source.Name != "federation/entity.graphql" {
   262  			continue
   263  		}
   264  		entityGraphqlGenerated = true
   265  		require.NotContains(t, source.Input, "union _Entity")
   266  		require.Contains(t, source.Input, "type _Service {")
   267  		require.Contains(t, source.Input, "extend type Query {")
   268  		require.NotContains(t, source.Input, "_entities(representations: [_Any!]!): [_Entity]!")
   269  		require.Contains(t, source.Input, "_service: _Service!")
   270  	}
   271  	require.True(t, entityGraphqlGenerated)
   272  
   273  	_, cfg = load(t, "testdata/schema/customquerytype.yml")
   274  	for _, source := range cfg.Sources {
   275  		if source.Name != "federation/entity.graphql" {
   276  			continue
   277  		}
   278  		require.Contains(t, source.Input, "extend type CustomQuery {")
   279  	}
   280  }
   281  
   282  func load(t *testing.T, name string) (*federation, *config.Config) {
   283  	t.Helper()
   284  
   285  	cfg, err := config.LoadConfig(name)
   286  	require.NoError(t, err)
   287  
   288  	if cfg.Federation.Version == 0 {
   289  		cfg.Federation.Version = 1
   290  	}
   291  
   292  	f := &federation{Version: cfg.Federation.Version}
   293  	cfg.Sources = append(cfg.Sources, f.InjectSourceEarly())
   294  	require.NoError(t, cfg.LoadSchema())
   295  
   296  	if src := f.InjectSourceLate(cfg.Schema); src != nil {
   297  		cfg.Sources = append(cfg.Sources, src)
   298  	}
   299  	require.NoError(t, cfg.LoadSchema())
   300  
   301  	require.NoError(t, cfg.Init())
   302  	return f, cfg
   303  }
   304  

View as plain text