File: //proc/thread-self/root/opt/go/pkg/mod/github.com/go-openapi/
[email protected]/request_test.go
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package runtime
import (
"bufio"
"bytes"
"context"
"io"
"net/http"
"net/url"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
)
type eofReader struct{}
func (e *eofReader) Read(_ []byte) (int, error) {
return 0, io.EOF
}
func closeReader(rdr io.Reader) *closeCounting {
return &closeCounting{
rdr: rdr,
}
}
type closeCounting struct {
rdr io.Reader
closed int
}
func (c *closeCounting) Read(d []byte) (int, error) {
return c.rdr.Read(d)
}
func (c *closeCounting) Close() error {
c.closed++
if cr, ok := c.rdr.(io.ReadCloser); ok {
return cr.Close()
}
return nil
}
type countingBufioReader struct {
buffereds int
peeks int
reads int
br interface {
Buffered() int
Peek(int) ([]byte, error)
Read([]byte) (int, error)
}
}
func (c *countingBufioReader) Buffered() int {
c.buffereds++
return c.br.Buffered()
}
func (c *countingBufioReader) Peek(v int) ([]byte, error) {
c.peeks++
return c.br.Peek(v)
}
func (c *countingBufioReader) Read(p []byte) (int, error) {
c.reads++
return c.br.Read(p)
}
func TestPeekingReader(t *testing.T) {
// just passes to original reader when nothing called
exp1 := []byte("original")
pr1 := newPeekingReader(closeReader(bytes.NewReader(exp1)))
b1, err := io.ReadAll(pr1)
require.NoError(t, err)
assert.Equal(t, exp1, b1)
// uses actual when there was some buffering
exp2 := []byte("actual")
pr2 := newPeekingReader(closeReader(bytes.NewReader(exp2)))
peeked, err := pr2.underlying.Peek(1)
require.NoError(t, err)
require.Equal(t, "a", string(peeked))
b2, err := io.ReadAll(pr2)
require.NoError(t, err)
assert.Equal(t, string(exp2), string(b2))
// passes close call through to original reader
cr := closeReader(closeReader(bytes.NewReader(exp2)))
pr3 := newPeekingReader(cr)
require.NoError(t, pr3.Close())
require.Equal(t, 1, cr.closed)
// returns false when the stream is empty
pr4 := newPeekingReader(closeReader(&eofReader{}))
require.False(t, pr4.HasContent())
// returns true when the stream has content
rdr := closeReader(strings.NewReader("hello"))
pr := newPeekingReader(rdr)
cbr := &countingBufioReader{
br: bufio.NewReader(rdr),
}
pr.underlying = cbr
require.True(t, pr.HasContent())
require.Equal(t, 1, cbr.buffereds)
require.Equal(t, 1, cbr.peeks)
require.Equal(t, 0, cbr.reads)
require.True(t, pr.HasContent())
require.Equal(t, 2, cbr.buffereds)
require.Equal(t, 1, cbr.peeks)
require.Equal(t, 0, cbr.reads)
b, err := io.ReadAll(pr)
require.NoError(t, err)
require.Equal(t, "hello", string(b))
require.Equal(t, 2, cbr.buffereds)
require.Equal(t, 1, cbr.peeks)
require.Equal(t, 2, cbr.reads)
require.Equal(t, 0, cbr.br.Buffered())
t.Run("closing a closed peekingReader", func(t *testing.T) {
const content = "content"
r := newPeekingReader(io.NopCloser(strings.NewReader(content)))
require.NoError(t, r.Close())
require.NotPanics(t, func() {
err := r.Close()
require.Error(t, err)
})
})
t.Run("reading from a closed peekingReader", func(t *testing.T) {
const content = "content"
r := newPeekingReader(io.NopCloser(strings.NewReader(content)))
require.NoError(t, r.Close())
require.NotPanics(t, func() {
_, err := io.ReadAll(r)
require.Error(t, err)
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
})
})
t.Run("reading from a nil peekingReader", func(t *testing.T) {
var r *peekingReader
require.NotPanics(t, func() {
buf := make([]byte, 10)
_, err := r.Read(buf)
require.Error(t, err)
require.ErrorIs(t, err, io.EOF)
})
})
}
func TestJSONRequest(t *testing.T) {
req, err := JSONRequest(http.MethodGet, "/swagger.json", nil)
require.NoError(t, err)
assert.Equal(t, http.MethodGet, req.Method)
assert.Equal(t, JSONMime, req.Header.Get(HeaderContentType))
assert.Equal(t, JSONMime, req.Header.Get(HeaderAccept))
req, err = JSONRequest(http.MethodGet, "%2", nil)
require.Error(t, err)
assert.Nil(t, req)
}
func TestHasBody(t *testing.T) {
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "", nil)
require.NoError(t, err)
assert.False(t, HasBody(req))
req.ContentLength = 123
assert.True(t, HasBody(req))
}
func TestMethod(t *testing.T) {
testcase := []struct {
method string
canHaveBody bool
allowsBody bool
isSafe bool
}{
{"put", true, true, false},
{"post", true, true, false},
{"patch", true, true, false},
{"delete", true, true, false},
{"get", false, true, true},
{"options", false, true, false},
{"head", false, false, true},
{"invalid", false, true, false},
{"", false, true, false},
}
for _, tc := range testcase {
t.Run(tc.method, func(t *testing.T) {
assert.Equal(t, tc.canHaveBody, CanHaveBody(tc.method), "CanHaveBody")
req := http.Request{Method: tc.method}
assert.Equal(t, tc.allowsBody, AllowsBody(&req), "AllowsBody")
assert.Equal(t, tc.isSafe, IsSafe(&req), "IsSafe")
})
}
}
func TestReadSingle(t *testing.T) {
values := url.Values(make(map[string][]string))
values.Add("something", "the thing")
assert.Equal(t, "the thing", ReadSingleValue(Values(values), "something"))
assert.Empty(t, ReadSingleValue(Values(values), "notthere"))
}
func TestReadCollection(t *testing.T) {
values := url.Values(make(map[string][]string))
values.Add("something", "value1,value2")
assert.Equal(t, []string{"value1", "value2"}, ReadCollectionValue(Values(values), "something", "csv"))
assert.Empty(t, ReadCollectionValue(Values(values), "notthere", ""))
}