binding/common_test.go
KN4CK3R b29e041704 Reworked Required and OmitEmpty (#8)
The old behaviour is inconsistent because this fails:
```go
type TestForm struct {
	Valid string `binding:"Url"`
	Fails string `binding:"Email"`
}

form := &TestForm{
	Valid: "",
	Fails: "",
}
_ := RawValidate(form)

```
Here you need `OmitEmpty` even if the field is not required.

The new logic:

| Empty | Required | Valid |
| -------- | -------- | -------- |
| Yes | Yes | No |
| Yes | No | Yes, all other rules are skipped |
| No | / | Maybe, other rules decide |

In short: Rules are skipped if a field is empty and not required. Thats the normal behaviour in other validation libraries like Yup too.

After this change there are some places in Gitea where we can add form validation to optional fields which is not possible at the moment. Possible changes in:
21465a2ce3/modules/structs/admin_user.go (L31)
21465a2ce3/modules/structs/admin_user.go (L35)
21465a2ce3/modules/structs/org_team.go (L38)
21465a2ce3/modules/validation/binding.go (L90)
21465a2ce3/modules/validation/binding.go (L108)

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
Reviewed-on: go-chi/binding#8
Reviewed-by: 6543 <6543@obermui.de>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: KN4CK3R <kn4ck3r@noreply.gitea.io>
Co-committed-by: KN4CK3R <kn4ck3r@noreply.gitea.io>
2021-06-09 23:32:01 +08:00

125 lines
4.0 KiB
Go
Executable File

// Copyright 2014 Martini Authors
// Copyright 2014 The Macaron Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
package binding
import (
"mime/multipart"
"net/http"
)
// These types are mostly contrived examples, but they're used
// across many test cases. The idea is to cover all the scenarios
// that this binding package might encounter in actual use.
type (
// For basic test cases with a required field
Post struct {
Title string `form:"title" json:"title" binding:"Required"`
Content string `form:"content" json:"content"`
}
// To be used as a nested struct (with a required field)
Person struct {
Name string `form:"name" json:"name" binding:"Required"`
Email string `form:"email" json:"email"`
}
// For advanced test cases: multiple values, embedded
// and nested structs, an ignored field, and single
// and multiple file uploads
BlogPost struct {
Post
Id int `binding:"Required"` // JSON not specified here for test coverage
Ignored string `form:"-" json:"-"`
Ratings []int `form:"rating" json:"ratings"`
Author Person `json:"author"`
Coauthor *Person `json:"coauthor"`
HeaderImage *multipart.FileHeader
Pictures []*multipart.FileHeader `form:"picture"`
unexported string `form:"unexported"`
}
EmbedPerson struct {
*Person
}
SadForm struct {
AlphaDash string `form:"AlphaDash" binding:"AlphaDash"`
AlphaDashDot string `form:"AlphaDashDot" binding:"AlphaDashDot"`
Size string `form:"Size" binding:"Size(1)"`
SizeSlice []string `form:"SizeSlice" binding:"Size(1)"`
MinSize string `form:"MinSize" binding:"MinSize(5)"`
MinSizeSlice []string `form:"MinSizeSlice" binding:"MinSize(5)"`
MaxSize string `form:"MaxSize" binding:"MaxSize(1)"`
MaxSizeSlice []string `form:"MaxSizeSlice" binding:"MaxSize(1)"`
Range int `form:"Range" binding:"Range(1,2)"`
RangeInvalid int `form:"RangeInvalid" binding:"Range(1)"`
Email string `binding:"Email"`
Url string `form:"Url" binding:"Url"`
UrlEmpty string `form:"UrlEmpty" binding:"Url"`
In string `form:"In" binding:"Default(0);In(1,2,3)"`
InInvalid string `form:"InInvalid" binding:"In(1,2,3)"`
NotIn string `form:"NotIn" binding:"NotIn(1,2,3)"`
Include string `form:"Include" binding:"Include(a)"`
Exclude string `form:"Exclude" binding:"Exclude(a)"`
Empty string
}
Group struct {
Name string `json:"name" binding:"Required"`
People []Person `json:"people" binding:"MinSize(1)"`
}
CustomErrorHandle struct {
Rule `binding:"CustomRule"`
}
// The common function signature of the handlers going under test.
handlerFunc func(req *http.Request, obj interface{}) Errors
// Used for testing mapping an interface to the context
// If used (withInterface = true in the testCases), a modeler
// should be mapped to the context as well as BlogPost, meaning
// you can receive a modeler in your application instead of a
// concrete BlogPost.
modeler interface {
Model() string
}
)
func (p Post) Validate(req *http.Request, errs Errors) Errors {
if len(p.Title) < 10 {
errs = append(errs, Error{
FieldNames: []string{"title"},
Classification: "LengthError",
Message: "Life is too short",
})
}
return errs
}
func (p Post) Model() string {
return p.Title
}
func (g Group) Model() string {
return g.Name
}
const (
testRoute = "/test"
formContentType = "application/x-www-form-urlencoded"
)