Repository navigation
Expand file tree
/
Copy pathrequest_form_test.go
More file actions
146 lines (126 loc) · 4.86 KB
/
Copy pathrequest_form_test.go
File metadata and controls
146 lines (126 loc) · 4.86 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
/*
* 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 (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"net/http"
"net/http/httptest"
"net/url"
"testing"
)
// ============ form tag tests ============
type formSingleStruct struct {
Name string `form:"name"`
Age int `form:"age"`
}
// TestFormTag_BodyMethodMatrix exercises the form binder across the POST/PUT/
// PATCH methods that share the body-parsing case in request_body.go.
func TestFormTag_BodyMethodMatrix(t *testing.T) {
cases := []struct {
name string
method string
values url.Values
want formSingleStruct
}{
{"POST", http.MethodPost, url.Values{"name": {"alice"}, "age": {"30"}}, formSingleStruct{Name: "alice", Age: 30}},
{"PUT", http.MethodPut, url.Values{"name": {"bob"}, "age": {"25"}}, formSingleStruct{Name: "bob", Age: 25}},
{"PATCH", http.MethodPatch, url.Values{"name": {"carol"}}, formSingleStruct{Name: "carol"}},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
captured, rec := doRequest[formSingleStruct](t, captureHandler[formSingleStruct],
tc.method, "/", withFormBody(tc.values))
require.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, tc.want.Name, captured.request.Name, "Name")
if tc.want.Age != 0 {
assert.Equal(t, tc.want.Age, captured.request.Age, "Age")
}
})
}
}
func TestFormTag_GET_IgnoresBody(t *testing.T) {
captured, rec := doRequest[formSingleStruct](t, captureHandler[formSingleStruct],
http.MethodGet, "/", withFormBody(url.Values{"name": {"ignored"}}))
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "", captured.request.Name, "Name (GET ignores body)")
}
func TestFormTag_WrongContentType_415(t *testing.T) {
_, rec := doRequest[formSingleStruct](t, captureHandler[formSingleStruct],
http.MethodPost, "/", withRawBody("text/plain", []byte("name=alice")))
assert.Equal(t, http.StatusUnsupportedMediaType, rec.Code)
}
func TestFormTag_ContentLengthZero_SkipsBinding(t *testing.T) {
var captured formSingleStruct
req, _ := http.NewRequest(http.MethodPost, "/", nil)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
rec := httptest.NewRecorder()
asTestHTTPHandler(RequestParser(func(ctx *Context, req formSingleStruct) {
captured = req
ctx.NewResponse(http.StatusOK)
})).ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "", captured.Name, "Name (no body)")
}
type formAllStruct struct {
Values KeyValues `form:""`
}
func TestFormTag_EmptyTag_BindsAllValues(t *testing.T) {
captured, rec := doRequest[formAllStruct](t, captureHandler[formAllStruct],
http.MethodPost, "/", withFormBody(url.Values{"a": {"1"}, "b": {"2"}}))
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, []string{"1"}, captured.request.Values["a"], "Values[a]")
assert.Equal(t, []string{"2"}, captured.request.Values["b"], "Values[b]")
}
type formAllWrongTypeStruct struct {
Values string `form:""`
}
func TestFormTag_EmptyTag_WrongType_Panics(t *testing.T) {
require.Panics(t, func() {
_ = RequestParser(captureHandler[formAllWrongTypeStruct])
})
}
type formMultipleEmptyStruct struct {
F1 KeyValues `form:""`
F2 KeyValues `form:""`
}
func TestFormTag_MultipleEmptyTags_Panics(t *testing.T) {
require.Panics(t, func() {
_ = RequestParser(captureHandler[formMultipleEmptyStruct])
})
}
type formNonEmptyAfterEmptyStruct struct {
All KeyValues `form:""`
Single string `form:"name"`
}
func TestFormTag_NonEmptyAfterEmpty_Panics(t *testing.T) {
require.Panics(t, func() {
_ = RequestParser(captureHandler[formNonEmptyAfterEmptyStruct])
})
}
type formMultipleValuesStruct struct {
IDs []string `form:"id"`
}
func TestFormTag_MultipleValues_SliceField(t *testing.T) {
captured, rec := doRequest[formMultipleValuesStruct](t, captureHandler[formMultipleValuesStruct],
http.MethodPost, "/", withFormBody(url.Values{"id": {"1", "2", "3"}}))
assert.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, []string{"1", "2", "3"}, captured.request.IDs, "IDs")
}
type formAndJsonStruct struct {
Form string `form:"f"`
Json string `json:"j"`
}
// A request type mixing form and json body tags: a form-encoded body binds the
// form field while the json field keeps its zero value — parse selects exactly
// one body binder based on Content-Type and never attempts the other source.
func TestFormTag_WithJsonField_PartialBinding(t *testing.T) {
captured, rec := doRequest[formAndJsonStruct](t, captureHandler[formAndJsonStruct],
http.MethodPost, "/", withFormBody(url.Values{"f": {"form-value"}}))
require.Equal(t, http.StatusOK, rec.Code)
assert.Equal(t, "form-value", captured.request.Form, "Form (from body)")
assert.Equal(t, "", captured.request.Json, "Json (untouched, keeps zero value)")
}