Skip to content

Commit

Permalink
feat(tdhttp): allow usage of td.Q when using PostForm helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
Cédric Roussel committed Oct 20, 2022
1 parent 34efbcd commit 3664cbd
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 6 deletions.
2 changes: 2 additions & 0 deletions helpers/tdhttp/q.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ import (
// - pointer on any type above, plus any or any other pointer
type Q map[string]any

var _ URLValuesEncoder = Q(nil)

// AddTo adds the q contents to qp.
func (q Q) AddTo(qp url.Values) error {
for param, value := range q {
Expand Down
21 changes: 18 additions & 3 deletions helpers/tdhttp/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,14 @@ func post(target string, body io.Reader, headersQueryParams ...any) (*http.Reque
return newRequest(http.MethodPost, target, body, headersQueryParams)
}

func postForm(target string, data url.Values, headersQueryParams ...any) (*http.Request, error) {
func postForm(target string, data URLValuesEncoder, headersQueryParams ...any) (*http.Request, error) {
var body string
if data != nil {
body = data.Encode()
}

return newRequest(
http.MethodPost, target, strings.NewReader(data.Encode()),
http.MethodPost, target, strings.NewReader(body),
append(headersQueryParams, "Content-Type", "application/x-www-form-urlencoded"),
)
}
Expand Down Expand Up @@ -336,6 +341,16 @@ func Post(target string, body io.Reader, headersQueryParams ...any) *http.Reques
return req
}

// URLValuesEncoder is an interface [PostForm] and [TestAPI.PostForm] data
// must implement.
// Encode can be called to generate a "URL encoded" form such as
// ("bar=baz&foo=quux") sorted by key.
//
// [url.Values] and [Q] implement this interface.
type URLValuesEncoder interface {
Encode() string
}

// PostForm creates a HTTP POST with data's keys and values
// URL-encoded as the request body. "Content-Type" header is
// automatically set to "application/x-www-form-urlencoded". Other
Expand All @@ -351,7 +366,7 @@ func Post(target string, body io.Reader, headersQueryParams ...any) *http.Reques
// )
//
// See [NewRequest] for all possible formats accepted in headersQueryParams.
func PostForm(target string, data url.Values, headersQueryParams ...any) *http.Request {
func PostForm(target string, data URLValuesEncoder, headersQueryParams ...any) *http.Request {
req, err := postForm(target, data, headersQueryParams...)
if err != nil {
panic(err)
Expand Down
45 changes: 44 additions & 1 deletion helpers/tdhttp/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ func TestNewRequest(tt *testing.T) {
"URL": td.String("/path"),
}))

// PostForm
// PostForm - url.Values
t.Cmp(
tdhttp.PostForm("/path",
url.Values{
Expand All @@ -256,6 +256,49 @@ func TestNewRequest(tt *testing.T) {
),
}))

// PostForm - td.Q
t.Cmp(
tdhttp.PostForm("/path",
tdhttp.Q{
"param1": "val1",
"param2": "val2",
},
"Foo", "Bar"),
td.Struct(
&http.Request{
Method: "POST",
Header: http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded"},
"Foo": []string{"Bar"},
},
},
td.StructFields{
"URL": td.String("/path"),
"Body": td.Smuggle(
io.ReadAll,
[]byte("param1=val1&param2=val2"),
),
}))

// PostForm - nil data
t.Cmp(
tdhttp.PostForm("/path", nil, "Foo", "Bar"),
td.Struct(
&http.Request{
Method: "POST",
Header: http.Header{
"Content-Type": []string{"application/x-www-form-urlencoded"},
"Foo": []string{"Bar"},
},
},
td.StructFields{
"URL": td.String("/path"),
"Body": td.Smuggle(
io.ReadAll,
[]byte{},
),
}))

// PostMultipartFormData
req := tdhttp.PostMultipartFormData("/path",
&tdhttp.MultipartBody{
Expand Down
3 changes: 1 addition & 2 deletions helpers/tdhttp/test_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"reflect"
"runtime"
"strings"
Expand Down Expand Up @@ -262,7 +261,7 @@ func (ta *TestAPI) Post(target string, body io.Reader, headersQueryParams ...any
// Note that [TestAPI.Failed] status is reset just after this call.
//
// See [NewRequest] for all possible formats accepted in headersQueryParams.
func (ta *TestAPI) PostForm(target string, data url.Values, headersQueryParams ...any) *TestAPI {
func (ta *TestAPI) PostForm(target string, data URLValuesEncoder, headersQueryParams ...any) *TestAPI {
ta.t.Helper()
req, err := postForm(target, data, headersQueryParams...)
if err != nil {
Expand Down
15 changes: 15 additions & 0 deletions helpers/tdhttp/test_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,21 @@ func TestNewTestAPI(t *testing.T) {
Failed())
td.CmpEmpty(t, mockT.LogBuf())

mockT = tdutil.NewT("test")
td.CmpFalse(t,
tdhttp.NewTestAPI(mockT, mux).
PostForm("/any", tdhttp.Q{"p1": "v1", "p2": "v2"}).
CmpStatus(200).
CmpHeader(containsKey).
CmpBody("POST!\n---\np1=v1&p2=v2").
CmpResponse(td.Code(func(assert *td.T, resp *http.Response) {
assert.Cmp(resp.StatusCode, 200)
assert.Cmp(resp.Header, containsKey)
assert.Smuggle(resp.Body, io.ReadAll, td.String("POST!\n---\np1=v1&p2=v2"))
})).
Failed())
td.CmpEmpty(t, mockT.LogBuf())

mockT = tdutil.NewT("test")
td.CmpFalse(t,
tdhttp.NewTestAPI(mockT, mux).
Expand Down

0 comments on commit 3664cbd

Please sign in to comment.