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