github.com/99designs/gqlgen@v0.17.45/plugin/federation/federation_test.go (about) 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 }