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

     1  package executor_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/99designs/gqlgen/graphql"
     8  	"github.com/99designs/gqlgen/graphql/executor/testexecutor"
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  	"github.com/vektah/gqlparser/v2/ast"
    12  	"github.com/vektah/gqlparser/v2/gqlerror"
    13  	"github.com/vektah/gqlparser/v2/parser"
    14  )
    15  
    16  func TestExecutor(t *testing.T) {
    17  	exec := testexecutor.New()
    18  
    19  	t.Run("calls query on executable schema", func(t *testing.T) {
    20  		resp := query(exec, "", "{name}")
    21  		assert.Equal(t, `{"name":"test"}`, string(resp.Data))
    22  	})
    23  
    24  	t.Run("invokes operation middleware in order", func(t *testing.T) {
    25  		var calls []string
    26  		exec.AroundOperations(func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
    27  			calls = append(calls, "first")
    28  			return next(ctx)
    29  		})
    30  		exec.AroundOperations(func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
    31  			calls = append(calls, "second")
    32  			return next(ctx)
    33  		})
    34  
    35  		resp := query(exec, "", "{name}")
    36  		assert.Equal(t, `{"name":"test"}`, string(resp.Data))
    37  		assert.Equal(t, []string{"first", "second"}, calls)
    38  	})
    39  
    40  	t.Run("invokes response middleware in order", func(t *testing.T) {
    41  		var calls []string
    42  		exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
    43  			calls = append(calls, "first")
    44  			return next(ctx)
    45  		})
    46  		exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
    47  			calls = append(calls, "second")
    48  			return next(ctx)
    49  		})
    50  
    51  		resp := query(exec, "", "{name}")
    52  		assert.Equal(t, `{"name":"test"}`, string(resp.Data))
    53  		assert.Equal(t, []string{"first", "second"}, calls)
    54  	})
    55  
    56  	t.Run("invokes field middleware in order", func(t *testing.T) {
    57  		var calls []string
    58  		exec.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
    59  			calls = append(calls, "first")
    60  			return next(ctx)
    61  		})
    62  		exec.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
    63  			calls = append(calls, "second")
    64  			return next(ctx)
    65  		})
    66  
    67  		resp := query(exec, "", "{name}")
    68  		assert.Equal(t, `{"name":"test"}`, string(resp.Data))
    69  		assert.Equal(t, []string{"first", "second"}, calls)
    70  	})
    71  
    72  	t.Run("invokes operation mutators", func(t *testing.T) {
    73  		var calls []string
    74  		exec.Use(&testParamMutator{
    75  			Mutate: func(ctx context.Context, req *graphql.RawParams) *gqlerror.Error {
    76  				calls = append(calls, "param")
    77  				return nil
    78  			},
    79  		})
    80  		exec.Use(&testCtxMutator{
    81  			Mutate: func(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error {
    82  				calls = append(calls, "context")
    83  				return nil
    84  			},
    85  		})
    86  		resp := query(exec, "", "{name}")
    87  		assert.Equal(t, `{"name":"test"}`, string(resp.Data))
    88  		assert.Equal(t, []string{"param", "context"}, calls)
    89  	})
    90  
    91  	t.Run("get query parse error in AroundResponses", func(t *testing.T) {
    92  		var errors1 gqlerror.List
    93  		var errors2 gqlerror.List
    94  		exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
    95  			resp := next(ctx)
    96  			errors1 = graphql.GetErrors(ctx)
    97  			errors2 = resp.Errors
    98  			return resp
    99  		})
   100  
   101  		resp := query(exec, "", "invalid")
   102  		assert.Equal(t, "", string(resp.Data))
   103  		assert.Equal(t, 1, len(resp.Errors))
   104  		assert.Equal(t, 1, len(errors1))
   105  		assert.Equal(t, 1, len(errors2))
   106  	})
   107  
   108  	t.Run("query caching", func(t *testing.T) {
   109  		ctx := context.Background()
   110  		cache := &graphql.MapCache{}
   111  		exec.SetQueryCache(cache)
   112  		qry := `query Foo {name}`
   113  
   114  		t.Run("cache miss populates cache", func(t *testing.T) {
   115  			resp := query(exec, "Foo", qry)
   116  			assert.Equal(t, `{"name":"test"}`, string(resp.Data))
   117  
   118  			cacheDoc, ok := cache.Get(ctx, qry)
   119  			require.True(t, ok)
   120  			require.Equal(t, "Foo", cacheDoc.(*ast.QueryDocument).Operations[0].Name)
   121  		})
   122  
   123  		t.Run("cache hits use document from cache", func(t *testing.T) {
   124  			doc, err := parser.ParseQuery(&ast.Source{Input: `query Bar {name}`})
   125  			require.Nil(t, err)
   126  			cache.Add(ctx, qry, doc)
   127  
   128  			resp := query(exec, "Bar", qry)
   129  			assert.Equal(t, `{"name":"test"}`, string(resp.Data))
   130  
   131  			cacheDoc, ok := cache.Get(ctx, qry)
   132  			require.True(t, ok)
   133  			require.Equal(t, "Bar", cacheDoc.(*ast.QueryDocument).Operations[0].Name)
   134  		})
   135  	})
   136  }
   137  
   138  type testParamMutator struct {
   139  	Mutate func(context.Context, *graphql.RawParams) *gqlerror.Error
   140  }
   141  
   142  func (m *testParamMutator) ExtensionName() string {
   143  	return "Operation: Mutate Parameters"
   144  }
   145  
   146  func (m *testParamMutator) Validate(s graphql.ExecutableSchema) error {
   147  	return nil
   148  }
   149  
   150  func (m *testParamMutator) MutateOperationParameters(ctx context.Context, r *graphql.RawParams) *gqlerror.Error {
   151  	return m.Mutate(ctx, r)
   152  }
   153  
   154  type testCtxMutator struct {
   155  	Mutate func(context.Context, *graphql.OperationContext) *gqlerror.Error
   156  }
   157  
   158  func (m *testCtxMutator) ExtensionName() string {
   159  	return "Operation: Mutate the Context"
   160  }
   161  
   162  func (m *testCtxMutator) Validate(s graphql.ExecutableSchema) error {
   163  	return nil
   164  }
   165  
   166  func (m *testCtxMutator) MutateOperationContext(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error {
   167  	return m.Mutate(ctx, rc)
   168  }
   169  
   170  func TestErrorServer(t *testing.T) {
   171  	exec := testexecutor.NewError()
   172  
   173  	t.Run("get resolver error in AroundResponses", func(t *testing.T) {
   174  		var errors1 gqlerror.List
   175  		var errors2 gqlerror.List
   176  		exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
   177  			resp := next(ctx)
   178  			errors1 = graphql.GetErrors(ctx)
   179  			errors2 = resp.Errors
   180  			return resp
   181  		})
   182  
   183  		resp := query(exec, "", "{name}")
   184  		assert.Equal(t, "null", string(resp.Data))
   185  		assert.Equal(t, 1, len(errors1))
   186  		assert.Equal(t, 1, len(errors2))
   187  	})
   188  }
   189  
   190  func query(exec *testexecutor.TestExecutor, op, q string) *graphql.Response {
   191  	ctx := graphql.StartOperationTrace(context.Background())
   192  	now := graphql.Now()
   193  	rc, err := exec.CreateOperationContext(ctx, &graphql.RawParams{
   194  		Query:         q,
   195  		OperationName: op,
   196  		ReadTime: graphql.TraceTiming{
   197  			Start: now,
   198  			End:   now,
   199  		},
   200  	})
   201  
   202  	if err != nil {
   203  		return exec.DispatchError(ctx, err)
   204  	}
   205  
   206  	resp, ctx2 := exec.DispatchOperation(ctx, rc)
   207  	return resp(ctx2)
   208  }