func TestTypeSystem_UnionTypesMustBeResolvable_AcceptsAUnionOfObjectTypesDefiningIsTypeOf(t *testing.T) { _, err := schemaWithFieldType(graphql.NewUnion(graphql.UnionConfig{ Name: "SomeUnion", Types: []*graphql.Object{objectWithIsTypeOf}, })) if err != nil { t.Fatalf("unexpected error: %v", err) } }
func TestTypeSystem_DefinitionExample_ProhibitsNilTypeInUnions(t *testing.T) { ttype := graphql.NewUnion(graphql.UnionConfig{ Name: "BadUnion", Types: []*graphql.Object{nil}, }) expected := `BadUnion may only contain Object types, it cannot contain: <nil>.` if ttype.Error().Error() != expected { t.Fatalf(`expected %v , got: %v`, expected, ttype.Error()) } }
func TestTypeSystem_UnionTypesMustBeArray_RejectsAUnionTypeWithoutTypes(t *testing.T) { _, err := schemaWithFieldType(graphql.NewUnion(graphql.UnionConfig{ Name: "SomeUnion", ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { return nil }, })) expectedError := "Must provide Array of types for Union SomeUnion." if err == nil || err.Error() != expectedError { t.Fatalf("Expected error: %v, got %v", expectedError, err) } }
func TestTypeSystem_UnionTypesMustBeResolvable_RejectsAUnionTypeNotDefiningResolveTypeOfObjectTypesNotDefiningIsTypeOf(t *testing.T) { _, err := schemaWithFieldType(graphql.NewUnion(graphql.UnionConfig{ Name: "SomeUnion", Types: []*graphql.Object{someObjectType}, })) expectedError := `Union Type SomeUnion does not provide a "resolveType" function and ` + `possible Type SomeObject does not provide a "isTypeOf" function. ` + `There is no way to resolve this possible type during execution.` if err == nil || err.Error() != expectedError { t.Fatalf("Expected error: %v, got %v", expectedError, err) } }
func TestTypeSystem_UnionTypesMustBeResolvable_AcceptsAUnionTypeDefiningResolveType(t *testing.T) { _, err := schemaWithFieldType(graphql.NewUnion(graphql.UnionConfig{ Name: "SomeUnion", Types: []*graphql.Object{someObjectType}, ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { return nil }, })) if err != nil { t.Fatalf("unexpected error: %v", err) } }
func schemaWithUnionOfType(ttype *graphql.Object) (graphql.Schema, error) { badObjectType := graphql.NewUnion(graphql.UnionConfig{ Name: "BadUnion", ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { return nil }, Types: []*graphql.Object{ttype}, }) return graphql.NewSchema(graphql.SchemaConfig{ Query: graphql.NewObject(graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ "f": &graphql.Field{ Type: badObjectType, }, }, }), }) }
func init() { var beingInterface = graphql.NewInterface(graphql.InterfaceConfig{ Name: "Being", Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "surname": &graphql.ArgumentConfig{ Type: graphql.Boolean, }, }, }, }, }) var petInterface = graphql.NewInterface(graphql.InterfaceConfig{ Name: "Pet", Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "surname": &graphql.ArgumentConfig{ Type: graphql.Boolean, }, }, }, }, }) var canineInterface = graphql.NewInterface(graphql.InterfaceConfig{ Name: "Canine", Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "surname": &graphql.ArgumentConfig{ Type: graphql.Boolean, }, }, }, }, }) var dogCommandEnum = graphql.NewEnum(graphql.EnumConfig{ Name: "DogCommand", Values: graphql.EnumValueConfigMap{ "SIT": &graphql.EnumValueConfig{ Value: 0, }, "HEEL": &graphql.EnumValueConfig{ Value: 1, }, "DOWN": &graphql.EnumValueConfig{ Value: 2, }, }, }) var dogType = graphql.NewObject(graphql.ObjectConfig{ Name: "Dog", IsTypeOf: func(p graphql.IsTypeOfParams) bool { return true }, Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "surname": &graphql.ArgumentConfig{ Type: graphql.Boolean, }, }, }, "nickname": &graphql.Field{ Type: graphql.String, }, "barkVolume": &graphql.Field{ Type: graphql.Int, }, "barks": &graphql.Field{ Type: graphql.Boolean, }, "doesKnowCommand": &graphql.Field{ Type: graphql.Boolean, Args: graphql.FieldConfigArgument{ "dogCommand": &graphql.ArgumentConfig{ Type: dogCommandEnum, }, }, }, "isHousetrained": &graphql.Field{ Type: graphql.Boolean, Args: graphql.FieldConfigArgument{ "atOtherHomes": &graphql.ArgumentConfig{ Type: graphql.Boolean, DefaultValue: true, }, }, }, "isAtLocation": &graphql.Field{ Type: graphql.Boolean, Args: graphql.FieldConfigArgument{ "x": &graphql.ArgumentConfig{ Type: graphql.Int, }, "y": &graphql.ArgumentConfig{ Type: graphql.Int, }, }, }, }, Interfaces: []*graphql.Interface{ beingInterface, petInterface, canineInterface, }, }) var furColorEnum = graphql.NewEnum(graphql.EnumConfig{ Name: "FurColor", Values: graphql.EnumValueConfigMap{ "BROWN": &graphql.EnumValueConfig{ Value: 0, }, "BLACK": &graphql.EnumValueConfig{ Value: 1, }, "TAN": &graphql.EnumValueConfig{ Value: 2, }, "SPOTTED": &graphql.EnumValueConfig{ Value: 3, }, }, }) var catType = graphql.NewObject(graphql.ObjectConfig{ Name: "Cat", IsTypeOf: func(p graphql.IsTypeOfParams) bool { return true }, Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "surname": &graphql.ArgumentConfig{ Type: graphql.Boolean, }, }, }, "nickname": &graphql.Field{ Type: graphql.String, }, "meowVolume": &graphql.Field{ Type: graphql.Int, }, "meows": &graphql.Field{ Type: graphql.Boolean, }, "furColor": &graphql.Field{ Type: furColorEnum, }, }, Interfaces: []*graphql.Interface{ beingInterface, petInterface, }, }) var catOrDogUnion = graphql.NewUnion(graphql.UnionConfig{ Name: "CatOrDog", Types: []*graphql.Object{ dogType, catType, }, ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { // not used for validation return nil }, }) var intelligentInterface = graphql.NewInterface(graphql.InterfaceConfig{ Name: "Intelligent", Fields: graphql.Fields{ "iq": &graphql.Field{ Type: graphql.Int, }, }, }) var humanType = graphql.NewObject(graphql.ObjectConfig{ Name: "Human", IsTypeOf: func(p graphql.IsTypeOfParams) bool { return true }, Interfaces: []*graphql.Interface{ beingInterface, intelligentInterface, }, Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "surname": &graphql.ArgumentConfig{ Type: graphql.Boolean, }, }, }, "pets": &graphql.Field{ Type: graphql.NewList(petInterface), }, "iq": &graphql.Field{ Type: graphql.Int, }, }, }) humanType.AddFieldConfig("relatives", &graphql.Field{ Type: graphql.NewList(humanType), }) var alienType = graphql.NewObject(graphql.ObjectConfig{ Name: "Alien", IsTypeOf: func(p graphql.IsTypeOfParams) bool { return true }, Interfaces: []*graphql.Interface{ beingInterface, intelligentInterface, }, Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "surname": &graphql.ArgumentConfig{ Type: graphql.Boolean, }, }, }, "iq": &graphql.Field{ Type: graphql.Int, }, "numEyes": &graphql.Field{ Type: graphql.Int, }, }, }) var dogOrHumanUnion = graphql.NewUnion(graphql.UnionConfig{ Name: "DogOrHuman", Types: []*graphql.Object{ dogType, humanType, }, ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { // not used for validation return nil }, }) var humanOrAlienUnion = graphql.NewUnion(graphql.UnionConfig{ Name: "HumanOrAlien", Types: []*graphql.Object{ alienType, humanType, }, ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { // not used for validation return nil }, }) var complexInputObject = graphql.NewInputObject(graphql.InputObjectConfig{ Name: "ComplexInput", Fields: graphql.InputObjectConfigFieldMap{ "requiredField": &graphql.InputObjectFieldConfig{ Type: graphql.NewNonNull(graphql.Boolean), }, "intField": &graphql.InputObjectFieldConfig{ Type: graphql.Int, }, "stringField": &graphql.InputObjectFieldConfig{ Type: graphql.String, }, "booleanField": &graphql.InputObjectFieldConfig{ Type: graphql.Boolean, }, "stringListField": &graphql.InputObjectFieldConfig{ Type: graphql.NewList(graphql.String), }, }, }) var complicatedArgs = graphql.NewObject(graphql.ObjectConfig{ Name: "ComplicatedArgs", // TODO List // TODO Coercion // TODO NotNulls Fields: graphql.Fields{ "intArgField": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "intArg": &graphql.ArgumentConfig{ Type: graphql.Int, }, }, }, "nonNullIntArgField": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "nonNullIntArg": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, }, }, "stringArgField": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "stringArg": &graphql.ArgumentConfig{ Type: graphql.String, }, }, }, "booleanArgField": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "booleanArg": &graphql.ArgumentConfig{ Type: graphql.Boolean, }, }, }, "enumArgField": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "enumArg": &graphql.ArgumentConfig{ Type: furColorEnum, }, }, }, "floatArgField": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "floatArg": &graphql.ArgumentConfig{ Type: graphql.Float, }, }, }, "idArgField": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "idArg": &graphql.ArgumentConfig{ Type: graphql.ID, }, }, }, "stringListArgField": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "stringListArg": &graphql.ArgumentConfig{ Type: graphql.NewList(graphql.String), }, }, }, "complexArgField": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "complexArg": &graphql.ArgumentConfig{ Type: complexInputObject, }, }, }, "multipleReqs": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "req1": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, "req2": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, }, }, "multipleOpts": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "opt1": &graphql.ArgumentConfig{ Type: graphql.Int, DefaultValue: 0, }, "opt2": &graphql.ArgumentConfig{ Type: graphql.Int, DefaultValue: 0, }, }, }, "multipleOptAndReq": &graphql.Field{ Type: graphql.String, Args: graphql.FieldConfigArgument{ "req1": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, "req2": &graphql.ArgumentConfig{ Type: graphql.NewNonNull(graphql.Int), }, "opt1": &graphql.ArgumentConfig{ Type: graphql.Int, DefaultValue: 0, }, "opt2": &graphql.ArgumentConfig{ Type: graphql.Int, DefaultValue: 0, }, }, }, }, }) queryRoot := graphql.NewObject(graphql.ObjectConfig{ Name: "QueryRoot", Fields: graphql.Fields{ "human": &graphql.Field{ Args: graphql.FieldConfigArgument{ "id": &graphql.ArgumentConfig{ Type: graphql.ID, }, }, Type: humanType, }, "alien": &graphql.Field{ Type: alienType, }, "dog": &graphql.Field{ Type: dogType, }, "cat": &graphql.Field{ Type: catType, }, "pet": &graphql.Field{ Type: petInterface, }, "catOrDog": &graphql.Field{ Type: catOrDogUnion, }, "dogOrHuman": &graphql.Field{ Type: dogOrHumanUnion, }, "humanOrAlien": &graphql.Field{ Type: humanOrAlienUnion, }, "complicatedArgs": &graphql.Field{ Type: complicatedArgs, }, }, }) schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: queryRoot, Directives: []*graphql.Directive{ graphql.NewDirective(graphql.DirectiveConfig{ Name: "operationOnly", Locations: []string{graphql.DirectiveLocationQuery}, }), graphql.IncludeDirective, graphql.SkipDirective, }, Types: []graphql.Type{ catType, dogType, humanType, alienType, }, }) if err != nil { panic(err) } TestSchema = &schema }
var objectWithIsTypeOf = graphql.NewObject(graphql.ObjectConfig{ Name: "ObjectWithIsTypeOf", IsTypeOf: func(p graphql.IsTypeOfParams) bool { return true }, Fields: graphql.Fields{ "f": &graphql.Field{ Type: graphql.String, }, }, }) var someUnionType = graphql.NewUnion(graphql.UnionConfig{ Name: "SomeUnion", ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { return nil }, Types: []*graphql.Object{ someObjectType, }, }) var someInterfaceType = graphql.NewInterface(graphql.InterfaceConfig{ Name: "SomeInterface", ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { return nil }, Fields: graphql.Fields{ "f": &graphql.Field{ Type: graphql.String, }, }, })
Type: graphql.Boolean, }, }, IsTypeOf: func(p graphql.IsTypeOfParams) bool { _, ok := p.Value.(*testCat2) return ok }, }) var petType = graphql.NewUnion(graphql.UnionConfig{ Name: "Pet", Types: []*graphql.Object{ dogType, catType, }, ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { if _, ok := p.Value.(*testCat2); ok { return catType } if _, ok := p.Value.(*testDog2); ok { return dogType } return nil }, }) var personType = graphql.NewObject(graphql.ObjectConfig{ Name: "Person", Interfaces: []*graphql.Interface{ namedType, }, Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String,
}, }, }) var objectType = graphql.NewObject(graphql.ObjectConfig{ Name: "Object", IsTypeOf: func(p graphql.IsTypeOfParams) bool { return true }, }) var interfaceType = graphql.NewInterface(graphql.InterfaceConfig{ Name: "Interface", }) var unionType = graphql.NewUnion(graphql.UnionConfig{ Name: "Union", Types: []*graphql.Object{ objectType, }, }) var enumType = graphql.NewEnum(graphql.EnumConfig{ Name: "Enum", Values: graphql.EnumValueConfigMap{ "foo": &graphql.EnumValueConfig{}, }, }) var inputObjectType = graphql.NewInputObject(graphql.InputObjectConfig{ Name: "InputObject", }) func init() { blogAuthor.AddFieldConfig("recentArticle", &graphql.Field{ Type: blogArticle,
func TestResolveTypeOnUnionYieldsUsefulError(t *testing.T) { humanType := graphql.NewObject(graphql.ObjectConfig{ Name: "Human", Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, }, }, }) dogType := graphql.NewObject(graphql.ObjectConfig{ Name: "Dog", Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, }, "woofs": &graphql.Field{ Type: graphql.Boolean, }, }, }) catType := graphql.NewObject(graphql.ObjectConfig{ Name: "Cat", Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, }, "meows": &graphql.Field{ Type: graphql.Boolean, }, }, }) petType := graphql.NewUnion(graphql.UnionConfig{ Name: "Pet", Types: []*graphql.Object{ dogType, catType, }, ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { if _, ok := p.Value.(*testCat); ok { return catType } if _, ok := p.Value.(*testDog); ok { return dogType } if _, ok := p.Value.(*testHuman); ok { return humanType } return nil }, }) schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: graphql.NewObject(graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ "pets": &graphql.Field{ Type: graphql.NewList(petType), Resolve: func(p graphql.ResolveParams) (interface{}, error) { return []interface{}{ &testDog{"Odie", true}, &testCat{"Garfield", false}, &testHuman{"Jon"}, }, nil }, }, }, }), }) if err != nil { t.Fatalf("Error in schema %v", err.Error()) } query := `{ pets { ... on Dog { name woofs } ... on Cat { name meows } } }` expected := &graphql.Result{ Data: map[string]interface{}{ "pets": []interface{}{ map[string]interface{}{ "name": "Odie", "woofs": bool(true), }, map[string]interface{}{ "name": "Garfield", "meows": bool(false), }, nil, }, }, Errors: []gqlerrors.FormattedError{ { Message: `Runtime Object type "Human" is not a possible type for "Pet".`, Locations: []location.SourceLocation{}, }, }, } result := graphql.Do(graphql.Params{ Schema: schema, RequestString: query, }) if len(result.Errors) == 0 { t.Fatalf("wrong result, expected errors: %v, got: %v", len(expected.Errors), len(result.Errors)) } if !reflect.DeepEqual(expected, result) { t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expected, result)) } }
func TestIsTypeOfUsedToResolveRuntimeTypeForUnion(t *testing.T) { dogType := graphql.NewObject(graphql.ObjectConfig{ Name: "Dog", IsTypeOf: func(p graphql.IsTypeOfParams) bool { _, ok := p.Value.(*testDog) return ok }, Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, }, "woofs": &graphql.Field{ Type: graphql.Boolean, }, }, }) catType := graphql.NewObject(graphql.ObjectConfig{ Name: "Cat", IsTypeOf: func(p graphql.IsTypeOfParams) bool { _, ok := p.Value.(*testCat) return ok }, Fields: graphql.Fields{ "name": &graphql.Field{ Type: graphql.String, }, "meows": &graphql.Field{ Type: graphql.Boolean, }, }, }) // ie declare Pet has Dot and Cat object types petType := graphql.NewUnion(graphql.UnionConfig{ Name: "Pet", Types: []*graphql.Object{ dogType, catType, }, }) schema, err := graphql.NewSchema(graphql.SchemaConfig{ Query: graphql.NewObject(graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ "pets": &graphql.Field{ Type: graphql.NewList(petType), Resolve: func(p graphql.ResolveParams) (interface{}, error) { return []interface{}{ &testDog{"Odie", true}, &testCat{"Garfield", false}, }, nil }, }, }, }), }) if err != nil { t.Fatalf("Error in schema %v", err.Error()) } query := `{ pets { ... on Dog { name woofs } ... on Cat { name meows } } }` expected := &graphql.Result{ Data: map[string]interface{}{ "pets": []interface{}{ map[string]interface{}{ "name": "Odie", "woofs": bool(true), }, map[string]interface{}{ "name": "Garfield", "meows": bool(false), }, }, }, Errors: nil, } result := graphql.Do(graphql.Params{ Schema: schema, RequestString: query, }) if len(result.Errors) != 0 { t.Fatalf("wrong result, unexpected errors: %v", result.Errors) } if !reflect.DeepEqual(expected, result) { t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expected, result)) } }