github.com/apipluspower/gqlgen@v0.15.2/graphql/executor/executor_test.go (about)

     1  package executor_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/apipluspower/gqlgen/graphql"
     8  	"github.com/apipluspower/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 root field middleware in order", func(t *testing.T) {
    57  		var calls []string
    58  		exec.AroundRootFields(func(ctx context.Context, next graphql.RootResolver) graphql.Marshaler {
    59  			calls = append(calls, "first")
    60  			return next(ctx)
    61  		})
    62  		exec.AroundRootFields(func(ctx context.Context, next graphql.RootResolver) graphql.Marshaler {
    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 field middleware in order", func(t *testing.T) {
    73  		var calls []string
    74  		exec.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
    75  			calls = append(calls, "first")
    76  			return next(ctx)
    77  		})
    78  		exec.AroundFields(func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
    79  			calls = append(calls, "second")
    80  			return next(ctx)
    81  		})
    82  
    83  		resp := query(exec, "", "{name}")
    84  		assert.Equal(t, `{"name":"test"}`, string(resp.Data))
    85  		assert.Equal(t, []string{"first", "second"}, calls)
    86  	})
    87  
    88  	t.Run("invokes operation mutators", func(t *testing.T) {
    89  		var calls []string
    90  		exec.Use(&testParamMutator{
    91  			Mutate: func(ctx context.Context, req *graphql.RawParams) *gqlerror.Error {
    92  				calls = append(calls, "param")
    93  				return nil
    94  			},
    95  		})
    96  		exec.Use(&testCtxMutator{
    97  			Mutate: func(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error {
    98  				calls = append(calls, "context")
    99  				return nil
   100  			},
   101  		})
   102  		resp := query(exec, "", "{name}")
   103  		assert.Equal(t, `{"name":"test"}`, string(resp.Data))
   104  		assert.Equal(t, []string{"param", "context"}, calls)
   105  	})
   106  
   107  	t.Run("get query parse error in AroundResponses", func(t *testing.T) {
   108  		var errors1 gqlerror.List
   109  		var errors2 gqlerror.List
   110  		exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
   111  			resp := next(ctx)
   112  			errors1 = graphql.GetErrors(ctx)
   113  			errors2 = resp.Errors
   114  			return resp
   115  		})
   116  
   117  		resp := query(exec, "", "invalid")
   118  		assert.Equal(t, "", string(resp.Data))
   119  		assert.Equal(t, 1, len(resp.Errors))
   120  		assert.Equal(t, 1, len(errors1))
   121  		assert.Equal(t, 1, len(errors2))
   122  	})
   123  
   124  	t.Run("query caching", func(t *testing.T) {
   125  		ctx := context.Background()
   126  		cache := &graphql.MapCache{}
   127  		exec.SetQueryCache(cache)
   128  		qry := `query Foo {name}`
   129  
   130  		t.Run("cache miss populates cache", func(t *testing.T) {
   131  			resp := query(exec, "Foo", qry)
   132  			assert.Equal(t, `{"name":"test"}`, string(resp.Data))
   133  
   134  			cacheDoc, ok := cache.Get(ctx, qry)
   135  			require.True(t, ok)
   136  			require.Equal(t, "Foo", cacheDoc.(*ast.QueryDocument).Operations[0].Name)
   137  		})
   138  
   139  		t.Run("cache hits use document from cache", func(t *testing.T) {
   140  			doc, err := parser.ParseQuery(&ast.Source{Input: `query Bar {name}`})
   141  			require.Nil(t, err)
   142  			cache.Add(ctx, qry, doc)
   143  
   144  			resp := query(exec, "Bar", qry)
   145  			assert.Equal(t, `{"name":"test"}`, string(resp.Data))
   146  
   147  			cacheDoc, ok := cache.Get(ctx, qry)
   148  			require.True(t, ok)
   149  			require.Equal(t, "Bar", cacheDoc.(*ast.QueryDocument).Operations[0].Name)
   150  		})
   151  	})
   152  }
   153  
   154  type testParamMutator struct {
   155  	Mutate func(context.Context, *graphql.RawParams) *gqlerror.Error
   156  }
   157  
   158  func (m *testParamMutator) ExtensionName() string {
   159  	return "Operation: Mutate Parameters"
   160  }
   161  
   162  func (m *testParamMutator) Validate(s graphql.ExecutableSchema) error {
   163  	return nil
   164  }
   165  
   166  func (m *testParamMutator) MutateOperationParameters(ctx context.Context, r *graphql.RawParams) *gqlerror.Error {
   167  	return m.Mutate(ctx, r)
   168  }
   169  
   170  type testCtxMutator struct {
   171  	Mutate func(context.Context, *graphql.OperationContext) *gqlerror.Error
   172  }
   173  
   174  func (m *testCtxMutator) ExtensionName() string {
   175  	return "Operation: Mutate the Context"
   176  }
   177  
   178  func (m *testCtxMutator) Validate(s graphql.ExecutableSchema) error {
   179  	return nil
   180  }
   181  
   182  func (m *testCtxMutator) MutateOperationContext(ctx context.Context, rc *graphql.OperationContext) *gqlerror.Error {
   183  	return m.Mutate(ctx, rc)
   184  }
   185  
   186  func TestErrorServer(t *testing.T) {
   187  	exec := testexecutor.NewError()
   188  
   189  	t.Run("get resolver error in AroundResponses", func(t *testing.T) {
   190  		var errors1 gqlerror.List
   191  		var errors2 gqlerror.List
   192  		exec.AroundResponses(func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
   193  			resp := next(ctx)
   194  			errors1 = graphql.GetErrors(ctx)
   195  			errors2 = resp.Errors
   196  			return resp
   197  		})
   198  
   199  		resp := query(exec, "", "{name}")
   200  		assert.Equal(t, "null", string(resp.Data))
   201  		assert.Equal(t, 1, len(errors1))
   202  		assert.Equal(t, 1, len(errors2))
   203  	})
   204  }
   205  
   206  func query(exec *testexecutor.TestExecutor, op, q string) *graphql.Response {
   207  	ctx := graphql.StartOperationTrace(context.Background())
   208  	now := graphql.Now()
   209  	rc, err := exec.CreateOperationContext(ctx, &graphql.RawParams{
   210  		Query:         q,
   211  		OperationName: op,
   212  		ReadTime: graphql.TraceTiming{
   213  			Start: now,
   214  			End:   now,
   215  		},
   216  	})
   217  	if err != nil {
   218  		return exec.DispatchError(ctx, err)
   219  	}
   220  
   221  	resp, ctx2 := exec.DispatchOperation(ctx, rc)
   222  	return resp(ctx2)
   223  }