Repository navigation
Expand file tree
/
Copy pathrequest_body_test.go
More file actions
169 lines (144 loc) · 5.28 KB
/
Copy pathrequest_body_test.go
File metadata and controls
169 lines (144 loc) · 5.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package httpserver
import (
"bytes"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"io"
"mime/multipart"
"net/http"
"net/url"
"testing"
)
// ============ body tag tests ============
type bodyStruct struct {
Body io.ReadCloser `body:""`
}
func TestBodyTag_Basic_BindsRawBody(t *testing.T) {
captured, rec := doRequest[bodyStruct](t, captureHandler[bodyStruct],
http.MethodPost, "/", withRawBody("text/plain", []byte("raw body content")))
assert.Equal(t, http.StatusOK, rec.Code)
if captured.request.Body == nil {
t.Fatal("Body is nil")
}
data, _ := io.ReadAll(captured.request.Body)
assert.Equal(t, "raw body content", string(data), "Body")
}
type bodyWithContentTypesStruct struct {
Body io.ReadCloser `body:"text/plain application/xml"`
}
func TestBodyTag_WithContentTypes_AcceptsMatching(t *testing.T) {
captured, rec := doRequest[bodyWithContentTypesStruct](t, captureHandler[bodyWithContentTypesStruct],
http.MethodPost, "/", withRawBody("text/plain", []byte("plain text")))
assert.Equal(t, http.StatusOK, rec.Code)
data, _ := io.ReadAll(captured.request.Body)
assert.Equal(t, "plain text", string(data), "Body")
}
func TestBodyTag_WithContentTypes_RejectsNonMatching_415(t *testing.T) {
_, rec := doRequest[bodyWithContentTypesStruct](t, captureHandler[bodyWithContentTypesStruct],
http.MethodPost, "/", withRawBody("application/json", []byte("{}")))
assert.Equal(t, http.StatusUnsupportedMediaType, rec.Code)
}
func TestBodyTag_MultipleContentTypes_AcceptsAny(t *testing.T) {
captured, rec := doRequest[bodyWithContentTypesStruct](t, captureHandler[bodyWithContentTypesStruct],
http.MethodPost, "/", withRawBody("application/xml", []byte("<x/>")))
assert.Equal(t, http.StatusOK, rec.Code)
data, _ := io.ReadAll(captured.request.Body)
assert.Equal(t, "<x/>", string(data), "Body")
}
type bodyConflictFormStruct struct {
Body io.ReadCloser `body:"application/x-www-form-urlencoded"`
Form url.Values `form:"name"`
}
type bodyConflictJsonStruct struct {
Body io.ReadCloser `body:"application/json"`
Data jsonSimpleStruct `json:""`
}
type bodyConflictMultipartStruct struct {
Body io.ReadCloser `body:"multipart/form-data"`
Reader *multipart.Reader `multipart:""`
}
type bodyMultipleStruct struct {
B1 io.ReadCloser `body:""`
B2 io.ReadCloser `body:""`
}
type bodyWrongTypeStruct struct {
Body string `body:""`
}
func TestBodyTag_ConflictsWithForm_Panics(t *testing.T) {
require.Panics(t, func() {
_ = RequestParser(captureHandler[bodyConflictFormStruct])
})
}
func TestBodyTag_ConflictsWithJson_Panics(t *testing.T) {
require.Panics(t, func() {
_ = RequestParser(captureHandler[bodyConflictJsonStruct])
})
}
func TestBodyTag_ConflictsWithMultipart_Panics(t *testing.T) {
require.Panics(t, func() {
_ = RequestParser(captureHandler[bodyConflictMultipartStruct])
})
}
func TestBodyTag_MultipleTags_Panics(t *testing.T) {
require.Panics(t, func() {
_ = RequestParser(captureHandler[bodyMultipleStruct])
})
}
func TestBodyTag_WrongType_Panics(t *testing.T) {
require.Panics(t, func() {
_ = RequestParser(captureHandler[bodyWrongTypeStruct])
})
}
// Test that body tag is used as fallback when no other body tags match
type bodyFallbackStruct struct {
Body io.ReadCloser `body:""`
}
func TestBodyTag_Fallback_WhenNoOtherBodyTagMatches(t *testing.T) {
captured, rec := doRequest[bodyFallbackStruct](t, captureHandler[bodyFallbackStruct],
http.MethodPost, "/", withRawBody("application/octet-stream", []byte("binary data")))
assert.Equal(t, http.StatusOK, rec.Code)
data, _ := io.ReadAll(captured.request.Body)
assert.Equal(t, "binary data", string(data), "Body")
}
func TestBodyTag_BodyReadable(t *testing.T) {
body := bytes.Repeat([]byte("a"), 1024)
captured, rec := doRequest[bodyStruct](t, captureHandler[bodyStruct],
http.MethodPost, "/", withRawBody("text/plain", body))
assert.Equal(t, http.StatusOK, rec.Code)
data, _ := io.ReadAll(captured.request.Body)
assert.Len(t, data, 1024, "Body length")
}
// TestBodyTag_BodyMethodMatrix exercises the raw-body binder across the PUT/
// PATCH/DELETE methods that share the body-parsing case in request_body.go.
func TestBodyTag_BodyMethodMatrix(t *testing.T) {
cases := []struct {
name string
method string
body string
}{
{"PUT", http.MethodPut, "put body"},
{"PATCH", http.MethodPatch, "patch body"},
{"DELETE", http.MethodDelete, "delete body"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
captured, rec := doRequest[bodyStruct](t, captureHandler[bodyStruct],
tc.method, "/", withRawBody("text/plain", []byte(tc.body)))
require.Equal(t, http.StatusOK, rec.Code)
data, _ := io.ReadAll(captured.request.Body)
assert.Equal(t, tc.body, string(data), "Body")
})
}
}
// ============ body tag content-length zero ============
func TestBodyTag_ContentLengthZero_SkipsBinding(t *testing.T) {
captured, rec := doRequest[bodyStruct](t, captureHandler[bodyStruct],
http.MethodPost, "/", withRawBody("text/plain", []byte{}))
assert.Equal(t, http.StatusOK, rec.Code)
assert.Nil(t, captured.request.Body, "Body should be nil when ContentLength is 0")
}