github.com/99designs/gqlgen@v0.17.45/graphql/handler/testserver/testserver.go (about) 1 package testserver 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/vektah/gqlparser/v2" 9 "github.com/vektah/gqlparser/v2/ast" 10 11 "github.com/99designs/gqlgen/graphql" 12 "github.com/99designs/gqlgen/graphql/handler" 13 ) 14 15 // New provides a server for use in tests that isn't relying on generated code. It isnt a perfect reproduction of 16 // a generated server, but it aims to be good enough to test the handler package without relying on codegen. 17 func New() *TestServer { 18 next := make(chan struct{}) 19 completeSubscription := make(chan struct{}) 20 21 schema := gqlparser.MustLoadSchema(&ast.Source{Input: ` 22 type Query { 23 name: String! 24 find(id: Int!): String! 25 } 26 type Mutation { 27 name: String! 28 } 29 type Subscription { 30 name: String! 31 } 32 `}) 33 34 srv := &TestServer{ 35 next: next, 36 completeSubscription: completeSubscription, 37 } 38 39 srv.Server = handler.New(&graphql.ExecutableSchemaMock{ 40 ExecFunc: func(ctx context.Context) graphql.ResponseHandler { 41 rc := graphql.GetOperationContext(ctx) 42 switch rc.Operation.Operation { 43 case ast.Query: 44 ran := false 45 return func(ctx context.Context) *graphql.Response { 46 if ran { 47 return nil 48 } 49 ran = true 50 // Field execution happens inside the generated code, lets simulate some of it. 51 ctx = graphql.WithFieldContext(ctx, &graphql.FieldContext{ 52 Object: "Query", 53 Field: graphql.CollectedField{ 54 Field: &ast.Field{ 55 Name: "name", 56 Alias: "name", 57 Definition: schema.Types["Query"].Fields.ForName("name"), 58 }, 59 }, 60 }) 61 res, err := graphql.GetOperationContext(ctx).ResolverMiddleware(ctx, func(ctx context.Context) (interface{}, error) { 62 return &graphql.Response{Data: []byte(`{"name":"test"}`)}, nil 63 }) 64 if err != nil { 65 panic(err) 66 } 67 return res.(*graphql.Response) 68 } 69 case ast.Mutation: 70 return graphql.OneShot(graphql.ErrorResponse(ctx, "mutations are not supported")) 71 case ast.Subscription: 72 return func(context context.Context) *graphql.Response { 73 select { 74 case <-ctx.Done(): 75 return nil 76 case <-next: 77 return &graphql.Response{ 78 Data: []byte(`{"name":"test"}`), 79 } 80 case <-completeSubscription: 81 return nil 82 } 83 } 84 default: 85 return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation")) 86 } 87 }, 88 SchemaFunc: func() *ast.Schema { 89 return schema 90 }, 91 ComplexityFunc: func(typeName string, fieldName string, childComplexity int, args map[string]interface{}) (i int, b bool) { 92 return srv.complexity, true 93 }, 94 }) 95 return srv 96 } 97 98 // NewError provides a server for use in resolver error tests that isn't relying on generated code. It isnt a perfect reproduction of 99 // a generated server, but it aims to be good enough to test the handler package without relying on codegen. 100 func NewError() *TestServer { 101 next := make(chan struct{}) 102 103 schema := gqlparser.MustLoadSchema(&ast.Source{Input: ` 104 type Query { 105 name: String! 106 } 107 `}) 108 109 srv := &TestServer{ 110 next: next, 111 } 112 113 srv.Server = handler.New(&graphql.ExecutableSchemaMock{ 114 ExecFunc: func(ctx context.Context) graphql.ResponseHandler { 115 rc := graphql.GetOperationContext(ctx) 116 switch rc.Operation.Operation { 117 case ast.Query: 118 ran := false 119 return func(ctx context.Context) *graphql.Response { 120 if ran { 121 return nil 122 } 123 ran = true 124 125 graphql.AddError(ctx, fmt.Errorf("resolver error")) 126 127 return &graphql.Response{ 128 Data: []byte(`null`), 129 } 130 } 131 case ast.Mutation: 132 return graphql.OneShot(graphql.ErrorResponse(ctx, "mutations are not supported")) 133 case ast.Subscription: 134 return graphql.OneShot(graphql.ErrorResponse(ctx, "subscription are not supported")) 135 default: 136 return graphql.OneShot(graphql.ErrorResponse(ctx, "unsupported GraphQL operation")) 137 } 138 }, 139 SchemaFunc: func() *ast.Schema { 140 return schema 141 }, 142 ComplexityFunc: func(typeName string, fieldName string, childComplexity int, args map[string]interface{}) (i int, b bool) { 143 return srv.complexity, true 144 }, 145 }) 146 return srv 147 } 148 149 type TestServer struct { 150 *handler.Server 151 next chan struct{} 152 completeSubscription chan struct{} 153 complexity int 154 } 155 156 func (s *TestServer) SendNextSubscriptionMessage() { 157 select { 158 case s.next <- struct{}{}: 159 case <-time.After(1 * time.Second): 160 fmt.Println("WARNING: no active subscription") 161 } 162 } 163 164 func (s *TestServer) SendCompleteSubscriptionMessage() { 165 select { 166 case s.completeSubscription <- struct{}{}: 167 case <-time.After(1 * time.Second): 168 fmt.Println("WARNING: no active subscription") 169 } 170 } 171 172 func (s *TestServer) SetCalculatedComplexity(complexity int) { 173 s.complexity = complexity 174 }