...

Source file src/github.com/go-openapi/runtime/request_test.go

Documentation: github.com/go-openapi/runtime

     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 runtime
    16  
    17  import (
    18  	"bufio"
    19  	"bytes"
    20  	"context"
    21  	"io"
    22  	"net/http"
    23  	"net/url"
    24  	"strings"
    25  	"testing"
    26  
    27  	"github.com/stretchr/testify/require"
    28  
    29  	"github.com/stretchr/testify/assert"
    30  )
    31  
    32  type eofReader struct{}
    33  
    34  func (e *eofReader) Read(_ []byte) (int, error) {
    35  	return 0, io.EOF
    36  }
    37  
    38  func closeReader(rdr io.Reader) *closeCounting {
    39  	return &closeCounting{
    40  		rdr: rdr,
    41  	}
    42  }
    43  
    44  type closeCounting struct {
    45  	rdr    io.Reader
    46  	closed int
    47  }
    48  
    49  func (c *closeCounting) Read(d []byte) (int, error) {
    50  	return c.rdr.Read(d)
    51  }
    52  
    53  func (c *closeCounting) Close() error {
    54  	c.closed++
    55  	if cr, ok := c.rdr.(io.ReadCloser); ok {
    56  		return cr.Close()
    57  	}
    58  	return nil
    59  }
    60  
    61  type countingBufioReader struct {
    62  	buffereds int
    63  	peeks     int
    64  	reads     int
    65  
    66  	br interface {
    67  		Buffered() int
    68  		Peek(int) ([]byte, error)
    69  		Read([]byte) (int, error)
    70  	}
    71  }
    72  
    73  func (c *countingBufioReader) Buffered() int {
    74  	c.buffereds++
    75  	return c.br.Buffered()
    76  }
    77  
    78  func (c *countingBufioReader) Peek(v int) ([]byte, error) {
    79  	c.peeks++
    80  	return c.br.Peek(v)
    81  }
    82  
    83  func (c *countingBufioReader) Read(p []byte) (int, error) {
    84  	c.reads++
    85  	return c.br.Read(p)
    86  }
    87  
    88  func TestPeekingReader(t *testing.T) {
    89  	// just passes to original reader when nothing called
    90  	exp1 := []byte("original")
    91  	pr1 := newPeekingReader(closeReader(bytes.NewReader(exp1)))
    92  	b1, err := io.ReadAll(pr1)
    93  	require.NoError(t, err)
    94  	assert.Equal(t, exp1, b1)
    95  
    96  	// uses actual when there was some buffering
    97  	exp2 := []byte("actual")
    98  	pr2 := newPeekingReader(closeReader(bytes.NewReader(exp2)))
    99  	peeked, err := pr2.underlying.Peek(1)
   100  	require.NoError(t, err)
   101  	require.Equal(t, "a", string(peeked))
   102  	b2, err := io.ReadAll(pr2)
   103  	require.NoError(t, err)
   104  	assert.Equal(t, string(exp2), string(b2))
   105  
   106  	// passes close call through to original reader
   107  	cr := closeReader(closeReader(bytes.NewReader(exp2)))
   108  	pr3 := newPeekingReader(cr)
   109  	require.NoError(t, pr3.Close())
   110  	require.Equal(t, 1, cr.closed)
   111  
   112  	// returns false when the stream is empty
   113  	pr4 := newPeekingReader(closeReader(&eofReader{}))
   114  	require.False(t, pr4.HasContent())
   115  
   116  	// returns true when the stream has content
   117  	rdr := closeReader(strings.NewReader("hello"))
   118  	pr := newPeekingReader(rdr)
   119  	cbr := &countingBufioReader{
   120  		br: bufio.NewReader(rdr),
   121  	}
   122  	pr.underlying = cbr
   123  
   124  	require.True(t, pr.HasContent())
   125  	require.Equal(t, 1, cbr.buffereds)
   126  	require.Equal(t, 1, cbr.peeks)
   127  	require.Equal(t, 0, cbr.reads)
   128  	require.True(t, pr.HasContent())
   129  	require.Equal(t, 2, cbr.buffereds)
   130  	require.Equal(t, 1, cbr.peeks)
   131  	require.Equal(t, 0, cbr.reads)
   132  
   133  	b, err := io.ReadAll(pr)
   134  	require.NoError(t, err)
   135  	require.Equal(t, "hello", string(b))
   136  	require.Equal(t, 2, cbr.buffereds)
   137  	require.Equal(t, 1, cbr.peeks)
   138  	require.Equal(t, 2, cbr.reads)
   139  	require.Equal(t, 0, cbr.br.Buffered())
   140  
   141  	t.Run("closing a closed peekingReader", func(t *testing.T) {
   142  		const content = "content"
   143  		r := newPeekingReader(io.NopCloser(strings.NewReader(content)))
   144  		require.NoError(t, r.Close())
   145  
   146  		require.NotPanics(t, func() {
   147  			err := r.Close()
   148  			require.Error(t, err)
   149  		})
   150  	})
   151  
   152  	t.Run("reading from a closed peekingReader", func(t *testing.T) {
   153  		const content = "content"
   154  		r := newPeekingReader(io.NopCloser(strings.NewReader(content)))
   155  		require.NoError(t, r.Close())
   156  
   157  		require.NotPanics(t, func() {
   158  			_, err := io.ReadAll(r)
   159  			require.Error(t, err)
   160  			require.ErrorIs(t, err, io.ErrUnexpectedEOF)
   161  		})
   162  	})
   163  
   164  	t.Run("reading from a nil peekingReader", func(t *testing.T) {
   165  		var r *peekingReader
   166  		require.NotPanics(t, func() {
   167  			buf := make([]byte, 10)
   168  			_, err := r.Read(buf)
   169  			require.Error(t, err)
   170  			require.ErrorIs(t, err, io.EOF)
   171  		})
   172  	})
   173  }
   174  
   175  func TestJSONRequest(t *testing.T) {
   176  	req, err := JSONRequest(http.MethodGet, "/swagger.json", nil)
   177  	require.NoError(t, err)
   178  	assert.Equal(t, http.MethodGet, req.Method)
   179  	assert.Equal(t, JSONMime, req.Header.Get(HeaderContentType))
   180  	assert.Equal(t, JSONMime, req.Header.Get(HeaderAccept))
   181  
   182  	req, err = JSONRequest(http.MethodGet, "%2", nil)
   183  	require.Error(t, err)
   184  	assert.Nil(t, req)
   185  }
   186  
   187  func TestHasBody(t *testing.T) {
   188  	req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "", nil)
   189  	require.NoError(t, err)
   190  	assert.False(t, HasBody(req))
   191  
   192  	req.ContentLength = 123
   193  	assert.True(t, HasBody(req))
   194  }
   195  
   196  func TestMethod(t *testing.T) {
   197  	testcase := []struct {
   198  		method      string
   199  		canHaveBody bool
   200  		allowsBody  bool
   201  		isSafe      bool
   202  	}{
   203  		{"put", true, true, false},
   204  		{"post", true, true, false},
   205  		{"patch", true, true, false},
   206  		{"delete", true, true, false},
   207  		{"get", false, true, true},
   208  		{"options", false, true, false},
   209  		{"head", false, false, true},
   210  		{"invalid", false, true, false},
   211  		{"", false, true, false},
   212  	}
   213  
   214  	for _, tc := range testcase {
   215  		t.Run(tc.method, func(t *testing.T) {
   216  			assert.Equal(t, tc.canHaveBody, CanHaveBody(tc.method), "CanHaveBody")
   217  
   218  			req := http.Request{Method: tc.method}
   219  			assert.Equal(t, tc.allowsBody, AllowsBody(&req), "AllowsBody")
   220  			assert.Equal(t, tc.isSafe, IsSafe(&req), "IsSafe")
   221  		})
   222  	}
   223  }
   224  
   225  func TestReadSingle(t *testing.T) {
   226  	values := url.Values(make(map[string][]string))
   227  	values.Add("something", "the thing")
   228  	assert.Equal(t, "the thing", ReadSingleValue(Values(values), "something"))
   229  	assert.Empty(t, ReadSingleValue(Values(values), "notthere"))
   230  }
   231  
   232  func TestReadCollection(t *testing.T) {
   233  	values := url.Values(make(map[string][]string))
   234  	values.Add("something", "value1,value2")
   235  	assert.Equal(t, []string{"value1", "value2"}, ReadCollectionValue(Values(values), "something", "csv"))
   236  	assert.Empty(t, ReadCollectionValue(Values(values), "notthere", ""))
   237  }
   238  

View as plain text