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