github.com/maeglindeveloper/gqlgen@v0.13.1-0.20210413081235-57808b12a0a0/graphql/executor/testexecutor/testexecutor.go (about)

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