1 package graphql_test
2
3 import (
4 "context"
5 "io"
6 "net/http"
7 "net/http/httptest"
8 "testing"
9
10 graphql "github.com/cli/shurcooL-graphql"
11 )
12
13 func TestClient_Query_partialDataWithErrorResponse(t *testing.T) {
14 mux := http.NewServeMux()
15 mux.HandleFunc("/graphql", func(w http.ResponseWriter, req *http.Request) {
16 w.Header().Set("Content-Type", "application/json")
17 mustWrite(w, `{
18 "data": {
19 "node1": {
20 "id": "MDEyOklzc3VlQ29tbWVudDE2OTQwNzk0Ng=="
21 },
22 "node2": null
23 },
24 "errors": [
25 {
26 "message": "Could not resolve to a node with the global id of 'NotExist'",
27 "type": "NOT_FOUND",
28 "path": [
29 "node2"
30 ],
31 "locations": [
32 {
33 "line": 10,
34 "column": 4
35 }
36 ]
37 }
38 ]
39 }`)
40 })
41 client := graphql.NewClient("/graphql", &http.Client{Transport: localRoundTripper{handler: mux}})
42
43 var q struct {
44 Node1 *struct {
45 ID graphql.ID
46 } `graphql:"node1: node(id: \"MDEyOklzc3VlQ29tbWVudDE2OTQwNzk0Ng==\")"`
47 Node2 *struct {
48 ID graphql.ID
49 } `graphql:"node2: node(id: \"NotExist\")"`
50 }
51 err := client.Query(context.Background(), &q, nil)
52 if err == nil {
53 t.Fatal("got error: nil, want: non-nil")
54 }
55 if got, want := err.Error(), "Message: Could not resolve to a node with the global id of 'NotExist', Locations: [{Line:10 Column:4}]"; got != want {
56 t.Errorf("got error: %v, want: %v", got, want)
57 }
58 if q.Node1 == nil || q.Node1.ID != "MDEyOklzc3VlQ29tbWVudDE2OTQwNzk0Ng==" {
59 t.Errorf("got wrong q.Node1: %v", q.Node1)
60 }
61 if q.Node2 != nil {
62 t.Errorf("got non-nil q.Node2: %v, want: nil", *q.Node2)
63 }
64 }
65
66 func TestClient_Query_noDataWithErrorResponse(t *testing.T) {
67 mux := http.NewServeMux()
68 mux.HandleFunc("/graphql", func(w http.ResponseWriter, req *http.Request) {
69 w.Header().Set("Content-Type", "application/json")
70 mustWrite(w, `{
71 "errors": [
72 {
73 "message": "Field 'user' is missing required arguments: login",
74 "locations": [
75 {
76 "line": 7,
77 "column": 3
78 }
79 ]
80 }
81 ]
82 }`)
83 })
84 client := graphql.NewClient("/graphql", &http.Client{Transport: localRoundTripper{handler: mux}})
85
86 var q struct {
87 User struct {
88 Name graphql.String
89 }
90 }
91 err := client.Query(context.Background(), &q, nil)
92 if err == nil {
93 t.Fatal("got error: nil, want: non-nil")
94 }
95 if got, want := err.Error(), "Message: Field 'user' is missing required arguments: login, Locations: [{Line:7 Column:3}]"; got != want {
96 t.Errorf("got error: %v, want: %v", got, want)
97 }
98 if q.User.Name != "" {
99 t.Errorf("got non-empty q.User.Name: %v", q.User.Name)
100 }
101 }
102
103 func TestClient_Query_errorStatusCode(t *testing.T) {
104 mux := http.NewServeMux()
105 mux.HandleFunc("/graphql", func(w http.ResponseWriter, req *http.Request) {
106 http.Error(w, "important message", http.StatusInternalServerError)
107 })
108 client := graphql.NewClient("/graphql", &http.Client{Transport: localRoundTripper{handler: mux}})
109
110 var q struct {
111 User struct {
112 Name graphql.String
113 }
114 }
115 err := client.Query(context.Background(), &q, nil)
116 if err == nil {
117 t.Fatal("got error: nil, want: non-nil")
118 }
119 if got, want := err.Error(), `non-200 OK status code: 500 Internal Server Error body: "important message\n"`; got != want {
120 t.Errorf("got error: %v, want: %v", got, want)
121 }
122 if q.User.Name != "" {
123 t.Errorf("got non-empty q.User.Name: %v", q.User.Name)
124 }
125 }
126
127
128
129 func TestClient_Query_emptyVariables(t *testing.T) {
130 mux := http.NewServeMux()
131 mux.HandleFunc("/graphql", func(w http.ResponseWriter, req *http.Request) {
132 body := mustRead(req.Body)
133 if got, want := body, `{"query":"{user{name}}"}`+"\n"; got != want {
134 t.Errorf("got body: %v, want %v", got, want)
135 }
136 w.Header().Set("Content-Type", "application/json")
137 mustWrite(w, `{"data": {"user": {"name": "Gopher"}}}`)
138 })
139 client := graphql.NewClient("/graphql", &http.Client{Transport: localRoundTripper{handler: mux}})
140
141 var q struct {
142 User struct {
143 Name string
144 }
145 }
146 err := client.Query(context.Background(), &q, map[string]any{})
147 if err != nil {
148 t.Fatal(err)
149 }
150 if got, want := q.User.Name, "Gopher"; got != want {
151 t.Errorf("got q.User.Name: %q, want: %q", got, want)
152 }
153 }
154
155
156
157 type localRoundTripper struct {
158 handler http.Handler
159 }
160
161 func (l localRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
162 w := httptest.NewRecorder()
163 l.handler.ServeHTTP(w, req)
164 return w.Result(), nil
165 }
166
167 func mustRead(r io.Reader) string {
168 b, err := io.ReadAll(r)
169 if err != nil {
170 panic(err)
171 }
172 return string(b)
173 }
174
175 func mustWrite(w io.Writer, s string) {
176 _, err := io.WriteString(w, s)
177 if err != nil {
178 panic(err)
179 }
180 }
181
View as plain text