...

Source file src/github.com/shurcooL/githubv4/githubv4_test.go

Documentation: github.com/shurcooL/githubv4

     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  	// Shouldn't panic with nil parameter.
    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"` // GraphQL alias.
    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 { // Common fields for all events.
   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  // localRoundTripper is an http.RoundTripper that executes HTTP transactions
   268  // by using handler directly, instead of going over an HTTP connection.
   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