...

Source file src/github.com/go-openapi/runtime/client/runtime_test.go

Documentation: github.com/go-openapi/runtime/client

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    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  // task This describes a task. Tasks require a content property to be set.
    45  type task struct {
    46  	// Completed
    47  	Completed bool `json:"completed" xml:"completed"`
    48  
    49  	// Content Task content can contain [GFM](https://help.github.com/articles/github-flavored-markdown/).
    50  	Content string `json:"content" xml:"content"`
    51  
    52  	// ID This id property is autogenerated when a task is created.
    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  	// test that it can make a simple request
    62  	// and get the response for it.
    63  	// defaults all the way down
    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  	// test that it can make a simple request
   138  	// and get the response for it.
   139  	// defaults all the way down
   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  	// test that it can make a simple XML request
   188  	// and get the response for it.
   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  	// test that it can make a simple text request
   236  	// and get the response for it.
   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  	// test that it can make a simple csv request
   278  	// and get the response for it.
   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  	// test that it can make a simple request
   441  	// and get the response for it.
   442  	// defaults all the way down
   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  	// test that it can make a simple request
   544  	// and get the response for it.
   545  	// defaults all the way down
   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  	// test that it can make a simple request
   597  	// and get the response for it.
   598  	// defaults all the way down
   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  	// specDoc, err := spec.Load("../../fixtures/codegen/todolist.simple.yml")
   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  	// without the fallback consumer
   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  	// add the fallback consumer
   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  	// test that it can make a simple request
   852  	// and get the response for it.
   853  	// defaults all the way down
   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) { //nolint:maintidx // linter evaluates the total lines of code, which is misleading
   901  	const (
   902  		operationID = "getTasks"
   903  
   904  		// these values should be sufficient for most CI engines
   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  		// this writer sets the timeout parameter of the ClientRequest
   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  		// in real life, the runtime context may be cancellable for other reasons than timeout
  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  			// NOTE: we'll be able to catch more precisely which context was canceled
  1144  			// in go1.21 and context.WithTimeoutCause.
  1145  			operationCtx, cancelOperation := context.WithTimeout(
  1146  				signedContext("test"),
  1147  				serverDelay-(clientNoTimeout-serverDelay)/4, // this one times out
  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, // this one times out
  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") // verify that no deadline is passed to the emitted context
  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, // leaves defaults
  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  	// inject a round tripper to check the context in the request about to be emitted
  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  	// inject a round tripper to check the context in the request about to be emitted
  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