GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 57c3d8...a6b7ad )
by
unknown
01:54 queued 16s
created

mollie.TestClient_newResponse   A

Complexity

Conditions 4

Size

Total Lines 33
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 25
nop 1
dl 0
loc 33
rs 9.28
c 0
b 0
f 0
1
package mollie
2
3
import (
4
	"context"
5
	"fmt"
6
	"io"
7
	"net/http"
8
	"net/http/httptest"
9
	"net/url"
10
	"os"
11
	"strings"
12
	"testing"
13
	"time"
14
15
	"github.com/VictorAvelar/mollie-api-go/v4/pkg/idempotency"
16
	"github.com/VictorAvelar/mollie-api-go/v4/testdata"
17
	"github.com/stretchr/testify/assert"
18
	"github.com/stretchr/testify/require"
19
)
20
21
func TestNewClient(t *testing.T) {
22
	c := http.DefaultClient
23
	{
24
		c.Timeout = 25 * time.Second
25
	}
26
27
	tests := []struct {
28
		name   string
29
		client *http.Client
30
	}{
31
		{
32
			"nil returns a valid client",
33
			nil,
34
		},
35
		{
36
			"a passed client is decorated",
37
			c,
38
		},
39
	}
40
41
	conf := NewConfig(true, APITokenEnv)
42
43
	for _, tt := range tests {
44
		t.Run(tt.name, func(t *testing.T) {
45
			_, err := NewClient(tt.client, conf)
46
			assert.Nil(t, err)
47
		})
48
	}
49
}
50
51
func TestNewClientWithEnvVars(t *testing.T) {
52
	setEnv()
53
	setup()
54
	defer unsetEnv()
55
	defer teardown()
56
57
	c := http.DefaultClient
58
	{
59
		c.Timeout = 25 * time.Second
60
	}
61
62
	tests := []struct {
63
		name   string
64
		client *http.Client
65
	}{
66
		{
67
			"nil returns a valid client",
68
			nil,
69
		},
70
		{
71
			"a passed client is decorated",
72
			c,
73
		},
74
	}
75
76
	for _, tt := range tests {
77
		t.Run(tt.name, func(t *testing.T) {
78
			got, err := NewClient(tt.client, tConf)
79
			require.Nil(t, err)
80
			assert.NotEmpty(t, got.authentication)
81
		})
82
	}
83
}
84
85
func TestClient_NewAPIRequest(t *testing.T) {
86
	type args struct {
87
		ctx     context.Context
88
		method  string
89
		uri     string
90
		body    interface{}
91
		options interface{}
92
	}
93
94
	type testCtxKey string
95
96
	cases := []struct {
97
		name    string
98
		args    args
99
		outBody string
100
		outURI  string
101
		wantCtx bool
102
	}{
103
		{
104
			"request with empty context works as expected",
105
			args{
106
				ctx:     nil,
107
				method:  http.MethodGet,
108
				uri:     "test",
109
				body:    []string{"hello", "world"},
110
				options: nil,
111
			},
112
			`["hello","world"]` + "\n",
113
			"/test",
114
			false,
115
		},
116
		{
117
			"request with context works and the same as without context",
118
			args{
119
				ctx:     context.WithValue(context.Background(), testCtxKey("test-key"), "I will make it to the other side"),
120
				method:  http.MethodGet,
121
				uri:     "test",
122
				body:    "some simple string",
123
				options: nil,
124
			},
125
			"\"some simple string\"\n",
126
			"/test",
127
			true,
128
		},
129
	}
130
131
	for _, c := range cases {
132
		t.Run(c.name, func(t *testing.T) {
133
			setEnv()
134
			setup()
135
			defer teardown()
136
			defer unsetEnv()
137
138
			req, _ := tClient.NewAPIRequest(c.args.ctx, c.args.method, c.args.uri, c.args.body)
139
140
			testHeader(t, req, "Accept", RequestContentType)
141
			testHeader(t, req, AuthHeader, "Bearer token_X12b31ggg23")
142
143
			assert.Equal(t, tServer.URL+c.outURI, req.URL.String())
144
			body, _ := io.ReadAll(req.Body)
145
			assert.Equal(t, c.outBody, string(body))
146
		})
147
	}
148
}
149
150
func TestClient_NewApiRequest_IdempotencyKeys(t *testing.T) {
151
	type args struct {
152
		method string
153
	}
154
155
	tests := []struct {
156
		name   string
157
		args   args
158
		expect string
159
		dummy  bool
160
	}{
161
		{
162
			"using the std key generator",
163
			args{http.MethodPost},
164
			"",
165
			false,
166
		},
167
		{
168
			"using a nop key generator with the default text",
169
			args{http.MethodPost},
170
			"",
171
			true,
172
		},
173
		{
174
			"using a nop key generator with custom text",
175
			args{http.MethodPost},
176
			"testing_mollie_idem_key",
177
			true,
178
		},
179
		{
180
			"using the std key generator with a non supported method (get)",
181
			args{http.MethodGet},
182
			"",
183
			false,
184
		},
185
		{
186
			"using the std key generator with a non supported method (put)",
187
			args{http.MethodPut},
188
			"",
189
			false,
190
		},
191
		{
192
			"using the std key generator with a non supported method (delete)",
193
			args{http.MethodDelete},
194
			"",
195
			false,
196
		},
197
	}
198
199
	for _, tt := range tests {
200
		t.Run(tt.name, func(t *testing.T) {
201
			setEnv()
202
			setup()
203
			defer teardown()
204
			defer unsetEnv()
205
206
			if tt.dummy {
207
				g := idempotency.NewNopGenerator(tt.expect)
208
				if tt.expect == "" {
209
					tt.expect = idempotency.TestKeyExpected
210
				}
211
				tClient.SetIdempotencyKeyGenerator(g)
212
			}
213
214
			req, err := tClient.NewAPIRequest(context.Background(), tt.args.method, "/", nil)
215
216
			assert.Nil(t, err)
217
218
			if tt.args.method != http.MethodPost {
219
				assert.Empty(t, req.Header.Get(IdempotencyKeyHeader))
220
			} else {
221
				assert.NotEmpty(t, req.Header.Get(IdempotencyKeyHeader))
222
				if tt.dummy {
223
					testHeader(t, req, IdempotencyKeyHeader, tt.expect)
224
				} else {
225
					assert.Len(t, req.Header.Get(IdempotencyKeyHeader), 36)
226
					assert.Regexp(t, `(?m)^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$`, req.Header.Get(IdempotencyKeyHeader))
227
				}
228
			}
229
		})
230
	}
231
}
232
233
func TestClient_NewAPIRequest_ForceErrors(t *testing.T) {
234
	type args struct {
235
		ctx    context.Context
236
		method string
237
		uri    string
238
		body   interface{}
239
	}
240
241
	noPre := func() error {
242
		return nil
243
	}
244
245
	cases := []struct {
246
		name string
247
		args args
248
		err  error
249
		pre  func() error
250
	}{
251
		{
252
			"err uri without trailing slash",
253
			args{
254
				context.Background(),
255
				http.MethodGet,
256
				"test",
257
				nil,
258
			},
259
			errBadBaseURL,
260
			func() error {
261
				uri, err := url.Parse("http://localhost")
262
				if err != nil {
263
					return err
264
				}
265
				tClient = &Client{
266
					BaseURL: uri,
267
				}
268
				return nil
269
			},
270
		},
271
		{
272
			"err parsing request uri",
273
			args{
274
				context.Background(),
275
				"\\\\\\",
276
				"test",
277
				nil,
278
			},
279
			fmt.Errorf("new_request: net/http: invalid method \"\\\\\\\\\\\\\""),
280
			noPre,
281
		},
282
		{
283
			"err serializing request body",
284
			args{
285
				context.Background(),
286
				http.MethodPost,
287
				"test",
288
				make(chan int),
289
			},
290
			fmt.Errorf("encoding_error: json: unsupported type: chan int"),
291
			noPre,
292
		},
293
		{
294
			"err when parsing requested uri",
295
			args{
296
				context.Background(),
297
				http.MethodPut,
298
				":",
299
				nil,
300
			},
301
			fmt.Errorf("url_parsing_error: parse \":\": missing protocol scheme"),
302
			noPre,
303
		},
304
	}
305
306
	for _, c := range cases {
307
		setup()
308
		defer teardown()
309
		t.Run(c.name, func(t *testing.T) {
310
			err := c.pre()
311
			require.Nil(t, err)
312
			_, err = tClient.NewAPIRequest(c.args.ctx, c.args.method, c.args.uri, c.args.body)
313
			assert.NotNil(t, err)
314
			assert.EqualError(t, err, c.err.Error())
315
		})
316
	}
317
}
318
319
func TestClient_IsAccessToken(t *testing.T) {
320
	cases := []struct {
321
		name  string
322
		value string
323
		want  bool
324
	}{
325
		{
326
			"Test dummy_text",
327
			"dummy_text",
328
			false,
329
		},
330
		{
331
			"Test pattern is matched",
332
			"access_testing_token_here",
333
			true,
334
		},
335
		{
336
			"Test patter is not matched if not in the right position",
337
			"testing_token_access_here",
338
			false,
339
		},
340
		{
341
			"Test other tokens are not matched",
342
			"test_your_token_here",
343
			false,
344
		},
345
		{
346
			"Test empty is not a matched",
347
			"",
348
			false,
349
		},
350
	}
351
352
	for _, c := range cases {
353
		t.Run(c.name, func(t *testing.T) {
354
			client := &Client{
355
				authentication: c.value,
356
			}
357
358
			got := client.HasAccessToken()
359
			assert.Equal(t, c.want, got)
360
		})
361
	}
362
}
363
364
func TestClient_NewAPIRequest_OrgTokenOverApiKey(t *testing.T) {
365
	setup()
366
	defer teardown()
367
	_ = tClient.WithAuthenticationValue("org_token")
368
	req, _ := tClient.NewAPIRequest(context.TODO(), "GET", "test", nil)
369
370
	testHeader(t, req, AuthHeader, "Bearer org_token")
371
}
372
373
func TestClient_WithAuthenticationValue_Error(t *testing.T) {
374
	setup()
375
	defer teardown()
376
	err := tClient.WithAuthenticationValue("")
377
378
	if err == nil {
379
		t.Errorf("unexpected error, want %v and got %v", errEmptyAuthKey, err)
380
	}
381
}
382
383
func TestClient_Do(t *testing.T) {
384
	type args struct {
385
		ctx    context.Context
386
		method string
387
		uri    string
388
		body   interface{}
389
	}
390
391
	cases := []struct {
392
		name    string
393
		args    args
394
		wantErr bool
395
		err     error
396
		want    int
397
		handler http.HandlerFunc
398
		pre     func(r *http.Request)
399
	}{
400
		{
401
			"execute successful request",
402
			args{
403
				context.Background(),
404
				http.MethodGet,
405
				"/test",
406
				nil,
407
			},
408
			false,
409
			nil,
410
			http.StatusOK,
411
			func(w http.ResponseWriter, r *http.Request) {
412
				testMethod(t, r, "GET")
413
				testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
414
				w.WriteHeader(http.StatusOK)
415
			},
416
			func(r *http.Request) {},
417
		},
418
		{
419
			"error when request url is invalid (nil)",
420
			args{
421
				context.Background(),
422
				http.MethodGet,
423
				"/test",
424
				nil,
425
			},
426
			true,
427
			fmt.Errorf("http_error: Get \"\": http: nil Request.URL"),
428
			http.StatusOK,
429
			func(w http.ResponseWriter, r *http.Request) {
430
				testMethod(t, r, "GET")
431
				testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
432
				w.Write([]byte("nothing"))
433
			},
434
			func(r *http.Request) {
435
				r.URL = nil
436
			},
437
		},
438
		{
439
			"error when request response is non successful",
440
			args{
441
				context.Background(),
442
				http.MethodGet,
443
				"/test",
444
				nil,
445
			},
446
			true,
447
			fmt.Errorf("500 500 Internal Server Error: "),
448
			http.StatusOK,
449
			func(w http.ResponseWriter, r *http.Request) {
450
				testMethod(t, r, "GET")
451
				testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
452
				w.WriteHeader(http.StatusInternalServerError)
453
			},
454
			func(r *http.Request) {},
455
		},
456
		{
457
			"execute returns an error during the response creation request",
458
			args{
459
				context.Background(),
460
				http.MethodGet,
461
				"/test",
462
				nil,
463
			},
464
			true,
465
			fmt.Errorf("unexpected EOF"),
466
			http.StatusOK,
467
			func(w http.ResponseWriter, r *http.Request) {
468
				testMethod(t, r, "GET")
469
				testHeader(t, r, AuthHeader, "Bearer token_X12b31ggg23")
470
				w.Header().Set("Content-Length", "1")
471
			},
472
			func(r *http.Request) {},
473
		},
474
	}
475
476
	for _, c := range cases {
477
		setEnv()
478
		setup()
479
		defer teardown()
480
		defer unsetEnv()
481
		t.Run(c.name, func(t *testing.T) {
482
			tMux.HandleFunc(c.args.uri, c.handler)
483
			req, _ := tClient.NewAPIRequest(c.args.ctx, c.args.method, c.args.uri, c.args.body)
484
			c.pre(req)
485
			res, err := tClient.Do(req)
486
			if c.wantErr {
487
				assert.EqualError(t, err, c.err.Error())
488
			} else {
489
				assert.Nil(t, err)
490
				assert.Equal(t, c.want, res.StatusCode)
491
			}
492
		})
493
	}
494
}
495
496
func TestCheckResponse(t *testing.T) {
497
	res1 := &http.Response{
498
		StatusCode: http.StatusNotFound,
499
		Status:     http.StatusText(http.StatusNotFound),
500
		Body:       io.NopCloser(strings.NewReader("not found ok")),
501
	}
502
503
	res3 := &http.Response{
504
		StatusCode: http.StatusNotFound,
505
		Status:     http.StatusText(http.StatusNotFound),
506
		Body:       io.NopCloser(strings.NewReader("")),
507
	}
508
509
	res2 := &http.Response{
510
		StatusCode: http.StatusOK,
511
		Status:     http.StatusText(http.StatusOK),
512
		Body:       io.NopCloser(strings.NewReader("success ok")),
513
	}
514
515
	tests := []struct {
516
		name string
517
		code string
518
		arg  *Response
519
	}{
520
		{
521
			"successful response",
522
			"",
523
			&Response{Response: res2},
524
		},
525
		{
526
			"not found response",
527
			"Not Found",
528
			&Response{Response: res1},
529
		},
530
		{
531
			"success with empty body",
532
			"",
533
			&Response{Response: res3},
534
		},
535
	}
536
537
	for _, tt := range tests {
538
		t.Run(tt.name, func(t *testing.T) {
539
			if err := CheckResponse(tt.arg); err != nil {
540
				if !strings.Contains(err.Error(), tt.code) {
541
					t.Error(err)
542
				}
543
			}
544
		})
545
	}
546
}
547
548
func TestClient_newResponse(t *testing.T) {
549
	type args struct {
550
		res *http.Response
551
	}
552
553
	cases := []struct {
554
		name    string
555
		args    args
556
		want    *Response
557
		wantErr bool
558
		err     error
559
	}{
560
		{
561
			"new response fails on read operation",
562
			args{
563
				&http.Response{
564
					Body: closedReader(),
565
				},
566
			},
567
			&Response{},
568
			true,
569
			fmt.Errorf("unexpected EOF"),
570
		},
571
	}
572
573
	for _, c := range cases {
574
		t.Run(c.name, func(t *testing.T) {
575
			got, err := newResponse(c.args.res)
576
			if c.wantErr {
577
				assert.EqualError(t, err, c.err.Error())
578
			} else {
579
				assert.Nil(t, err)
580
				assert.Equal(t, c.want, got)
581
			}
582
		})
583
	}
584
}
585
586
func TestClient_newError(t *testing.T) {
587
	type args struct {
588
		res *Response
589
	}
590
591
	cases := []struct {
592
		name    string
593
		args    args
594
		want    *BaseError
595
		wantErr bool
596
		err     error
597
	}{
598
		{
599
			"new error fails on read operation",
600
			args{
601
				&Response{Response: &http.Response{Body: closedReader(), ContentLength: 1}},
602
			},
603
			&BaseError{},
604
			true,
605
			fmt.Errorf("unexpected end of JSON input"),
606
		},
607
		{
608
			"new error is constructed based on response",
609
			args{
610
				&Response{Response: &http.Response{Body: closedReader()}},
611
			},
612
			&BaseError{},
613
			false,
614
			nil,
615
		},
616
	}
617
618
	for _, c := range cases {
619
		t.Run(c.name, func(t *testing.T) {
620
			err := newError(c.args.res)
621
			if c.wantErr {
622
				assert.EqualError(t, err, c.err.Error())
623
			} else {
624
				assert.IsType(t, &BaseError{}, err)
625
				assert.EqualValues(t, c.want, err)
626
			}
627
		})
628
	}
629
}
630
631
// <----- Testing helpers ----->
632
633
var (
634
	tMux    *http.ServeMux
635
	tServer *httptest.Server
636
	tClient *Client
637
	tConf   *Config
638
)
639
640
var (
641
	noPre    = func() {}
642
	crashSrv = func() {
643
		u, _ := url.Parse(tServer.URL)
644
		tClient.BaseURL = u
645
	}
646
	setAccessToken = func() {
647
		tClient.WithAuthenticationValue("access_token_test")
648
	}
649
)
650
651
// the parameter indicates if you want to prepare your tests against the US sandbox
652
// just to be used when doing integration testing.
653
func setup() {
654
	tMux = http.NewServeMux()
655
	tServer = httptest.NewServer(tMux)
656
	tConf = NewAPITestingConfig(true)
657
	tClient, _ = NewClient(nil, tConf)
658
	u, _ := url.Parse(tServer.URL + "/")
659
	tClient.BaseURL = u
660
}
661
662
func teardown() {
663
	tServer.Close()
664
}
665
666
func setEnv() {
667
	_ = os.Setenv(APITokenEnv, "token_X12b31ggg23")
668
	_ = os.Setenv(OrgTokenEnv, "access_ey1923n23123n1k3b123jv12g312h31v32g13")
669
}
670
671
func unsetEnv() {
672
	_ = os.Unsetenv(APITokenEnv)
673
	_ = os.Unsetenv(OrgTokenEnv)
674
}
675
676
func testMethod(t *testing.T, r *http.Request, want string) {
677
	if got := r.Method; got != want {
678
		t.Errorf("Request method: %v, want %v", got, want)
679
	}
680
}
681
682
func testHeader(t *testing.T, r *http.Request, header string, want string) {
683
	if got := r.Header.Get(header); got != want {
684
		t.Errorf("Header.Get(%q) returned %q, want %q", header, got, want)
685
	}
686
}
687
688
func testQuery(t *testing.T, r *http.Request, want string) {
689
	if r.URL.Query().Encode() != want {
690
		t.Errorf("Query().Encode() returned unexpected values, want: %q, got %q", want, r.URL.Query().Encode())
691
	}
692
}
693
694
func errorHandler(w http.ResponseWriter, r *http.Request) {
695
	w.WriteHeader(http.StatusInternalServerError)
696
	w.Write([]byte(testdata.InternalServerErrorResponse))
697
}
698
699
func encodingHandler(w http.ResponseWriter, r *http.Request) {
700
	w.WriteHeader(http.StatusOK)
701
	_, _ = w.Write([]byte(`{hello: [{},]}`))
702
}
703
704
type readCloserErr struct{}
705
706
func (r *readCloserErr) Read(p []byte) (n int, err error) {
707
	return 0, io.ErrUnexpectedEOF
708
}
709
710
func (r *readCloserErr) Close() error {
711
	return nil
712
}
713
714
func closedReader() io.ReadCloser {
715
	return &readCloserErr{}
716
}
717
718
// <----- .Testing helpers ----->
719