Skip to content

Commit 1626592

Browse files
committed
removed feeding the last empty bytes into the callback, improved test coverage
1 parent 17df486 commit 1626592

File tree

2 files changed

+163
-7
lines changed

2 files changed

+163
-7
lines changed

http/body.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,11 @@ func (b *Body) Callback(cb func([]byte) error) error {
5555
switch b.error {
5656
case nil:
5757
case io.EOF:
58-
return cb(data)
58+
if len(data) > 0 {
59+
return cb(data)
60+
}
61+
62+
return nil
5963
default:
6064
return b.error
6165
}
@@ -208,13 +212,9 @@ func (b *Body) Reset(request *Request) {
208212
func (b *Body) multipartBoundary() (boundary string, ok bool) {
209213
for key, value := range strutil.WalkKV(strutil.CutParams(b.request.ContentType)) {
210214
if key == "boundary" {
211-
if len(boundary) != 0 {
212-
return "", false
213-
}
214-
215-
boundary = value
215+
return value, true
216216
}
217217
}
218218

219-
return boundary, true
219+
return "", false
220220
}

http/body_test.go

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,167 @@ import (
55
"testing"
66

77
"github.com/indigo-web/indigo/config"
8+
"github.com/indigo-web/indigo/http/mime"
9+
"github.com/indigo-web/indigo/http/status"
810
"github.com/indigo-web/indigo/transport/dummy"
911
"github.com/stretchr/testify/require"
1012
)
1113

1214
func TestBody(t *testing.T) {
15+
newBody := func(data ...string) *Body {
16+
chunks := make([][]byte, len(data))
17+
for i, chunk := range data {
18+
chunks[i] = []byte(chunk)
19+
}
20+
21+
return NewBody(dummy.NewMockClient(chunks...))
22+
}
23+
24+
t.Run("Callback", func(t *testing.T) {
25+
t.Run("happy path", func(t *testing.T) {
26+
body := newBody("hello", "world")
27+
written := make([]string, 0, 2)
28+
err := body.Callback(func(bytes []byte) error {
29+
written = append(written, string(bytes))
30+
return nil
31+
})
32+
require.NoError(t, err)
33+
require.Equal(t, []string{"hello", "world"}, written)
34+
})
35+
36+
t.Run("error", func(t *testing.T) {
37+
body := newBody("hello", "world")
38+
var written []string
39+
closure := func(bytes []byte) error {
40+
written = append(written, string(bytes))
41+
return status.ErrBadRequest
42+
}
43+
err := body.Callback(closure)
44+
require.EqualError(t, err, status.ErrBadRequest.Error())
45+
require.Equal(t, []string{"hello"}, written)
46+
47+
err = body.Callback(closure)
48+
require.EqualError(t, err, status.ErrBadRequest.Error())
49+
require.Equal(t, []string{"hello"}, written)
50+
})
51+
})
52+
53+
t.Run("Bytes and String", func(t *testing.T) {
54+
newRequest := func(cfg *config.Config, contentLength int, chunked bool) *Request {
55+
return &Request{
56+
cfg: cfg,
57+
commonHeaders: commonHeaders{
58+
ContentLength: contentLength,
59+
Chunked: chunked,
60+
},
61+
}
62+
}
63+
64+
test := func(chunked bool) func(t *testing.T) {
65+
return func(t *testing.T) {
66+
testHelloworld := func(t *testing.T, cfg *config.Config) {
67+
body := newBody("hello", "world")
68+
body.request = newRequest(cfg, len("hello")+len("world"), chunked)
69+
data, err := body.String()
70+
require.NoError(t, err)
71+
require.Equal(t, "helloworld", data)
72+
73+
data, err = body.String()
74+
require.NoError(t, err)
75+
require.Equal(t, "helloworld", data)
76+
}
77+
78+
t.Run("happy path", func(t *testing.T) {
79+
testHelloworld(t, config.Default())
80+
})
81+
82+
t.Run("cap buffer", func(t *testing.T) {
83+
cfg := config.Default()
84+
cfg.Body.MaxSize = 5
85+
testHelloworld(t, cfg)
86+
})
87+
}
88+
}
89+
90+
t.Run("plain", test(false))
91+
t.Run("chunked", test(true))
92+
})
93+
94+
t.Run("JSON", func(t *testing.T) {
95+
newRequest := func(mime string) *Request {
96+
return &Request{
97+
cfg: config.Default(),
98+
commonHeaders: commonHeaders{
99+
ContentType: mime,
100+
},
101+
}
102+
}
103+
104+
type sampleModel struct {
105+
Hello string `json:"Hello"`
106+
}
107+
108+
const jsonSample = `{"Hello": "world"}`
109+
110+
parseJSON := func(mime, sample string) (sampleModel, error) {
111+
body := newBody(sample)
112+
body.request = newRequest(mime)
113+
114+
var m sampleModel
115+
err := body.JSON(&m)
116+
return m, err
117+
}
118+
119+
t.Run("happy path", func(t *testing.T) {
120+
model, err := parseJSON(mime.JSON, jsonSample)
121+
require.NoError(t, err)
122+
require.Equal(t, "world", model.Hello)
123+
})
124+
125+
t.Run("incompatible mime", func(t *testing.T) {
126+
_, err := parseJSON(mime.HTTP, jsonSample)
127+
require.EqualError(t, err, status.ErrUnsupportedMediaType.Error())
128+
})
129+
})
130+
131+
t.Run("Form", func(t *testing.T) {
132+
newRequest := func(mime string) *Request {
133+
return &Request{
134+
cfg: config.Default(),
135+
commonHeaders: commonHeaders{
136+
ContentType: mime,
137+
},
138+
}
139+
}
140+
141+
t.Run("urlencoded", func(t *testing.T) {
142+
body := newBody("hello=world")
143+
body.request = newRequest(mime.FormUrlencoded)
144+
form, err := body.Form()
145+
require.NoError(t, err)
146+
world, found := form.Name("hello")
147+
require.True(t, found)
148+
require.Equal(t, "world", world.Value)
149+
})
150+
151+
t.Run("multipart", func(t *testing.T) {
152+
body := newBody("--foo\r\nContent-Disposition: form-data; name=foo\r\n\r\nbar\r\n--foo--\r\n")
153+
body.request = newRequest(mime.Multipart + "; boundary=foo")
154+
form, err := body.Form()
155+
require.NoError(t, err)
156+
bar, found := form.Name("foo")
157+
require.True(t, found)
158+
require.Equal(t, "bar", bar.Value)
159+
})
160+
161+
t.Run("incompatible", func(t *testing.T) {
162+
body := newBody("hello")
163+
body.request = newRequest(mime.HTTP)
164+
_, err := body.Form()
165+
require.EqualError(t, err, status.ErrUnsupportedMediaType.Error())
166+
})
167+
})
168+
13169
t.Run("reader", func(t *testing.T) {
14170
data := dummy.NewMockClient([]byte("Hello, world!"))
15171
request := &Request{cfg: config.Default()}

0 commit comments

Comments
 (0)