1package graphql_test
2
3import (
4	"reflect"
5	"testing"
6
7	"github.com/graphql-go/graphql"
8	"github.com/graphql-go/graphql/gqlerrors"
9	"github.com/graphql-go/graphql/language/location"
10	"github.com/graphql-go/graphql/testutil"
11)
12
13// testNumberHolder maps to numberHolderType
14type testNumberHolder struct {
15	TheNumber int `json:"theNumber"` // map field to `theNumber` so it can be resolve by the default ResolveFn
16}
17type testRoot struct {
18	NumberHolder *testNumberHolder
19}
20
21func newTestRoot(originalNumber int) *testRoot {
22	return &testRoot{
23		NumberHolder: &testNumberHolder{originalNumber},
24	}
25}
26func (r *testRoot) ImmediatelyChangeTheNumber(newNumber int) *testNumberHolder {
27	r.NumberHolder.TheNumber = newNumber
28	return r.NumberHolder
29}
30func (r *testRoot) PromiseToChangeTheNumber(newNumber int) *testNumberHolder {
31	return r.ImmediatelyChangeTheNumber(newNumber)
32}
33func (r *testRoot) FailToChangeTheNumber(newNumber int) *testNumberHolder {
34	panic("Cannot change the number")
35}
36func (r *testRoot) PromiseAndFailToChangeTheNumber(newNumber int) *testNumberHolder {
37	panic("Cannot change the number")
38}
39
40// numberHolderType creates a mapping to testNumberHolder
41var numberHolderType = graphql.NewObject(graphql.ObjectConfig{
42	Name: "NumberHolder",
43	Fields: graphql.Fields{
44		"theNumber": &graphql.Field{
45			Type: graphql.Int,
46		},
47	},
48})
49
50var mutationsTestSchema, _ = graphql.NewSchema(graphql.SchemaConfig{
51	Query: graphql.NewObject(graphql.ObjectConfig{
52		Name: "Query",
53		Fields: graphql.Fields{
54			"numberHolder": &graphql.Field{
55				Type: numberHolderType,
56			},
57		},
58	}),
59	Mutation: graphql.NewObject(graphql.ObjectConfig{
60		Name: "Mutation",
61		Fields: graphql.Fields{
62			"immediatelyChangeTheNumber": &graphql.Field{
63				Type: numberHolderType,
64				Args: graphql.FieldConfigArgument{
65					"newNumber": &graphql.ArgumentConfig{
66						Type: graphql.Int,
67					},
68				},
69				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
70					newNumber := 0
71					obj, _ := p.Source.(*testRoot)
72					newNumber, _ = p.Args["newNumber"].(int)
73					return obj.ImmediatelyChangeTheNumber(newNumber), nil
74				},
75			},
76			"promiseToChangeTheNumber": &graphql.Field{
77				Type: numberHolderType,
78				Args: graphql.FieldConfigArgument{
79					"newNumber": &graphql.ArgumentConfig{
80						Type: graphql.Int,
81					},
82				},
83				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
84					newNumber := 0
85					obj, _ := p.Source.(*testRoot)
86					newNumber, _ = p.Args["newNumber"].(int)
87					return obj.PromiseToChangeTheNumber(newNumber), nil
88				},
89			},
90			"failToChangeTheNumber": &graphql.Field{
91				Type: numberHolderType,
92				Args: graphql.FieldConfigArgument{
93					"newNumber": &graphql.ArgumentConfig{
94						Type: graphql.Int,
95					},
96				},
97				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
98					newNumber := 0
99					obj, _ := p.Source.(*testRoot)
100					newNumber, _ = p.Args["newNumber"].(int)
101					return obj.FailToChangeTheNumber(newNumber), nil
102				},
103			},
104			"promiseAndFailToChangeTheNumber": &graphql.Field{
105				Type: numberHolderType,
106				Args: graphql.FieldConfigArgument{
107					"newNumber": &graphql.ArgumentConfig{
108						Type: graphql.Int,
109					},
110				},
111				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
112					newNumber := 0
113					obj, _ := p.Source.(*testRoot)
114					newNumber, _ = p.Args["newNumber"].(int)
115					return obj.PromiseAndFailToChangeTheNumber(newNumber), nil
116				},
117			},
118		},
119	}),
120})
121
122func TestMutations_ExecutionOrdering_EvaluatesMutationsSerially(t *testing.T) {
123
124	root := newTestRoot(6)
125	doc := `mutation M {
126      first: immediatelyChangeTheNumber(newNumber: 1) {
127        theNumber
128      },
129      second: promiseToChangeTheNumber(newNumber: 2) {
130        theNumber
131      },
132      third: immediatelyChangeTheNumber(newNumber: 3) {
133        theNumber
134      }
135      fourth: promiseToChangeTheNumber(newNumber: 4) {
136        theNumber
137      },
138      fifth: immediatelyChangeTheNumber(newNumber: 5) {
139        theNumber
140      }
141    }`
142
143	expected := &graphql.Result{
144		Data: map[string]interface{}{
145			"first": map[string]interface{}{
146				"theNumber": 1,
147			},
148			"second": map[string]interface{}{
149				"theNumber": 2,
150			},
151			"third": map[string]interface{}{
152				"theNumber": 3,
153			},
154			"fourth": map[string]interface{}{
155				"theNumber": 4,
156			},
157			"fifth": map[string]interface{}{
158				"theNumber": 5,
159			},
160		},
161	}
162	// parse query
163	ast := testutil.TestParse(t, doc)
164
165	// execute
166	ep := graphql.ExecuteParams{
167		Schema: mutationsTestSchema,
168		AST:    ast,
169		Root:   root,
170	}
171	result := testutil.TestExecute(t, ep)
172	if len(result.Errors) != len(expected.Errors) {
173		t.Fatalf("Unexpected errors, Diff: %v", testutil.Diff(expected.Errors, result.Errors))
174	}
175	if !reflect.DeepEqual(expected, result) {
176		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expected, result))
177	}
178}
179func TestMutations_EvaluatesMutationsCorrectlyInThePresenceOfAFailedMutation(t *testing.T) {
180
181	root := newTestRoot(6)
182	doc := `mutation M {
183      first: immediatelyChangeTheNumber(newNumber: 1) {
184        theNumber
185      },
186      second: promiseToChangeTheNumber(newNumber: 2) {
187        theNumber
188      },
189      third: failToChangeTheNumber(newNumber: 3) {
190        theNumber
191      }
192      fourth: promiseToChangeTheNumber(newNumber: 4) {
193        theNumber
194      },
195      fifth: immediatelyChangeTheNumber(newNumber: 5) {
196        theNumber
197      }
198      sixth: promiseAndFailToChangeTheNumber(newNumber: 6) {
199        theNumber
200      }
201    }`
202
203	expected := &graphql.Result{
204		Data: map[string]interface{}{
205			"first": map[string]interface{}{
206				"theNumber": 1,
207			},
208			"second": map[string]interface{}{
209				"theNumber": 2,
210			},
211			"third": nil,
212			"fourth": map[string]interface{}{
213				"theNumber": 4,
214			},
215			"fifth": map[string]interface{}{
216				"theNumber": 5,
217			},
218			"sixth": nil,
219		},
220		Errors: []gqlerrors.FormattedError{
221			{
222				Message: `Cannot change the number`,
223				Locations: []location.SourceLocation{
224					{Line: 8, Column: 7},
225				},
226			},
227			{
228				Message: `Cannot change the number`,
229				Locations: []location.SourceLocation{
230					{Line: 17, Column: 7},
231				},
232			},
233		},
234	}
235	// parse query
236	ast := testutil.TestParse(t, doc)
237
238	// execute
239	ep := graphql.ExecuteParams{
240		Schema: mutationsTestSchema,
241		AST:    ast,
242		Root:   root,
243	}
244	result := testutil.TestExecute(t, ep)
245	if len(result.Errors) != len(expected.Errors) {
246		t.Fatalf("Unexpected errors, Diff: %v", testutil.Diff(expected.Errors, result.Errors))
247	}
248	t.Skipf("Testing equality for slice of errors in results")
249	if !reflect.DeepEqual(expected, result) {
250		t.Fatalf("Unexpected result, Diff: %v", testutil.Diff(expected, result))
251	}
252}
253