github.com/dgraph-io/dgraph@v1.2.8/graphql/resolve/mutation_test.go (about)

     1  /*
     2   * Copyright 2019 Dgraph Labs, Inc. and Contributors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package resolve
    18  
    19  import (
    20  	"encoding/json"
    21  	"io/ioutil"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/dgraph-io/dgraph/graphql/dgraph"
    26  	"github.com/dgraph-io/dgraph/graphql/schema"
    27  	"github.com/dgraph-io/dgraph/graphql/test"
    28  	"github.com/dgraph-io/dgraph/x"
    29  	"github.com/stretchr/testify/require"
    30  	"gopkg.in/yaml.v2"
    31  )
    32  
    33  // Tests showing that GraphQL mutations -> Dgraph mutations
    34  // is working as expected.
    35  //
    36  // Note: this doesn't include GQL validation errors!  The rewriting code assumes
    37  // it's rewriting a mutation that's valid (with valid variables) for the schema.
    38  // So can't test GQL errors here - that's integration testing on the pipeline to
    39  // ensure that those errors get caught before they reach rewriting.
    40  
    41  type testCase struct {
    42  	Name            string
    43  	GQLMutation     string
    44  	GQLVariables    string
    45  	Explanation     string
    46  	DGMutations     []*dgraphMutation
    47  	DGQuery         string
    48  	Error           *x.GqlError
    49  	ValidationError *x.GqlError
    50  }
    51  
    52  type dgraphMutation struct {
    53  	SetJSON    string
    54  	DeleteJSON string
    55  	Cond       string
    56  }
    57  
    58  func TestMutationRewriting(t *testing.T) {
    59  	t.Run("Validate Mutations", func(t *testing.T) {
    60  		mutationValidation(t, "validate_mutation_test.yaml", NewAddRewriter)
    61  	})
    62  	t.Run("Add Mutation Rewriting", func(t *testing.T) {
    63  		mutationRewriting(t, "add_mutation_test.yaml", NewAddRewriter)
    64  	})
    65  	t.Run("Update Mutation Rewriting", func(t *testing.T) {
    66  		mutationRewriting(t, "update_mutation_test.yaml", NewUpdateRewriter)
    67  	})
    68  	t.Run("Delete Mutation Rewriting", func(t *testing.T) {
    69  		mutationRewriting(t, "delete_mutation_test.yaml", NewDeleteRewriter)
    70  	})
    71  }
    72  
    73  func mutationValidation(t *testing.T, file string, rewriterFactory func() MutationRewriter) {
    74  	b, err := ioutil.ReadFile(file)
    75  	require.NoError(t, err, "Unable to read test file")
    76  
    77  	var tests []testCase
    78  	err = yaml.Unmarshal(b, &tests)
    79  	require.NoError(t, err, "Unable to unmarshal tests to yaml.")
    80  
    81  	gqlSchema := test.LoadSchemaFromFile(t, "schema.graphql")
    82  
    83  	for _, tcase := range tests {
    84  		t.Run(tcase.Name, func(t *testing.T) {
    85  			// -- Arrange --
    86  			var vars map[string]interface{}
    87  			if tcase.GQLVariables != "" {
    88  				err := json.Unmarshal([]byte(tcase.GQLVariables), &vars)
    89  				require.NoError(t, err)
    90  			}
    91  
    92  			_, err := gqlSchema.Operation(
    93  				&schema.Request{
    94  					Query:     tcase.GQLMutation,
    95  					Variables: vars,
    96  				})
    97  			require.NotNil(t, err)
    98  			require.Equal(t, err.Error(), tcase.ValidationError.Error())
    99  		})
   100  	}
   101  }
   102  
   103  func mutationRewriting(t *testing.T, file string, rewriterFactory func() MutationRewriter) {
   104  	b, err := ioutil.ReadFile(file)
   105  	require.NoError(t, err, "Unable to read test file")
   106  
   107  	var tests []testCase
   108  	err = yaml.Unmarshal(b, &tests)
   109  	require.NoError(t, err, "Unable to unmarshal tests to yaml.")
   110  
   111  	gqlSchema := test.LoadSchemaFromFile(t, "schema.graphql")
   112  
   113  	for _, tcase := range tests {
   114  		t.Run(tcase.Name, func(t *testing.T) {
   115  			// -- Arrange --
   116  			var vars map[string]interface{}
   117  			if tcase.GQLVariables != "" {
   118  				err := json.Unmarshal([]byte(tcase.GQLVariables), &vars)
   119  				require.NoError(t, err)
   120  			}
   121  
   122  			op, err := gqlSchema.Operation(
   123  				&schema.Request{
   124  					Query:     tcase.GQLMutation,
   125  					Variables: vars,
   126  				})
   127  			require.NoError(t, err)
   128  			mut := test.GetMutation(t, op)
   129  			rewriterToTest := rewriterFactory()
   130  
   131  			// -- Act --
   132  			q, muts, err := rewriterToTest.Rewrite(mut)
   133  
   134  			// -- Assert --
   135  			if tcase.Error != nil || err != nil {
   136  				require.Equal(t, tcase.Error.Error(), err.Error())
   137  			} else {
   138  				require.Equal(t, tcase.DGQuery, dgraph.AsString(q))
   139  				require.Len(t, muts, len(tcase.DGMutations))
   140  				for i, expected := range tcase.DGMutations {
   141  					require.Equal(t, expected.Cond, muts[i].Cond)
   142  					if len(muts[i].SetJson) > 0 || expected.SetJSON != "" {
   143  						require.JSONEq(t, expected.SetJSON, string(muts[i].SetJson))
   144  					}
   145  					if len(muts[i].DeleteJson) > 0 || expected.DeleteJSON != "" {
   146  						require.JSONEq(t, expected.DeleteJSON, string(muts[i].DeleteJson))
   147  					}
   148  				}
   149  			}
   150  		})
   151  	}
   152  }
   153  
   154  func TestMutationQueryRewriting(t *testing.T) {
   155  	testTypes := map[string]struct {
   156  		mut      string
   157  		rewriter func() MutationRewriter
   158  		assigned map[string]string
   159  		result   map[string]interface{}
   160  	}{
   161  		"Add Post ": {
   162  			mut:      `addPost(input: [{title: "A Post", author: {id: "0x1"}}])`,
   163  			rewriter: NewAddRewriter,
   164  			assigned: map[string]string{"Post1": "0x4"},
   165  		},
   166  		"Update Post ": {
   167  			mut: `updatePost(input: {filter: {postID
   168  				:  ["0x4"]}, set: {text: "Updated text"} }) `,
   169  			rewriter: NewUpdateRewriter,
   170  			result: map[string]interface{}{
   171  				"updatePost": []interface{}{map[string]interface{}{"uid": "0x4"}}},
   172  		},
   173  	}
   174  
   175  	allowedTestTypes := map[string][]string{
   176  		"UPDATE_MUTATION":     []string{"Update Post "},
   177  		"ADD_UPDATE_MUTATION": []string{"Add Post ", "Update Post "},
   178  	}
   179  
   180  	b, err := ioutil.ReadFile("mutation_query_test.yaml")
   181  	require.NoError(t, err, "Unable to read test file")
   182  
   183  	var tests map[string][]QueryRewritingCase
   184  	err = yaml.Unmarshal(b, &tests)
   185  	require.NoError(t, err, "Unable to unmarshal tests to yaml.")
   186  
   187  	gqlSchema := test.LoadSchemaFromFile(t, "schema.graphql")
   188  
   189  	for testType := range tests {
   190  		for _, name := range allowedTestTypes[testType] {
   191  			tt := testTypes[name]
   192  			for _, tcase := range tests[testType] {
   193  				t.Run(name+testType+tcase.Name, func(t *testing.T) {
   194  					rewriter := tt.rewriter()
   195  					// -- Arrange --
   196  					gqlMutationStr := strings.Replace(tcase.GQLQuery, testType, tt.mut, 1)
   197  					op, err := gqlSchema.Operation(
   198  						&schema.Request{
   199  							Query:     gqlMutationStr,
   200  							Variables: tcase.Variables,
   201  						})
   202  					require.NoError(t, err)
   203  					gqlMutation := test.GetMutation(t, op)
   204  					_, _, err = rewriter.Rewrite(gqlMutation)
   205  					require.Nil(t, err)
   206  
   207  					// -- Act --
   208  					dgQuery, err := rewriter.FromMutationResult(
   209  						gqlMutation, tt.assigned, tt.result)
   210  
   211  					// -- Assert --
   212  					require.Nil(t, err)
   213  					require.Equal(t, tcase.DGQuery, dgraph.AsString(dgQuery))
   214  				})
   215  			}
   216  		}
   217  	}
   218  }