...

Source file src/github.com/99designs/gqlgen/graphql/executor/testexecutor/testexecutor.go

Documentation: github.com/99designs/gqlgen/graphql/executor/testexecutor

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

View as plain text