...

Source file src/github.com/cli/go-gh/v2/pkg/api/graphql_client_test.go

Documentation: github.com/cli/go-gh/v2/pkg/api

     1  package api
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"net/http"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/stretchr/testify/assert"
    11  	"gopkg.in/h2non/gock.v1"
    12  )
    13  
    14  func TestGraphQLClient(t *testing.T) {
    15  	stubConfig(t, testConfig())
    16  	t.Cleanup(gock.Off)
    17  
    18  	gock.New("https://api.github.com").
    19  		Post("/graphql").
    20  		MatchHeader("Authorization", "token abc123").
    21  		BodyString(`{"query":"QUERY","variables":{"var":"test"}}`).
    22  		Reply(200).
    23  		JSON(`{"data":{"viewer":{"login":"hubot"}}}`)
    24  
    25  	client, err := DefaultGraphQLClient()
    26  	assert.NoError(t, err)
    27  
    28  	vars := map[string]interface{}{"var": "test"}
    29  	res := struct{ Viewer struct{ Login string } }{}
    30  	err = client.Do("QUERY", vars, &res)
    31  	assert.NoError(t, err)
    32  	assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
    33  	assert.Equal(t, "hubot", res.Viewer.Login)
    34  }
    35  
    36  func TestGraphQLClientDoError(t *testing.T) {
    37  	stubConfig(t, testConfig())
    38  	t.Cleanup(gock.Off)
    39  
    40  	gock.New("https://api.github.com").
    41  		Post("/graphql").
    42  		MatchHeader("Authorization", "token abc123").
    43  		BodyString(`{"query":"QUERY","variables":null}`).
    44  		Reply(200).
    45  		JSON(`{"errors":[{"type":"NOT_FOUND","path":["organization"],"message":"Could not resolve to an Organization with the login of 'cli'."}]}`)
    46  
    47  	client, err := DefaultGraphQLClient()
    48  	assert.NoError(t, err)
    49  
    50  	res := struct{ Organization struct{ Name string } }{}
    51  	err = client.Do("QUERY", nil, &res)
    52  	var graphQLErr *GraphQLError
    53  	assert.True(t, errors.As(err, &graphQLErr))
    54  	assert.EqualError(t, graphQLErr, "GraphQL: Could not resolve to an Organization with the login of 'cli'. (organization)")
    55  	assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
    56  }
    57  
    58  func TestGraphQLClientQueryError(t *testing.T) {
    59  	stubConfig(t, testConfig())
    60  	t.Cleanup(gock.Off)
    61  
    62  	gock.New("https://api.github.com").
    63  		Post("/graphql").
    64  		MatchHeader("Authorization", "token abc123").
    65  		BodyString(`{"query":"query QUERY{organization{name}}"}`).
    66  		Reply(200).
    67  		JSON(`{"errors":[{"type":"NOT_FOUND","path":["organization"],"message":"Could not resolve to an Organization with the login of 'cli'."}]}`)
    68  
    69  	client, err := DefaultGraphQLClient()
    70  	assert.NoError(t, err)
    71  
    72  	var res struct{ Organization struct{ Name string } }
    73  	err = client.Query("QUERY", &res, nil)
    74  	var graphQLErr *GraphQLError
    75  	assert.True(t, errors.As(err, &graphQLErr))
    76  	assert.EqualError(t, graphQLErr, "GraphQL: Could not resolve to an Organization with the login of 'cli'. (organization)")
    77  	assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
    78  }
    79  
    80  func TestGraphQLClientMutateError(t *testing.T) {
    81  	stubConfig(t, testConfig())
    82  	t.Cleanup(gock.Off)
    83  
    84  	gock.New("https://api.github.com").
    85  		Post("/graphql").
    86  		MatchHeader("Authorization", "token abc123").
    87  		BodyString(`{"query":"mutation MUTATE($input:ID!){updateRepository{repository{name}}}","variables":{"input":"variables"}}`).
    88  		Reply(200).
    89  		JSON(`{"errors":[{"type":"NOT_FOUND","path":["organization"],"message":"Could not resolve to an Organization with the login of 'cli'."}]}`)
    90  
    91  	client, err := DefaultGraphQLClient()
    92  	assert.NoError(t, err)
    93  
    94  	var mutation struct {
    95  		UpdateRepository struct{ Repository struct{ Name string } }
    96  	}
    97  	variables := map[string]interface{}{"input": "variables"}
    98  	err = client.Mutate("MUTATE", &mutation, variables)
    99  	var graphQLErr *GraphQLError
   100  	assert.True(t, errors.As(err, &graphQLErr))
   101  	assert.EqualError(t, graphQLErr, "GraphQL: Could not resolve to an Organization with the login of 'cli'. (organization)")
   102  	assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
   103  }
   104  
   105  func TestGraphQLClientDo(t *testing.T) {
   106  	tests := []struct {
   107  		name       string
   108  		host       string
   109  		httpMocks  func()
   110  		wantErr    bool
   111  		wantErrMsg string
   112  		wantLogin  string
   113  	}{
   114  		{
   115  			name: "success request",
   116  			httpMocks: func() {
   117  				gock.New("https://api.github.com").
   118  					Post("/graphql").
   119  					BodyString(`{"query":"QUERY","variables":{"var":"test"}}`).
   120  					Reply(200).
   121  					JSON(`{"data":{"viewer":{"login":"hubot"}}}`)
   122  			},
   123  			wantLogin: "hubot",
   124  		},
   125  		{
   126  			name: "fail request",
   127  			httpMocks: func() {
   128  				gock.New("https://api.github.com").
   129  					Post("/graphql").
   130  					BodyString(`{"query":"QUERY","variables":{"var":"test"}}`).
   131  					Reply(200).
   132  					JSON(`{"errors":[{"message":"OH NO"},{"message":"this is fine"}]}`)
   133  			},
   134  			wantErr:    true,
   135  			wantErrMsg: "GraphQL: OH NO, this is fine",
   136  		},
   137  		{
   138  			name: "http fail request empty response",
   139  			httpMocks: func() {
   140  				gock.New("https://api.github.com").
   141  					Post("/graphql").
   142  					BodyString(`{"query":"QUERY","variables":{"var":"test"}}`).
   143  					Reply(404).
   144  					JSON(`{}`)
   145  			},
   146  			wantErr:    true,
   147  			wantErrMsg: "HTTP 404 (https://api.github.com/graphql)",
   148  		},
   149  		{
   150  			name: "http fail request message response",
   151  			httpMocks: func() {
   152  				gock.New("https://api.github.com").
   153  					Post("/graphql").
   154  					BodyString(`{"query":"QUERY","variables":{"var":"test"}}`).
   155  					Reply(422).
   156  					JSON(`{"message": "OH NO"}`)
   157  			},
   158  			wantErr:    true,
   159  			wantErrMsg: "HTTP 422: OH NO (https://api.github.com/graphql)",
   160  		},
   161  		{
   162  			name: "http fail request errors response",
   163  			httpMocks: func() {
   164  				gock.New("https://api.github.com").
   165  					Post("/graphql").
   166  					BodyString(`{"query":"QUERY","variables":{"var":"test"}}`).
   167  					Reply(502).
   168  					JSON(`{"errors":[{"message":"Something went wrong"}]}`)
   169  			},
   170  			wantErr:    true,
   171  			wantErrMsg: "HTTP 502: Something went wrong (https://api.github.com/graphql)",
   172  		},
   173  		{
   174  			name: "support enterprise hosts",
   175  			host: "enterprise.com",
   176  			httpMocks: func() {
   177  				gock.New("https://enterprise.com").
   178  					Post("/api/graphql").
   179  					BodyString(`{"query":"QUERY","variables":{"var":"test"}}`).
   180  					Reply(200).
   181  					JSON(`{"data":{"viewer":{"login":"hubot"}}}`)
   182  			},
   183  			wantLogin: "hubot",
   184  		},
   185  	}
   186  
   187  	for _, tt := range tests {
   188  		t.Run(tt.name, func(t *testing.T) {
   189  			t.Cleanup(gock.Off)
   190  			if tt.host == "" {
   191  				tt.host = "github.com"
   192  			}
   193  			if tt.httpMocks != nil {
   194  				tt.httpMocks()
   195  			}
   196  			client, _ := NewGraphQLClient(ClientOptions{
   197  				Host:      tt.host,
   198  				AuthToken: "token",
   199  				Transport: http.DefaultTransport,
   200  			})
   201  			vars := map[string]interface{}{"var": "test"}
   202  			res := struct{ Viewer struct{ Login string } }{}
   203  			err := client.Do("QUERY", vars, &res)
   204  			if tt.wantErr {
   205  				assert.EqualError(t, err, tt.wantErrMsg)
   206  			} else {
   207  				assert.NoError(t, err)
   208  			}
   209  			assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
   210  			assert.Equal(t, tt.wantLogin, res.Viewer.Login)
   211  		})
   212  	}
   213  }
   214  
   215  func TestGraphQLClientDoWithContext(t *testing.T) {
   216  	tests := []struct {
   217  		name       string
   218  		wantErrMsg string
   219  		getCtx     func() context.Context
   220  	}{
   221  		{
   222  			name: "http fail request canceled",
   223  			getCtx: func() context.Context {
   224  				ctx, cancel := context.WithCancel(context.Background())
   225  				// call 'cancel' to ensure that context is already canceled
   226  				cancel()
   227  				return ctx
   228  			},
   229  			wantErrMsg: `Post "https://api.github.com/graphql": context canceled`,
   230  		},
   231  		{
   232  			name: "http fail request timed out",
   233  			getCtx: func() context.Context {
   234  				// pass current time to ensure that deadline has already passed
   235  				ctx, cancel := context.WithDeadline(context.Background(), time.Now())
   236  				cancel()
   237  				return ctx
   238  			},
   239  			wantErrMsg: `Post "https://api.github.com/graphql": context deadline exceeded`,
   240  		},
   241  	}
   242  
   243  	for _, tt := range tests {
   244  		t.Run(tt.name, func(t *testing.T) {
   245  			t.Cleanup(gock.Off)
   246  			gock.New("https://api.github.com").
   247  				Post("/graphql").
   248  				BodyString(`{"query":"QUERY","variables":{"var":"test"}}`).
   249  				Reply(200).
   250  				JSON(`{}`)
   251  
   252  			client, _ := NewGraphQLClient(ClientOptions{
   253  				Host:      "github.com",
   254  				AuthToken: "token",
   255  				Transport: http.DefaultTransport,
   256  			})
   257  
   258  			vars := map[string]interface{}{"var": "test"}
   259  			res := struct{ Viewer struct{ Login string } }{}
   260  
   261  			ctx := tt.getCtx()
   262  			gotErr := client.DoWithContext(ctx, "QUERY", vars, &res)
   263  
   264  			assert.True(t, gock.IsDone(), printPendingMocks(gock.Pending()))
   265  			assert.EqualError(t, gotErr, tt.wantErrMsg)
   266  		})
   267  	}
   268  }
   269  
   270  func TestGraphQLEndpoint(t *testing.T) {
   271  	tests := []struct {
   272  		name         string
   273  		host         string
   274  		wantEndpoint string
   275  	}{
   276  		{
   277  			name:         "github",
   278  			host:         "github.com",
   279  			wantEndpoint: "https://api.github.com/graphql",
   280  		},
   281  		{
   282  			name:         "localhost",
   283  			host:         "github.localhost",
   284  			wantEndpoint: "http://api.github.localhost/graphql",
   285  		},
   286  		{
   287  			name:         "garage",
   288  			host:         "garage.github.com",
   289  			wantEndpoint: "https://garage.github.com/api/graphql",
   290  		},
   291  		{
   292  			name:         "enterprise",
   293  			host:         "enterprise.com",
   294  			wantEndpoint: "https://enterprise.com/api/graphql",
   295  		},
   296  		{
   297  			name:         "tenant",
   298  			host:         "tenant.ghe.com",
   299  			wantEndpoint: "https://api.tenant.ghe.com/graphql",
   300  		},
   301  	}
   302  
   303  	for _, tt := range tests {
   304  		t.Run(tt.name, func(t *testing.T) {
   305  			endpoint := graphQLEndpoint(tt.host)
   306  			assert.Equal(t, tt.wantEndpoint, endpoint)
   307  		})
   308  	}
   309  }
   310  

View as plain text