1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package client
16
17 import (
18 "bytes"
19 "context"
20 "encoding/json"
21 "encoding/xml"
22 "errors"
23 "fmt"
24 "io"
25 "net/http"
26 "net/http/cookiejar"
27 "net/http/httptest"
28 "net/url"
29 "testing"
30 "time"
31
32 "github.com/go-openapi/runtime"
33 "github.com/go-openapi/strfmt"
34 "github.com/stretchr/testify/assert"
35 "github.com/stretchr/testify/require"
36 )
37
38 const (
39 token = "the-super-secret-token"
40 bearerToken = "Bearer " + token
41 charsetUTF8 = ";charset=utf-8"
42 )
43
44
45 type task struct {
46
47 Completed bool `json:"completed" xml:"completed"`
48
49
50 Content string `json:"content" xml:"content"`
51
52
53 ID int64 `json:"id" xml:"id"`
54 }
55
56 type testCtxKey uint8
57
58 const rtKey testCtxKey = 1
59
60 func TestRuntime_Concurrent(t *testing.T) {
61
62
63
64 result := []task{
65 {false, "task 1 content", 1},
66 {false, "task 2 content", 2},
67 }
68 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
69 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime)
70 rw.WriteHeader(http.StatusOK)
71 jsongen := json.NewEncoder(rw)
72 require.NoError(t, jsongen.Encode(result))
73 }))
74 defer server.Close()
75
76 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
77 return nil
78 })
79
80 hu, err := url.Parse(server.URL)
81 require.NoError(t, err)
82
83 rt := New(hu.Host, "/", []string{schemeHTTP})
84 resCC := make(chan interface{})
85 errCC := make(chan error)
86 var res interface{}
87
88 for j := 0; j < 6; j++ {
89 go func() {
90 resC := make(chan interface{})
91 errC := make(chan error)
92
93 go func() {
94 var resp interface{}
95 var errp error
96 for i := 0; i < 3; i++ {
97 resp, errp = rt.Submit(&runtime.ClientOperation{
98 ID: "getTasks",
99 Method: http.MethodGet,
100 PathPattern: "/",
101 Params: rwrtr,
102 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
103 if response.Code() == http.StatusOK {
104 var res []task
105 if e := consumer.Consume(response.Body(), &res); e != nil {
106 return nil, e
107 }
108 return res, nil
109 }
110 return nil, errors.New("generic error")
111 }),
112 })
113 <-time.After(100 * time.Millisecond)
114 }
115 resC <- resp
116 errC <- errp
117 }()
118 resCC <- <-resC
119 errCC <- <-errC
120 }()
121 }
122
123 c := 6
124 for c > 0 {
125 res = <-resCC
126 err = <-errCC
127 c--
128 }
129
130 require.NoError(t, err)
131 assert.IsType(t, []task{}, res)
132 actual := res.([]task)
133 assert.EqualValues(t, result, actual)
134 }
135
136 func TestRuntime_Canary(t *testing.T) {
137
138
139
140 result := []task{
141 {false, "task 1 content", 1},
142 {false, "task 2 content", 2},
143 }
144 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
145 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime)
146 rw.WriteHeader(http.StatusOK)
147 jsongen := json.NewEncoder(rw)
148 require.NoError(t, jsongen.Encode(result))
149 }))
150 defer server.Close()
151
152 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
153 return nil
154 })
155
156 hu, err := url.Parse(server.URL)
157 require.NoError(t, err)
158 rt := New(hu.Host, "/", []string{schemeHTTP})
159 res, err := rt.Submit(&runtime.ClientOperation{
160 ID: "getTasks",
161 Method: http.MethodGet,
162 PathPattern: "/",
163 Params: rwrtr,
164 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
165 if response.Code() == http.StatusOK {
166 var res []task
167 if e := consumer.Consume(response.Body(), &res); e != nil {
168 return nil, e
169 }
170 return res, nil
171 }
172 return nil, errors.New("generic error")
173 }),
174 })
175
176 require.NoError(t, err)
177 assert.IsType(t, []task{}, res)
178 actual := res.([]task)
179 assert.EqualValues(t, result, actual)
180 }
181
182 type tasks struct {
183 Tasks []task `xml:"task"`
184 }
185
186 func TestRuntime_XMLCanary(t *testing.T) {
187
188
189 result := tasks{
190 Tasks: []task{
191 {false, "task 1 content", 1},
192 {false, "task 2 content", 2},
193 },
194 }
195 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
196 rw.Header().Add(runtime.HeaderContentType, runtime.XMLMime)
197 rw.WriteHeader(http.StatusOK)
198 xmlgen := xml.NewEncoder(rw)
199 require.NoError(t, xmlgen.Encode(result))
200 }))
201 defer server.Close()
202
203 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
204 return nil
205 })
206
207 hu, err := url.Parse(server.URL)
208 require.NoError(t, err)
209
210 rt := New(hu.Host, "/", []string{schemeHTTP})
211 res, err := rt.Submit(&runtime.ClientOperation{
212 ID: "getTasks",
213 Method: http.MethodGet,
214 PathPattern: "/",
215 Params: rwrtr,
216 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
217 if response.Code() == http.StatusOK {
218 var res tasks
219 if e := consumer.Consume(response.Body(), &res); e != nil {
220 return nil, e
221 }
222 return res, nil
223 }
224 return nil, errors.New("generic error")
225 }),
226 })
227
228 require.NoError(t, err)
229 assert.IsType(t, tasks{}, res)
230 actual := res.(tasks)
231 assert.EqualValues(t, result, actual)
232 }
233
234 func TestRuntime_TextCanary(t *testing.T) {
235
236
237 result := "1: task 1 content; 2: task 2 content"
238 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
239 rw.Header().Add(runtime.HeaderContentType, runtime.TextMime)
240 rw.WriteHeader(http.StatusOK)
241 _, _ = rw.Write([]byte(result))
242 }))
243 defer server.Close()
244
245 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
246 return nil
247 })
248
249 hu, err := url.Parse(server.URL)
250 require.NoError(t, err)
251
252 rt := New(hu.Host, "/", []string{schemeHTTP})
253 res, err := rt.Submit(&runtime.ClientOperation{
254 ID: "getTasks",
255 Method: http.MethodGet,
256 PathPattern: "/",
257 Params: rwrtr,
258 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
259 if response.Code() == http.StatusOK {
260 var res string
261 if e := consumer.Consume(response.Body(), &res); e != nil {
262 return nil, e
263 }
264 return res, nil
265 }
266 return nil, errors.New("generic error")
267 }),
268 })
269
270 require.NoError(t, err)
271 assert.IsType(t, "", res)
272 actual := res.(string)
273 assert.EqualValues(t, result, actual)
274 }
275
276 func TestRuntime_CSVCanary(t *testing.T) {
277
278
279 result := `task,content,result
280 1,task1,ok
281 2,task2,fail
282 `
283 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
284 rw.Header().Add(runtime.HeaderContentType, runtime.CSVMime)
285 rw.WriteHeader(http.StatusOK)
286 _, _ = rw.Write([]byte(result))
287 }))
288 defer server.Close()
289
290 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
291 return nil
292 })
293
294 hu, err := url.Parse(server.URL)
295 require.NoError(t, err)
296
297 rt := New(hu.Host, "/", []string{schemeHTTP})
298 res, err := rt.Submit(&runtime.ClientOperation{
299 ID: "getTasks",
300 Method: http.MethodGet,
301 PathPattern: "/",
302 Params: rwrtr,
303 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
304 if response.Code() == http.StatusOK {
305 var res bytes.Buffer
306 if e := consumer.Consume(response.Body(), &res); e != nil {
307 return nil, e
308 }
309 return res, nil
310 }
311 return nil, errors.New("generic error")
312 }),
313 })
314
315 require.NoError(t, err)
316 assert.IsType(t, bytes.Buffer{}, res)
317 actual := res.(bytes.Buffer)
318 assert.EqualValues(t, result, actual.String())
319 }
320
321 type roundTripperFunc func(*http.Request) (*http.Response, error)
322
323 func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
324 return fn(req)
325 }
326
327 func TestRuntime_CustomTransport(t *testing.T) {
328 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
329 return nil
330 })
331 result := []task{
332 {false, "task 1 content", 1},
333 {false, "task 2 content", 2},
334 }
335
336 rt := New("localhost:3245", "/", []string{"ws", "wss", schemeHTTPS})
337 rt.Transport = roundTripperFunc(func(req *http.Request) (*http.Response, error) {
338 if req.URL.Scheme != schemeHTTPS {
339 return nil, errors.New("this was not a https request")
340 }
341 assert.Equal(t, "localhost:3245", req.Host)
342 assert.Equal(t, "localhost:3245", req.URL.Host)
343
344 var resp http.Response
345 resp.StatusCode = http.StatusOK
346 resp.Header = make(http.Header)
347 resp.Header.Set("content-type", "application/json")
348 buf := bytes.NewBuffer(nil)
349 enc := json.NewEncoder(buf)
350 require.NoError(t, enc.Encode(result))
351 resp.Body = io.NopCloser(buf)
352 return &resp, nil
353 })
354
355 res, err := rt.Submit(&runtime.ClientOperation{
356 ID: "getTasks",
357 Method: http.MethodGet,
358 PathPattern: "/",
359 Schemes: []string{"ws", "wss", schemeHTTPS},
360 Params: rwrtr,
361 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
362 if response.Code() == http.StatusOK {
363 var res []task
364 if e := consumer.Consume(response.Body(), &res); e != nil {
365 return nil, e
366 }
367 return res, nil
368 }
369 return nil, errors.New("generic error")
370 }),
371 })
372
373 require.NoError(t, err)
374 assert.IsType(t, []task{}, res)
375 actual := res.([]task)
376 assert.EqualValues(t, result, actual)
377 }
378
379 func TestRuntime_CustomCookieJar(t *testing.T) {
380 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
381 authenticated := false
382 for _, cookie := range req.Cookies() {
383 if cookie.Name == "sessionid" && cookie.Value == "abc" {
384 authenticated = true
385 }
386 }
387 if !authenticated {
388 username, password, ok := req.BasicAuth()
389 if ok && username == "username" && password == "password" {
390 authenticated = true
391 http.SetCookie(rw, &http.Cookie{Name: "sessionid", Value: "abc"})
392 }
393 }
394 if authenticated {
395 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime)
396 rw.WriteHeader(http.StatusOK)
397 jsongen := json.NewEncoder(rw)
398 require.NoError(t, jsongen.Encode([]task{}))
399 } else {
400 rw.WriteHeader(http.StatusUnauthorized)
401 }
402 }))
403 defer server.Close()
404
405 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
406 return nil
407 })
408
409 hu, err := url.Parse(server.URL)
410 require.NoError(t, err)
411
412 rt := New(hu.Host, "/", []string{schemeHTTP})
413 rt.Jar, err = cookiejar.New(nil)
414 require.NoError(t, err)
415
416 submit := func(authInfo runtime.ClientAuthInfoWriter) {
417 _, err := rt.Submit(&runtime.ClientOperation{
418 ID: "getTasks",
419 Method: http.MethodGet,
420 PathPattern: "/",
421 Params: rwrtr,
422 AuthInfo: authInfo,
423 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, _ runtime.Consumer) (interface{}, error) {
424 if response.Code() == http.StatusOK {
425 return map[string]interface{}{}, nil
426 }
427 return nil, errors.New("generic error")
428 }),
429 })
430
431 require.NoError(t, err)
432 }
433
434 submit(BasicAuth("username", "password"))
435 submit(nil)
436 }
437
438 func TestRuntime_AuthCanary(t *testing.T) {
439
440
441
442
443 result := []task{
444 {false, "task 1 content", 1},
445 {false, "task 2 content", 2},
446 }
447 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
448 if req.Header.Get(runtime.HeaderAuthorization) != bearerToken {
449 rw.WriteHeader(http.StatusUnauthorized)
450 return
451 }
452
453 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime)
454 rw.WriteHeader(http.StatusOK)
455 jsongen := json.NewEncoder(rw)
456 require.NoError(t, jsongen.Encode(result))
457 }))
458 defer server.Close()
459
460 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
461 return nil
462 })
463
464 hu, err := url.Parse(server.URL)
465 require.NoError(t, err)
466
467 rt := New(hu.Host, "/", []string{schemeHTTP})
468 res, err := rt.Submit(&runtime.ClientOperation{
469 ID: "getTasks",
470 Params: rwrtr,
471 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
472 if response.Code() == http.StatusOK {
473 var res []task
474 if e := consumer.Consume(response.Body(), &res); e != nil {
475 return nil, e
476 }
477 return res, nil
478 }
479 return nil, errors.New("generic error")
480 }),
481 AuthInfo: BearerToken(token),
482 })
483
484 require.NoError(t, err)
485 assert.IsType(t, []task{}, res)
486 actual := res.([]task)
487 assert.EqualValues(t, result, actual)
488 }
489
490 func TestRuntime_PickConsumer(t *testing.T) {
491 result := []task{
492 {false, "task 1 content", 1},
493 {false, "task 2 content", 2},
494 }
495 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
496 if req.Header.Get("Content-Type") != "application/octet-stream" {
497 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+charsetUTF8)
498 rw.WriteHeader(http.StatusBadRequest)
499 return
500 }
501 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+charsetUTF8)
502 rw.WriteHeader(http.StatusOK)
503 jsongen := json.NewEncoder(rw)
504 _ = jsongen.Encode(result)
505 }))
506 defer server.Close()
507
508 rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error {
509 return req.SetBodyParam(bytes.NewBufferString("hello"))
510 })
511
512 hu, err := url.Parse(server.URL)
513 require.NoError(t, err)
514
515 rt := New(hu.Host, "/", []string{schemeHTTP})
516 res, err := rt.Submit(&runtime.ClientOperation{
517 ID: "getTasks",
518 Method: "POST",
519 PathPattern: "/",
520 Schemes: []string{schemeHTTP},
521 ConsumesMediaTypes: []string{"application/octet-stream"},
522 Params: rwrtr,
523 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
524 if response.Code() == http.StatusOK {
525 var res []task
526 if e := consumer.Consume(response.Body(), &res); e != nil {
527 return nil, e
528 }
529 return res, nil
530 }
531 return nil, errors.New("generic error")
532 }),
533 AuthInfo: BearerToken(token),
534 })
535
536 require.NoError(t, err)
537 assert.IsType(t, []task{}, res)
538 actual := res.([]task)
539 assert.EqualValues(t, result, actual)
540 }
541
542 func TestRuntime_ContentTypeCanary(t *testing.T) {
543
544
545
546 result := []task{
547 {false, "task 1 content", 1},
548 {false, "task 2 content", 2},
549 }
550 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
551 if req.Header.Get(runtime.HeaderAuthorization) != bearerToken {
552 rw.WriteHeader(http.StatusBadRequest)
553 return
554 }
555 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+charsetUTF8)
556 rw.WriteHeader(http.StatusOK)
557 jsongen := json.NewEncoder(rw)
558 _ = jsongen.Encode(result)
559 }))
560 defer server.Close()
561
562 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
563 return nil
564 })
565
566 hu, err := url.Parse(server.URL)
567 require.NoError(t, err)
568
569 rt := New(hu.Host, "/", []string{schemeHTTP})
570 res, err := rt.Submit(&runtime.ClientOperation{
571 ID: "getTasks",
572 Method: http.MethodGet,
573 PathPattern: "/",
574 Schemes: []string{schemeHTTP},
575 Params: rwrtr,
576 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
577 if response.Code() == http.StatusOK {
578 var res []task
579 if e := consumer.Consume(response.Body(), &res); e != nil {
580 return nil, e
581 }
582 return res, nil
583 }
584 return nil, errors.New("generic error")
585 }),
586 AuthInfo: BearerToken(token),
587 })
588
589 require.NoError(t, err)
590 assert.IsType(t, []task{}, res)
591 actual := res.([]task)
592 assert.EqualValues(t, result, actual)
593 }
594
595 func TestRuntime_ChunkedResponse(t *testing.T) {
596
597
598
599 result := []task{
600 {false, "task 1 content", 1},
601 {false, "task 2 content", 2},
602 }
603 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
604 if req.Header.Get(runtime.HeaderAuthorization) != bearerToken {
605 rw.WriteHeader(http.StatusBadRequest)
606 return
607 }
608 rw.Header().Add(runtime.HeaderTransferEncoding, "chunked")
609 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+charsetUTF8)
610 rw.WriteHeader(http.StatusOK)
611 jsongen := json.NewEncoder(rw)
612 _ = jsongen.Encode(result)
613 }))
614 defer server.Close()
615
616 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
617 return nil
618 })
619
620
621 hu, err := url.Parse(server.URL)
622 require.NoError(t, err)
623
624 rt := New(hu.Host, "/", []string{schemeHTTP})
625 res, err := rt.Submit(&runtime.ClientOperation{
626 ID: "getTasks",
627 Method: http.MethodGet,
628 PathPattern: "/",
629 Schemes: []string{schemeHTTP},
630 Params: rwrtr,
631 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
632 if response.Code() == http.StatusOK {
633 var res []task
634 if e := consumer.Consume(response.Body(), &res); e != nil {
635 return nil, e
636 }
637 return res, nil
638 }
639 return nil, errors.New("generic error")
640 }),
641 AuthInfo: BearerToken(token),
642 })
643
644 require.NoError(t, err)
645 assert.IsType(t, []task{}, res)
646 actual := res.([]task)
647 assert.EqualValues(t, result, actual)
648 }
649
650 func TestRuntime_DebugValue(t *testing.T) {
651 t.Run("empty DEBUG means Debug is False", func(t *testing.T) {
652 t.Setenv("DEBUG", "")
653
654 runtime := New("", "/", []string{schemeHTTPS})
655 assert.False(t, runtime.Debug)
656 })
657
658 t.Run("non-Empty DEBUG means Debug is True", func(t *testing.T) {
659 t.Run("with numerical value", func(t *testing.T) {
660 t.Setenv("DEBUG", "1")
661
662 runtime := New("", "/", []string{schemeHTTPS})
663 assert.True(t, runtime.Debug)
664 })
665
666 t.Run("with boolean value true", func(t *testing.T) {
667 t.Setenv("DEBUG", "true")
668
669 runtime := New("", "/", []string{schemeHTTPS})
670 assert.True(t, runtime.Debug)
671 })
672
673 t.Run("with boolean value false", func(t *testing.T) {
674 t.Setenv("DEBUG", "false")
675
676 runtime := New("", "/", []string{schemeHTTPS})
677 assert.False(t, runtime.Debug)
678 })
679
680 t.Run("with string value ", func(t *testing.T) {
681 t.Setenv("DEBUG", "foo")
682
683 runtime := New("", "/", []string{schemeHTTPS})
684 assert.True(t, runtime.Debug)
685 })
686 })
687 }
688
689 func TestRuntime_OverrideScheme(t *testing.T) {
690 runtime := New("", "/", []string{schemeHTTPS})
691 sch := runtime.pickScheme([]string{schemeHTTP})
692 assert.Equal(t, schemeHTTPS, sch)
693 }
694
695 func TestRuntime_OverrideClient(t *testing.T) {
696 client := &http.Client{}
697 runtime := NewWithClient("", "/", []string{schemeHTTPS}, client)
698 var i int
699 runtime.clientOnce.Do(func() { i++ })
700 assert.Equal(t, client, runtime.client)
701 assert.Equal(t, 0, i)
702 }
703
704 type overrideRoundTripper struct {
705 overridden bool
706 }
707
708 func (o *overrideRoundTripper) RoundTrip(_ *http.Request) (*http.Response, error) {
709 o.overridden = true
710 res := new(http.Response)
711 res.StatusCode = http.StatusOK
712 res.Body = io.NopCloser(bytes.NewBufferString("OK"))
713 return res, nil
714 }
715
716 func TestRuntime_OverrideClientOperation(t *testing.T) {
717 client := &http.Client{}
718 rt := NewWithClient("", "/", []string{schemeHTTPS}, client)
719 var i int
720 rt.clientOnce.Do(func() { i++ })
721 assert.Equal(t, client, rt.client)
722 assert.Equal(t, 0, i)
723
724 client2 := new(http.Client)
725 var transport = &overrideRoundTripper{}
726 client2.Transport = transport
727 require.NotEqual(t, client, client2)
728
729 _, err := rt.Submit(&runtime.ClientOperation{
730 Client: client2,
731 Params: runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
732 return nil
733 }),
734 Reader: runtime.ClientResponseReaderFunc(func(_ runtime.ClientResponse, _ runtime.Consumer) (interface{}, error) {
735 return map[string]interface{}{}, nil
736 }),
737 })
738 require.NoError(t, err)
739 assert.True(t, transport.overridden)
740 }
741
742 func TestRuntime_PreserveTrailingSlash(t *testing.T) {
743 var redirected bool
744
745 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
746 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime+charsetUTF8)
747
748 if req.URL.Path == "/api/tasks" {
749 redirected = true
750 return
751 }
752 if req.URL.Path == "/api/tasks/" {
753 rw.WriteHeader(http.StatusOK)
754 }
755 }))
756 defer server.Close()
757
758 hu, err := url.Parse(server.URL)
759 require.NoError(t, err)
760
761 rt := New(hu.Host, "/", []string{schemeHTTP})
762 rwrtr := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
763 return nil
764 })
765
766 _, err = rt.Submit(&runtime.ClientOperation{
767 ID: "getTasks",
768 Method: http.MethodGet,
769 PathPattern: "/api/tasks/",
770 Params: rwrtr,
771 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, _ runtime.Consumer) (interface{}, error) {
772 if redirected {
773 return nil, errors.New("expected Submit to preserve trailing slashes - this caused a redirect")
774 }
775 if response.Code() == http.StatusOK {
776 return map[string]interface{}{}, nil
777 }
778 return nil, errors.New("generic error")
779 }),
780 })
781 require.NoError(t, err)
782 }
783
784 func TestRuntime_FallbackConsumer(t *testing.T) {
785 result := `W3siY29tcGxldGVkIjpmYWxzZSwiY29udGVudCI6ImRHRnpheUF4SUdOdmJuUmxiblE9IiwiaWQiOjF9XQ==`
786 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
787 rw.Header().Add(runtime.HeaderContentType, "application/x-task")
788 rw.WriteHeader(http.StatusOK)
789 _, _ = rw.Write([]byte(result))
790 }))
791 defer server.Close()
792
793 rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error {
794 return req.SetBodyParam(bytes.NewBufferString("hello"))
795 })
796
797 hu, err := url.Parse(server.URL)
798 require.NoError(t, err)
799 rt := New(hu.Host, "/", []string{schemeHTTP})
800
801
802 _, err = rt.Submit(&runtime.ClientOperation{
803 ID: "getTasks",
804 Method: "POST",
805 PathPattern: "/",
806 Schemes: []string{schemeHTTP},
807 ConsumesMediaTypes: []string{"application/octet-stream"},
808 Params: rwrtr,
809 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
810 if response.Code() == http.StatusOK {
811 var res []byte
812 if e := consumer.Consume(response.Body(), &res); e != nil {
813 return nil, e
814 }
815 return res, nil
816 }
817 return nil, errors.New("generic error")
818 }),
819 })
820 require.Error(t, err)
821 assert.Equal(t, `no consumer: "application/x-task"`, err.Error())
822
823
824 rt.Consumers["*/*"] = rt.Consumers[runtime.DefaultMime]
825 res, err := rt.Submit(&runtime.ClientOperation{
826 ID: "getTasks",
827 Method: "POST",
828 PathPattern: "/",
829 Schemes: []string{schemeHTTP},
830 ConsumesMediaTypes: []string{"application/octet-stream"},
831 Params: rwrtr,
832 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
833 if response.Code() == http.StatusOK {
834 var res []byte
835 if e := consumer.Consume(response.Body(), &res); e != nil {
836 return nil, e
837 }
838 return res, nil
839 }
840 return nil, errors.New("generic error")
841 }),
842 })
843
844 require.NoError(t, err)
845 assert.IsType(t, []byte{}, res)
846 actual := res.([]byte)
847 assert.EqualValues(t, result, actual)
848 }
849
850 func TestRuntime_AuthHeaderParamDetected(t *testing.T) {
851
852
853
854 result := []task{
855 {false, "task 1 content", 1},
856 {false, "task 2 content", 2},
857 }
858 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
859 if req.Header.Get(runtime.HeaderAuthorization) != bearerToken {
860 rw.WriteHeader(http.StatusUnauthorized)
861 return
862 }
863 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime)
864 rw.WriteHeader(http.StatusOK)
865 jsongen := json.NewEncoder(rw)
866 _ = jsongen.Encode(result)
867 }))
868 defer server.Close()
869
870 rwrtr := runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error {
871 return req.SetHeaderParam(runtime.HeaderAuthorization, bearerToken)
872 })
873
874 hu, err := url.Parse(server.URL)
875 require.NoError(t, err)
876
877 rt := New(hu.Host, "/", []string{schemeHTTP})
878 rt.DefaultAuthentication = BearerToken("not-the-super-secret-token")
879 res, err := rt.Submit(&runtime.ClientOperation{
880 ID: "getTasks",
881 Params: rwrtr,
882 Reader: runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
883 if response.Code() == http.StatusOK {
884 var res []task
885 if e := consumer.Consume(response.Body(), &res); e != nil {
886 return nil, e
887 }
888 return res, nil
889 }
890 return nil, errors.New("generic error")
891 }),
892 })
893
894 require.NoError(t, err)
895 assert.IsType(t, []task{}, res)
896 actual := res.([]task)
897 assert.EqualValues(t, result, actual)
898 }
899
900 func TestRuntime_Timeout(t *testing.T) {
901 const (
902 operationID = "getTasks"
903
904
905 clientTimeout time.Duration = 25 * time.Millisecond
906 serverDelay time.Duration = 100 * time.Millisecond
907 clientNoTimeout time.Duration = 250 * time.Millisecond
908 ctxError = "context deadline exceeded"
909 )
910 result := []task{
911 {false, "task 1 content", 1},
912 {false, "task 2 content", 2},
913 }
914
915 signedContext := func(value string) context.Context {
916 return context.WithValue(context.Background(), rtKey, value)
917 }
918
919 requestWriter := func(timeout time.Duration) runtime.ClientRequestWriter {
920
921 return runtime.ClientRequestWriterFunc(func(req runtime.ClientRequest, _ strfmt.Registry) error {
922 return req.SetTimeout(timeout)
923 })
924 }
925
926 requestReader := runtime.ClientResponseReaderFunc(func(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
927 if response.Code() != http.StatusOK {
928 return nil, errors.New("generic error")
929 }
930
931 var res []task
932 if e := consumer.Consume(response.Body(), &res); e != nil {
933 return nil, e
934 }
935 return res, nil
936 })
937
938 t.Run("with timeout specified as a request parameter, no operation context", func(t *testing.T) {
939 host, cleaner := serverBuilder(t, serverDelay, result)
940 t.Cleanup(cleaner)
941
942 rt := New(host, "/", []string{schemeHTTP})
943 rt.Context = signedContext("test")
944 rt.Transport = testContextTransport(t, true, true, "test")
945
946 t.Run("should not time out", func(t *testing.T) {
947 res, err := rt.Submit(&runtime.ClientOperation{
948 ID: operationID,
949 Context: nil,
950 Params: requestWriter(clientNoTimeout),
951 Reader: requestReader,
952 })
953 require.NoError(t, err)
954 assertResult(result)(t, res)
955 })
956
957 t.Run("should time out", func(t *testing.T) {
958 _, err := rt.Submit(&runtime.ClientOperation{
959 ID: operationID,
960 Context: nil,
961 Params: requestWriter(clientTimeout),
962 Reader: requestReader,
963 })
964 require.Error(t, err)
965 require.ErrorContains(t, err, ctxError)
966 })
967 })
968
969 t.Run("with timeout specified as a request parameter, no context at all", func(t *testing.T) {
970 host, cleaner := serverBuilder(t, serverDelay, result)
971 t.Cleanup(cleaner)
972
973 rt := New(host, "/", []string{schemeHTTP})
974 rt.Context = nil
975 rt.Transport = testContextTransport(t, true, false, "")
976
977 t.Run("should not time out", func(t *testing.T) {
978 res, err := rt.Submit(&runtime.ClientOperation{
979 ID: operationID,
980 Context: nil,
981 Params: requestWriter(clientNoTimeout),
982 Reader: requestReader,
983 })
984 require.NoError(t, err)
985 assertResult(result)(t, res)
986 })
987
988 t.Run("should time out", func(t *testing.T) {
989 _, err := rt.Submit(&runtime.ClientOperation{
990 ID: operationID,
991 Context: nil,
992 Params: requestWriter(clientTimeout),
993 Reader: requestReader,
994 })
995 require.Error(t, err)
996 require.ErrorContains(t, err, ctxError)
997 })
998 })
999
1000 t.Run("with inherited operation context, timeout specified as operation context, request timeout set to 0", func(t *testing.T) {
1001 host, cleaner := serverBuilder(t, serverDelay, result)
1002 t.Cleanup(cleaner)
1003
1004 rt := New(host, "/", []string{schemeHTTP})
1005 rt.Context = signedContext("test")
1006 rt.Transport = testContextTransport(t, true, true, "test")
1007
1008 t.Run("should not time out", func(t *testing.T) {
1009 operationCtx, cancel := context.WithTimeout(rt.Context, clientNoTimeout)
1010 defer cancel()
1011
1012 res, err := rt.Submit(&runtime.ClientOperation{
1013 ID: operationID,
1014 Context: operationCtx,
1015 Params: requestWriter(0),
1016 Reader: requestReader,
1017 })
1018 require.NoError(t, err)
1019 assertResult(result)(t, res)
1020 })
1021
1022 t.Run("should time out", func(t *testing.T) {
1023 operationCtx, cancel := context.WithTimeout(rt.Context, clientTimeout)
1024 defer cancel()
1025
1026 _, err := rt.Submit(&runtime.ClientOperation{
1027 ID: operationID,
1028 Context: operationCtx,
1029 Params: requestWriter(0),
1030 Reader: requestReader,
1031 })
1032 require.Error(t, err)
1033 require.ErrorContains(t, err, ctxError)
1034 })
1035 })
1036
1037 t.Run("with a fresh operation context, timeout specified as operation context, request timeout set to 0", func(t *testing.T) {
1038 host, cleaner := serverBuilder(t, serverDelay, result)
1039 t.Cleanup(cleaner)
1040 rt := New(host, "/", []string{schemeHTTP})
1041 rt.Context = nil
1042 rt.Transport = testContextTransport(t, true, false, "")
1043
1044 t.Run("should not time out", func(t *testing.T) {
1045 operationCtx, cancel := context.WithTimeout(context.Background(), clientNoTimeout)
1046 defer cancel()
1047
1048 res, err := rt.Submit(&runtime.ClientOperation{
1049 ID: operationID,
1050 Context: operationCtx,
1051 Params: requestWriter(0),
1052 Reader: requestReader,
1053 })
1054 require.NoError(t, err)
1055 assertResult(result)(t, res)
1056 })
1057
1058 t.Run("should time out", func(t *testing.T) {
1059 operationCtx, cancel := context.WithTimeout(context.Background(), clientTimeout)
1060 defer cancel()
1061
1062 _, err := rt.Submit(&runtime.ClientOperation{
1063 ID: operationID,
1064 Context: operationCtx,
1065 Params: requestWriter(0),
1066 Reader: requestReader,
1067 })
1068 require.Error(t, err)
1069 require.ErrorContains(t, err, ctxError)
1070 })
1071 })
1072
1073 t.Run("with an hypothetical timeout specified as runtime context, no operation context", func(t *testing.T) {
1074
1075 host, cleaner := serverBuilder(t, serverDelay, result)
1076 t.Cleanup(cleaner)
1077
1078 t.Run("should not time out", func(t *testing.T) {
1079 rt := New(host, "/", []string{schemeHTTP})
1080 ctx, cancel := context.WithTimeout(signedContext("test"), clientNoTimeout)
1081 defer cancel()
1082
1083 rt.Context = ctx
1084 rt.Transport = testContextTransport(t, true, true, "test")
1085
1086 res, err := rt.Submit(&runtime.ClientOperation{
1087 ID: operationID,
1088 Context: nil,
1089 Params: requestWriter(0),
1090 Reader: requestReader,
1091 })
1092 require.NoError(t, err)
1093 assertResult(result)(t, res)
1094 })
1095
1096 t.Run("should time out", func(t *testing.T) {
1097 rt := New(host, "/", []string{schemeHTTP})
1098 ctx, cancel := context.WithTimeout(signedContext("test"), clientTimeout)
1099 defer cancel()
1100
1101 rt.Context = ctx
1102 rt.Transport = testContextTransport(t, true, true, "test")
1103
1104 _, err := rt.Submit(&runtime.ClientOperation{
1105 ID: operationID,
1106 Context: nil,
1107 Params: requestWriter(0),
1108 Reader: requestReader,
1109 })
1110 require.Error(t, err)
1111 require.ErrorContains(t, err, ctxError)
1112 })
1113 })
1114
1115 t.Run("with multiple timeouts set, shortest wins", func(t *testing.T) {
1116 host, cleaner := serverBuilder(t, serverDelay, result)
1117 t.Cleanup(cleaner)
1118
1119 rt := New(host, "/", []string{schemeHTTP})
1120 runtimeCtx, cancelRuntime := context.WithTimeout(signedContext("test"), clientNoTimeout)
1121 rt.Context = runtimeCtx
1122 defer cancelRuntime()
1123 rt.Transport = testContextTransport(t, true, true, "test")
1124
1125 t.Run("should not time out", func(t *testing.T) {
1126 operationCtx, cancelOperation := context.WithTimeout(
1127 signedContext("test"),
1128 serverDelay+(clientNoTimeout-serverDelay)/2,
1129 )
1130 defer cancelOperation()
1131
1132 res, err := rt.Submit(&runtime.ClientOperation{
1133 ID: operationID,
1134 Context: operationCtx,
1135 Params: requestWriter(serverDelay + (clientNoTimeout-serverDelay)/3),
1136 Reader: requestReader,
1137 })
1138 require.NoError(t, err)
1139 assertResult(result)(t, res)
1140 })
1141
1142 t.Run("should time out on operation context deadline", func(t *testing.T) {
1143
1144
1145 operationCtx, cancelOperation := context.WithTimeout(
1146 signedContext("test"),
1147 serverDelay-(clientNoTimeout-serverDelay)/4,
1148 )
1149 defer cancelOperation()
1150
1151 _, err := rt.Submit(&runtime.ClientOperation{
1152 ID: operationID,
1153 Context: operationCtx,
1154 Params: requestWriter(
1155 serverDelay + (clientNoTimeout-serverDelay)/4,
1156 ),
1157 Reader: requestReader,
1158 })
1159 require.Error(t, err)
1160 require.ErrorContains(t, err, ctxError)
1161 })
1162
1163 t.Run("should time out on operation timeout param", func(t *testing.T) {
1164 operationCtx, cancelOperation := context.WithTimeout(
1165 signedContext("test"),
1166 serverDelay+(clientNoTimeout-serverDelay)/2,
1167 )
1168 defer cancelOperation()
1169
1170 _, err := rt.Submit(&runtime.ClientOperation{
1171 ID: operationID,
1172 Context: operationCtx,
1173 Params: requestWriter(
1174 serverDelay - (clientNoTimeout-serverDelay)/4,
1175 ),
1176 Reader: requestReader,
1177 })
1178 require.Error(t, err)
1179 require.ErrorContains(t, err, ctxError)
1180 })
1181 })
1182
1183 t.Run("with no context, explicit infinite wait", func(t *testing.T) {
1184 host, cleaner := serverBuilder(t, serverDelay, result)
1185 t.Cleanup(cleaner)
1186
1187 rt := New(host, "/", []string{schemeHTTP})
1188 rt.Context = signedContext("test")
1189 rt.Transport = testContextTransport(t, false, true, "test")
1190
1191 t.Run("should not time out", func(t *testing.T) {
1192 resp, err := rt.Submit(&runtime.ClientOperation{
1193 ID: operationID,
1194 Context: nil,
1195 Params: requestWriter(0),
1196 Reader: requestReader,
1197 })
1198 require.NoError(t, err)
1199 assertResult(result)(t, resp)
1200 })
1201 })
1202 t.Run("with no context, request uses the default timeout", func(t *testing.T) {
1203 requestEmptyWriter := runtime.ClientRequestWriterFunc(func(_ runtime.ClientRequest, _ strfmt.Registry) error {
1204 return nil
1205 })
1206 host, cleaner := serverBuilder(t, serverDelay, result)
1207 t.Cleanup(cleaner)
1208
1209 rt := New(host, "/", []string{schemeHTTP})
1210 rt.Context = signedContext("test")
1211 rt.Transport = testDefaultsInTransport(t, "test")
1212
1213 t.Run("should not time out", func(t *testing.T) {
1214 resp, err := rt.Submit(&runtime.ClientOperation{
1215 ID: operationID,
1216 Context: nil,
1217 Params: requestEmptyWriter,
1218 Reader: requestReader,
1219 })
1220 require.NoError(t, err)
1221 assertResult(result)(t, resp)
1222 })
1223 })
1224 }
1225
1226 func isContextSigned(ctx context.Context, value string) bool {
1227 v, ok := ctx.Value(rtKey).(string)
1228
1229 return ok && v == value
1230 }
1231
1232 func testContextTransport(t *testing.T, hasTimeout, expectSigned bool, value string) http.RoundTripper {
1233
1234
1235 return roundTripperFunc(func(req *http.Request) (*http.Response, error) {
1236 ctx := req.Context()
1237
1238 t.Run("request context should propagate value", func(t *testing.T) {
1239 assert.Equal(t, expectSigned, isContextSigned(ctx, value), "expected the request context to inherit values")
1240 })
1241
1242 t.Run(fmt.Sprintf("request context should have a deadline %t", hasTimeout), func(t *testing.T) {
1243 _, hasDeadline := ctx.Deadline()
1244 assert.Equalf(t, hasTimeout, hasDeadline, "expected request context to have a deadline")
1245 })
1246
1247 return http.DefaultTransport.RoundTrip(req)
1248 })
1249 }
1250
1251 func testDefaultsInTransport(t *testing.T, value string) http.RoundTripper {
1252
1253 return roundTripperFunc(func(req *http.Request) (*http.Response, error) {
1254 ctx := req.Context()
1255
1256 t.Run("request context should propagate value", func(t *testing.T) {
1257 assert.True(t, isContextSigned(ctx, value), "expected the request context to inherit values")
1258 })
1259
1260 t.Run("request context should have a default deadline", func(t *testing.T) {
1261 deadline, hasDeadline := ctx.Deadline()
1262 assert.True(t, hasDeadline, "expected request context to have a deadline")
1263
1264 remainingDuration := time.Until(deadline).Seconds()
1265 assert.InDeltaf(t, DefaultTimeout.Seconds(), remainingDuration, 1.0, "expected timeout to be set to DefaultTimeout")
1266 })
1267
1268 return http.DefaultTransport.RoundTrip(req)
1269 })
1270 }
1271
1272 func assertResult(result []task) func(testing.TB, interface{}) {
1273 return func(t testing.TB, res interface{}) {
1274 assert.IsType(t, []task{}, res)
1275 actual, ok := res.([]task)
1276 require.True(t, ok)
1277 assert.EqualValues(t, result, actual)
1278 }
1279 }
1280
1281 func serverBuilder(t testing.TB, delay time.Duration, result []task) (string, func()) {
1282 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
1283 ctx := req.Context()
1284 timer := time.NewTimer(delay)
1285
1286 select {
1287 case <-ctx.Done():
1288 http.Error(rw, ctx.Err().Error(), http.StatusInternalServerError)
1289
1290 return
1291 case <-timer.C:
1292 rw.Header().Add(runtime.HeaderContentType, runtime.JSONMime)
1293 rw.WriteHeader(http.StatusOK)
1294 jsongen := json.NewEncoder(rw)
1295 _ = jsongen.Encode(result)
1296
1297 return
1298 }
1299 }))
1300 hu, err := url.Parse(server.URL)
1301 require.NoError(t, err)
1302
1303 return hu.Host, server.Close
1304 }
1305
View as plain text