1 package githubv4_test
2
3 import (
4 "context"
5 "io"
6 "net/http"
7 "net/http/httptest"
8 "reflect"
9 "testing"
10 "time"
11
12 "github.com/shurcooL/githubv4"
13 )
14
15 func TestNewClient_nil(t *testing.T) {
16
17 client := githubv4.NewClient(nil)
18 _ = client
19 }
20
21 func TestClient_Query(t *testing.T) {
22 mux := http.NewServeMux()
23 mux.HandleFunc("/graphql", func(w http.ResponseWriter, req *http.Request) {
24 if got, want := req.Method, http.MethodPost; got != want {
25 t.Errorf("got request method: %v, want: %v", got, want)
26 }
27 body := mustRead(req.Body)
28 if got, want := body, `{"query":"{viewer{login,bio}}"}`+"\n"; got != want {
29 t.Errorf("got body: %v, want %v", got, want)
30 }
31 w.Header().Set("Content-Type", "application/json")
32 mustWrite(w, `{"data": {"viewer": {"login": "gopher", "bio": "The Go gopher."}}}`)
33 })
34 client := githubv4.NewClient(&http.Client{Transport: localRoundTripper{handler: mux}})
35
36 type query struct {
37 Viewer struct {
38 Login githubv4.String
39 Biography githubv4.String `graphql:"bio"`
40 }
41 }
42
43 var q query
44 err := client.Query(context.Background(), &q, nil)
45 if err != nil {
46 t.Fatal(err)
47 }
48 got := q
49
50 var want query
51 want.Viewer.Login = "gopher"
52 want.Viewer.Biography = "The Go gopher."
53 if !reflect.DeepEqual(got, want) {
54 t.Errorf("client.Query got: %v, want: %v", got, want)
55 }
56 }
57
58 func TestClient_Query_errorResponse(t *testing.T) {
59 mux := http.NewServeMux()
60 mux.HandleFunc("/graphql", func(w http.ResponseWriter, req *http.Request) {
61 w.Header().Set("Content-Type", "application/json")
62 mustWrite(w, `{
63 "data": null,
64 "errors": [
65 {
66 "message": "Field 'bad' doesn't exist on type 'Query'",
67 "locations": [
68 {
69 "line": 7,
70 "column": 3
71 }
72 ]
73 }
74 ]
75 }`)
76 })
77 client := githubv4.NewClient(&http.Client{Transport: localRoundTripper{handler: mux}})
78
79 var q struct {
80 Bad githubv4.String
81 }
82 err := client.Query(context.Background(), &q, nil)
83 if err == nil {
84 t.Fatal("got error: nil, want: non-nil")
85 }
86 if got, want := err.Error(), "Field 'bad' doesn't exist on type 'Query'"; got != want {
87 t.Errorf("got error: %v, want: %v", got, want)
88 }
89 }
90
91 func TestClient_Query_errorStatusCode(t *testing.T) {
92 mux := http.NewServeMux()
93 mux.HandleFunc("/graphql", func(w http.ResponseWriter, req *http.Request) {
94 http.Error(w, "404 Not Found", http.StatusNotFound)
95 })
96 client := githubv4.NewClient(&http.Client{Transport: localRoundTripper{handler: mux}})
97
98 var q struct {
99 Viewer struct {
100 Login githubv4.String
101 }
102 }
103 err := client.Query(context.Background(), &q, nil)
104 if err == nil {
105 t.Fatal("got error: nil, want: non-nil")
106 }
107 if got, want := err.Error(), `non-200 OK status code: 404 Not Found body: "404 Not Found\n"`; got != want {
108 t.Errorf("got error: %v, want: %v", got, want)
109 }
110 }
111
112 func TestClient_Query_union(t *testing.T) {
113 mux := http.NewServeMux()
114 mux.HandleFunc("/graphql", func(w http.ResponseWriter, req *http.Request) {
115 if got, want := req.Method, http.MethodPost; got != want {
116 t.Errorf("got request method: %v, want: %v", got, want)
117 }
118 body := mustRead(req.Body)
119 if got, want := body, `{"query":"query($issueNumber:Int!$repositoryName:String!$repositoryOwner:String!){repository(owner: $repositoryOwner, name: $repositoryName){issue(number: $issueNumber){timeline(first: 10){nodes{__typename,...on ClosedEvent{actor{login},createdAt},...on ReopenedEvent{actor{login},createdAt},...on RenamedTitleEvent{actor{login},createdAt,currentTitle,previousTitle}}}}}}","variables":{"issueNumber":1,"repositoryName":"go","repositoryOwner":"golang"}}`+"\n"; got != want {
120 t.Errorf("got body: %v, want %v", got, want)
121 }
122 w.Header().Set("Content-Type", "application/json")
123 mustWrite(w, `{"data": {
124 "repository": {
125 "issue": {
126 "timeline": {
127 "nodes": [
128 {
129 "__typename": "RenamedTitleEvent",
130 "createdAt": "2017-06-29T04:12:01Z",
131 "actor": {
132 "login": "gopher"
133 },
134 "currentTitle": "new",
135 "previousTitle": "old"
136 }
137 ]
138 }
139 }
140 }
141 }}`)
142 })
143 client := githubv4.NewClient(&http.Client{Transport: localRoundTripper{handler: mux}})
144
145 type event struct {
146 Actor struct{ Login githubv4.String }
147 CreatedAt githubv4.DateTime
148 }
149 type issueTimelineItem struct {
150 Typename string `graphql:"__typename"`
151 ClosedEvent struct {
152 event
153 } `graphql:"...on ClosedEvent"`
154 ReopenedEvent struct {
155 event
156 } `graphql:"...on ReopenedEvent"`
157 RenamedTitleEvent struct {
158 event
159 CurrentTitle string
160 PreviousTitle string
161 } `graphql:"...on RenamedTitleEvent"`
162 }
163 type query struct {
164 Repository struct {
165 Issue struct {
166 Timeline struct {
167 Nodes []issueTimelineItem
168 } `graphql:"timeline(first: 10)"`
169 } `graphql:"issue(number: $issueNumber)"`
170 } `graphql:"repository(owner: $repositoryOwner, name: $repositoryName)"`
171 }
172
173 var q query
174 variables := map[string]interface{}{
175 "repositoryOwner": githubv4.String("golang"),
176 "repositoryName": githubv4.String("go"),
177 "issueNumber": githubv4.Int(1),
178 }
179 err := client.Query(context.Background(), &q, variables)
180 if err != nil {
181 t.Fatal(err)
182 }
183 got := q
184
185 var want query
186 want.Repository.Issue.Timeline.Nodes = make([]issueTimelineItem, 1)
187 want.Repository.Issue.Timeline.Nodes[0].Typename = "RenamedTitleEvent"
188 want.Repository.Issue.Timeline.Nodes[0].RenamedTitleEvent.Actor.Login = "gopher"
189 want.Repository.Issue.Timeline.Nodes[0].RenamedTitleEvent.CreatedAt.Time = time.Unix(1498709521, 0).UTC()
190 want.Repository.Issue.Timeline.Nodes[0].RenamedTitleEvent.CurrentTitle = "new"
191 want.Repository.Issue.Timeline.Nodes[0].RenamedTitleEvent.PreviousTitle = "old"
192 want.Repository.Issue.Timeline.Nodes[0].ClosedEvent.event = want.Repository.Issue.Timeline.Nodes[0].RenamedTitleEvent.event
193 want.Repository.Issue.Timeline.Nodes[0].ReopenedEvent.event = want.Repository.Issue.Timeline.Nodes[0].RenamedTitleEvent.event
194 if !reflect.DeepEqual(got, want) {
195 t.Errorf("client.Query:\ngot: %+v\nwant: %+v", got, want)
196 }
197 }
198
199 func TestClient_Mutate(t *testing.T) {
200 mux := http.NewServeMux()
201 mux.HandleFunc("/graphql", func(w http.ResponseWriter, req *http.Request) {
202 if got, want := req.Method, http.MethodPost; got != want {
203 t.Errorf("got request method: %v, want: %v", got, want)
204 }
205 body := mustRead(req.Body)
206 if got, want := body, `{"query":"mutation($input:AddReactionInput!){addReaction(input:$input){reaction{content},subject{id,reactionGroups{users{totalCount}}}}}","variables":{"input":{"subjectId":"MDU6SXNzdWUyMTc5NTQ0OTc=","content":"HOORAY"}}}`+"\n"; got != want {
207 t.Errorf("got body: %v, want %v", got, want)
208 }
209 w.Header().Set("Content-Type", "application/json")
210 mustWrite(w, `{"data": {
211 "addReaction": {
212 "reaction": {
213 "content": "HOORAY"
214 },
215 "subject": {
216 "id": "MDU6SXNzdWUyMTc5NTQ0OTc=",
217 "reactionGroups": [
218 {
219 "users": {"totalCount": 3}
220 }
221 ]
222 }
223 }
224 }}`)
225 })
226 client := githubv4.NewClient(&http.Client{Transport: localRoundTripper{handler: mux}})
227
228 type reactionGroup struct {
229 Users struct {
230 TotalCount githubv4.Int
231 }
232 }
233 type mutation struct {
234 AddReaction struct {
235 Reaction struct {
236 Content githubv4.ReactionContent
237 }
238 Subject struct {
239 ID githubv4.ID
240 ReactionGroups []reactionGroup
241 }
242 } `graphql:"addReaction(input:$input)"`
243 }
244
245 var m mutation
246 input := githubv4.AddReactionInput{
247 SubjectID: "MDU6SXNzdWUyMTc5NTQ0OTc=",
248 Content: githubv4.ReactionContentHooray,
249 }
250 err := client.Mutate(context.Background(), &m, input, nil)
251 if err != nil {
252 t.Fatal(err)
253 }
254 got := m
255
256 var want mutation
257 want.AddReaction.Reaction.Content = githubv4.ReactionContentHooray
258 want.AddReaction.Subject.ID = "MDU6SXNzdWUyMTc5NTQ0OTc="
259 var rg reactionGroup
260 rg.Users.TotalCount = 3
261 want.AddReaction.Subject.ReactionGroups = []reactionGroup{rg}
262 if !reflect.DeepEqual(got, want) {
263 t.Errorf("client.Query got: %v, want: %v", got, want)
264 }
265 }
266
267
268
269 type localRoundTripper struct {
270 handler http.Handler
271 }
272
273 func (l localRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
274 w := httptest.NewRecorder()
275 l.handler.ServeHTTP(w, req)
276 return w.Result(), nil
277 }
278
279 func mustRead(r io.Reader) string {
280 b, err := io.ReadAll(r)
281 if err != nil {
282 panic(err)
283 }
284 return string(b)
285 }
286
287 func mustWrite(w io.Writer, s string) {
288 _, err := io.WriteString(w, s)
289 if err != nil {
290 panic(err)
291 }
292 }
293
View as plain text