1
2
3
4
5
6
7
8
9
10
11
12
13
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
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
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
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
113 pr4 := newPeekingReader(closeReader(&eofReader{}))
114 require.False(t, pr4.HasContent())
115
116
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