feat: complete contract-driven business development and MySQL integration
This commit is contained in:
104
contract/policy_test.go
Normal file
104
contract/policy_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package contract
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
apierrors "git.yuntuo.hk/k2safe/go-omni/errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type byteReader struct{ r io.Reader }
|
||||
|
||||
func (r byteReader) Read(p []byte) (int, error) {
|
||||
if len(p) > 1 {
|
||||
p = p[:1]
|
||||
}
|
||||
return r.r.Read(p)
|
||||
}
|
||||
|
||||
func TestBodyPolicyPropagatesOuterLimit(t *testing.T) {
|
||||
for _, limit := range []int64{0, 1, 4} {
|
||||
r := httptest.NewRequest("POST", "/", byteReader{strings.NewReader("{} ")})
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
r.Body = http.MaxBytesReader(httptest.NewRecorder(), r.Body, limit)
|
||||
err := MustNew[struct{}]().BindWithPolicy(r, nil, &struct{}{}, RequestPolicy{Body: "required", UTF8: "reject"})
|
||||
var e *apierrors.APIError
|
||||
if !errors.As(err, &e) || e.Status != 413 {
|
||||
t.Fatalf("limit %d: expected 413, got %v", limit, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStrictRequestPolicy(t *testing.T) {
|
||||
type request struct {
|
||||
Name string `json:"name"`
|
||||
Tags []string `form:"tag,optional"`
|
||||
}
|
||||
b := MustNew[request]()
|
||||
strict := RequestPolicy{UnknownQuery: "reject", UTF8: "reject", Charset: "utf8"}
|
||||
for _, tc := range []struct {
|
||||
name, url, ct, body string
|
||||
valid bool
|
||||
}{
|
||||
{"valid", "/?tag=a&tag=b", "application/json; charset=UTF-8", `{"name":"企业😀"}`, true},
|
||||
{"query", "/?typo=1", "application/json", `{"name":"ok"}`, false},
|
||||
{"charset", "/", "application/json; charset=gbk", `{"name":"ok"}`, false},
|
||||
{"utf8", "/", "application/json", "{\"name\":\"\xff\"}", false},
|
||||
{"truncatedRune", "/", "application/json", "{\"name\":\"\xe4\"}", false},
|
||||
{"tail", "/", "application/json", `{"name":"ok"} {}`, false},
|
||||
{"tailInvalidByte", "/", "application/json", "{\"name\":\"ok\"} \xff", false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
r := httptest.NewRequest("POST", tc.url, byteReader{strings.NewReader(tc.body)})
|
||||
r.Header.Set("Content-Type", tc.ct)
|
||||
var dst request
|
||||
err := b.BindWithPolicy(r, nil, &dst, strict)
|
||||
if (err == nil) != tc.valid {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tc.valid && len(dst.Tags) != 2 {
|
||||
t.Fatal("query slice rejected")
|
||||
}
|
||||
})
|
||||
}
|
||||
// Explicit route overrides inherit the rest of the App policy.
|
||||
r := httptest.NewRequest("POST", "/?typo=1", strings.NewReader(`{"name":"ok"}`))
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
r = r.WithContext(WithRequestPolicy(context.Background(), strict))
|
||||
if err := b.BindWithPolicy(r, nil, &request{}, RequestPolicy{UnknownQuery: "ignore"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBodyPresencePolicies(t *testing.T) {
|
||||
b := MustNew[struct{}]()
|
||||
for _, mode := range []string{"none", "emptyObject", "required", "optional"} {
|
||||
for _, body := range []string{"", "{}", "null", `{"unexpected":1}`, "{} {}", " "} {
|
||||
r := httptest.NewRequest("POST", "/", byteReader{strings.NewReader(body)})
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
err := b.BindWithPolicy(r, nil, &struct{}{}, RequestPolicy{Body: mode, UTF8: "reject"})
|
||||
valid := (mode == "none" && body == "") || (mode != "none" && body == "{}") || (mode == "optional" && body == "")
|
||||
if (err == nil) != valid {
|
||||
t.Fatalf("mode=%s body=%q: %v", mode, body, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
type object struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
r := httptest.NewRequest("POST", "/", bytes.NewReader(nil))
|
||||
if err := MustNew[object]().BindWithPolicy(r, nil, &object{}, RequestPolicy{Body: "optional"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// Chunked/unknown size is still bounded under strict UTF-8 checking.
|
||||
r = httptest.NewRequest("POST", "/", byteReader{strings.NewReader(`{"value":"1234567890"}`)})
|
||||
r.Header.Set("Content-Type", "application/json")
|
||||
if err := MustNew[object]().BindWithPolicy(r, nil, &object{}, RequestPolicy{UTF8: "reject"}, 8); err == nil {
|
||||
t.Fatal("chunked over-limit accepted")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user