1package graphql_test
2
3import (
4	"context"
5	"reflect"
6	"testing"
7
8	"github.com/graphql-go/graphql"
9	"github.com/graphql-go/graphql/testutil"
10)
11
12type T struct {
13	Query     string
14	Schema    graphql.Schema
15	Expected  interface{}
16	Variables map[string]interface{}
17}
18
19var Tests = []T{}
20
21func init() {
22	Tests = []T{
23		{
24			Query: `
25				query HeroNameQuery {
26					hero {
27						name
28					}
29				}
30			`,
31			Schema: testutil.StarWarsSchema,
32			Expected: &graphql.Result{
33				Data: map[string]interface{}{
34					"hero": map[string]interface{}{
35						"name": "R2-D2",
36					},
37				},
38			},
39		},
40		{
41			Query: `
42				query HeroNameAndFriendsQuery {
43					hero {
44						id
45						name
46						friends {
47							name
48						}
49					}
50				}
51			`,
52			Schema: testutil.StarWarsSchema,
53			Expected: &graphql.Result{
54				Data: map[string]interface{}{
55					"hero": map[string]interface{}{
56						"id":   "2001",
57						"name": "R2-D2",
58						"friends": []interface{}{
59							map[string]interface{}{
60								"name": "Luke Skywalker",
61							},
62							map[string]interface{}{
63								"name": "Han Solo",
64							},
65							map[string]interface{}{
66								"name": "Leia Organa",
67							},
68						},
69					},
70				},
71			},
72		},
73		{
74			Query: `
75				query HumanByIdQuery($id: String!) {
76					human(id: $id) {
77						name
78					}
79				}
80			`,
81			Schema: testutil.StarWarsSchema,
82			Expected: &graphql.Result{
83				Data: map[string]interface{}{
84					"human": map[string]interface{}{
85						"name": "Darth Vader",
86					},
87				},
88			},
89			Variables: map[string]interface{}{
90				"id": "1001",
91			},
92		},
93	}
94}
95
96func TestQuery(t *testing.T) {
97	for _, test := range Tests {
98		params := graphql.Params{
99			Schema:         test.Schema,
100			RequestString:  test.Query,
101			VariableValues: test.Variables,
102		}
103		testGraphql(test, params, t)
104	}
105}
106
107func testGraphql(test T, p graphql.Params, t *testing.T) {
108	result := graphql.Do(p)
109	if len(result.Errors) > 0 {
110		t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
111	}
112	if !reflect.DeepEqual(result, test.Expected) {
113		t.Fatalf("wrong result, query: %v, graphql result diff: %v", test.Query, testutil.Diff(test.Expected, result))
114	}
115}
116
117func TestBasicGraphQLExample(t *testing.T) {
118	// taken from `graphql-js` README
119
120	helloFieldResolved := func(p graphql.ResolveParams) (interface{}, error) {
121		return "world", nil
122	}
123
124	schema, err := graphql.NewSchema(graphql.SchemaConfig{
125		Query: graphql.NewObject(graphql.ObjectConfig{
126			Name: "RootQueryType",
127			Fields: graphql.Fields{
128				"hello": &graphql.Field{
129					Description: "Returns `world`",
130					Type:        graphql.String,
131					Resolve:     helloFieldResolved,
132				},
133			},
134		}),
135	})
136	if err != nil {
137		t.Fatalf("wrong result, unexpected errors: %v", err.Error())
138	}
139	query := "{ hello }"
140	var expected interface{}
141	expected = map[string]interface{}{
142		"hello": "world",
143	}
144
145	result := graphql.Do(graphql.Params{
146		Schema:        schema,
147		RequestString: query,
148	})
149	if len(result.Errors) > 0 {
150		t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
151	}
152	if !reflect.DeepEqual(result.Data, expected) {
153		t.Fatalf("wrong result, query: %v, graphql result diff: %v", query, testutil.Diff(expected, result))
154	}
155
156}
157
158func TestThreadsContextFromParamsThrough(t *testing.T) {
159	extractFieldFromContextFn := func(p graphql.ResolveParams) (interface{}, error) {
160		return p.Context.Value(p.Args["key"]), nil
161	}
162
163	schema, err := graphql.NewSchema(graphql.SchemaConfig{
164		Query: graphql.NewObject(graphql.ObjectConfig{
165			Name: "Query",
166			Fields: graphql.Fields{
167				"value": &graphql.Field{
168					Type: graphql.String,
169					Args: graphql.FieldConfigArgument{
170						"key": &graphql.ArgumentConfig{Type: graphql.String},
171					},
172					Resolve: extractFieldFromContextFn,
173				},
174			},
175		}),
176	})
177	if err != nil {
178		t.Fatalf("wrong result, unexpected errors: %v", err.Error())
179	}
180	query := `{ value(key:"a") }`
181
182	result := graphql.Do(graphql.Params{
183		Schema:        schema,
184		RequestString: query,
185		Context:       context.WithValue(context.TODO(), "a", "xyz"),
186	})
187	if len(result.Errors) > 0 {
188		t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
189	}
190	expected := map[string]interface{}{"value": "xyz"}
191	if !reflect.DeepEqual(result.Data, expected) {
192		t.Fatalf("wrong result, query: %v, graphql result diff: %v", query, testutil.Diff(expected, result))
193	}
194
195}
196
197func TestNewErrorChecksNilNodes(t *testing.T) {
198	schema, err := graphql.NewSchema(graphql.SchemaConfig{
199		Query: graphql.NewObject(graphql.ObjectConfig{
200			Name: "Query",
201			Fields: graphql.Fields{
202				"graphql_is": &graphql.Field{
203					Type: graphql.String,
204					Resolve: func(p graphql.ResolveParams) (interface{}, error) {
205						return "", nil
206					},
207				},
208			},
209		}),
210	})
211	if err != nil {
212		t.Fatalf("unexpected errors: %v", err.Error())
213	}
214	query := `{graphql_is:great(sort:ByPopularity)}{stars}`
215	result := graphql.Do(graphql.Params{
216		Schema:        schema,
217		RequestString: query,
218	})
219	if len(result.Errors) == 0 {
220		t.Fatalf("expected errors, got: %v", result)
221	}
222}
223
224func TestEmptyStringIsNotNull(t *testing.T) {
225	checkForEmptyString := func(p graphql.ResolveParams) (interface{}, error) {
226		arg := p.Args["arg"]
227		if arg == nil || arg.(string) != "" {
228			t.Errorf("Expected empty string for input arg, got %#v", arg)
229		}
230		return "yay", nil
231	}
232	returnEmptyString := func(p graphql.ResolveParams) (interface{}, error) {
233		return "", nil
234	}
235
236	schema, err := graphql.NewSchema(graphql.SchemaConfig{
237		Query: graphql.NewObject(graphql.ObjectConfig{
238			Name: "Query",
239			Fields: graphql.Fields{
240				"checkEmptyArg": &graphql.Field{
241					Type: graphql.String,
242					Args: graphql.FieldConfigArgument{
243						"arg": &graphql.ArgumentConfig{Type: graphql.String},
244					},
245					Resolve: checkForEmptyString,
246				},
247				"checkEmptyResult": &graphql.Field{
248					Type:    graphql.String,
249					Resolve: returnEmptyString,
250				},
251			},
252		}),
253	})
254	if err != nil {
255		t.Fatalf("wrong result, unexpected errors: %v", err.Error())
256	}
257	query := `{ checkEmptyArg(arg:"") checkEmptyResult }`
258
259	result := graphql.Do(graphql.Params{
260		Schema:        schema,
261		RequestString: query,
262	})
263	if len(result.Errors) > 0 {
264		t.Fatalf("wrong result, unexpected errors: %v", result.Errors)
265	}
266	expected := map[string]interface{}{"checkEmptyArg": "yay", "checkEmptyResult": ""}
267	if !reflect.DeepEqual(result.Data, expected) {
268		t.Errorf("wrong result, query: %v, graphql result diff: %v", query, testutil.Diff(expected, result))
269	}
270}
271