1
0
mirror of https://github.com/volatiletech/authboss.git synced 2024-11-24 08:42:17 +02:00

Move validation implementations to defaults

- Add more interfaces for validation
This commit is contained in:
Aaron L 2018-01-29 16:24:42 -08:00
parent 0e9761ddf5
commit 59b2874bcd
10 changed files with 287 additions and 237 deletions

View File

@ -70,7 +70,7 @@ func (a *Authboss) CurrentUserP(w http.ResponseWriter, r *http.Request) Storer {
if err != nil {
panic(err)
} else if i == nil {
panic(ErrUserFound)
panic(ErrUserNotFound)
}
return i
}

View File

@ -1,4 +1,4 @@
package authboss
package defaults
import (
"fmt"
@ -6,6 +6,7 @@ import (
"unicode"
"github.com/pkg/errors"
"github.com/volatiletech/authboss"
)
var blankRegex = regexp.MustCompile(`^\s*$`)
@ -33,8 +34,8 @@ func (r Rules) Field() string {
// Errors returns an array of errors for each validation error that
// is present in the given string. Returns nil if there are no errors.
func (r Rules) Errors(toValidate string) ErrorList {
errs := make(ErrorList, 0)
func (r Rules) Errors(toValidate string) authboss.ErrorList {
errs := make(authboss.ErrorList, 0)
ln := len(toValidate)
if r.Required && (ln == 0 || blankRegex.MatchString(toValidate)) {

View File

@ -1,4 +1,4 @@
package authboss
package defaults
import (
"regexp"

71
defaults/validation.go Normal file
View File

@ -0,0 +1,71 @@
package defaults
import (
"fmt"
"net/http"
"github.com/volatiletech/authboss"
)
// HTTPFormValidator validates HTTP post type inputs
type HTTPFormValidator struct {
Ruleset []authboss.FieldValidator
ConfirmFields []string
}
// Validate validates a request using the given ruleset.
func (h HTTPFormValidator) Validate(r *http.Request) authboss.ErrorList {
var errList authboss.ErrorList
for _, fieldValidator := range h.Ruleset {
field := fieldValidator.Field()
val := r.FormValue(field)
if errs := fieldValidator.Errors(val); len(errs) > 0 {
errList = append(errList, errs...)
}
}
for i := 0; i < len(h.ConfirmFields)-1; i += 2 {
fmt.Println(h.ConfirmFields)
main := r.FormValue(h.ConfirmFields[i])
if len(main) == 0 {
continue
}
confirm := r.FormValue(h.ConfirmFields[i+1])
if len(confirm) == 0 || main != confirm {
errList = append(errList, FieldError{h.ConfirmFields[i+1], fmt.Errorf("Does not match %s", h.ConfirmFields[i])})
}
}
return errList
}
// FieldError represents an error that occurs during validation and is always
// attached to field on a form.
type FieldError struct {
FieldName string
FieldErr error
}
// NewFieldError literally only exists because of poor name planning
// where name and err can't be exported on the struct due to the method names
func NewFieldError(name string, err error) FieldError {
return FieldError{FieldName: name, FieldErr: err}
}
// Name of the field the error is about
func (f FieldError) Name() string {
return f.FieldName
}
// Err for the field
func (f FieldError) Err() error {
return f.FieldErr
}
// Error in string form
func (f FieldError) Error() string {
return fmt.Sprintf("%s: %v", f.FieldName, f.FieldErr)
}

View File

@ -0,0 +1,74 @@
package defaults
import (
"testing"
"github.com/pkg/errors"
"github.com/volatiletech/authboss"
"github.com/volatiletech/authboss/internal/mocks"
)
func TestValidate(t *testing.T) {
t.Parallel()
req := mocks.Request("POST", "username", "john", "email", "john@john.com")
validator := HTTPFormValidator{
Ruleset: []authboss.FieldValidator{
mocks.FieldValidator{
FieldName: "username",
Errs: authboss.ErrorList{FieldError{"username", errors.New("must be longer than 4")}},
},
mocks.FieldValidator{
FieldName: "missing_field",
Errs: authboss.ErrorList{FieldError{"missing_field", errors.New("Expected field to exist")}},
},
mocks.FieldValidator{
FieldName: "email", Errs: nil,
},
},
}
errList := validator.Validate(req)
errs := errList.Map()
if errs["username"][0] != "must be longer than 4" {
t.Error("Expected a different error for username:", errs["username"][0])
}
if errs["missing_field"][0] != "Expected field to exist" {
t.Error("Expected a different error for missing_field:", errs["missing_field"][0])
}
if _, ok := errs["email"]; ok {
t.Error("Expected no errors for email.")
}
}
func TestValidate_Confirm(t *testing.T) {
t.Parallel()
validator := HTTPFormValidator{
ConfirmFields: []string{"username", "confirmUsername"},
}
req := mocks.Request("POST", "username", "john", "confirmUsername", "johnny")
errs := validator.Validate(req).Map()
if errs["confirmUsername"][0] != "Does not match username" {
t.Error("Expected a different error for confirmUsername:", errs["confirmUsername"][0])
}
req = mocks.Request("POST", "username", "john", "confirmUsername", "john")
errs = validator.Validate(req).Map()
if len(errs) != 0 {
t.Error("Expected no errors:", errs)
}
validator = HTTPFormValidator{
ConfirmFields: []string{"username"},
}
req = mocks.Request("POST", "username", "john", "confirmUsername", "john")
errs = validator.Validate(req).Map()
if len(errs) != 0 {
t.Error("Expected no errors:", errs)
}
}

View File

@ -10,12 +10,11 @@ import (
"time"
"github.com/pkg/errors"
"github.com/volatiletech/authboss"
)
// MockUser represents all possible fields a authboss User may have
type MockUser struct {
// User represents all possible fields a authboss User may have
type User struct {
Username string
Email string
Password string
@ -31,96 +30,97 @@ type MockUser struct {
OAuthExpiry time.Time
}
func (m MockUser) GetUsername(context.Context) (string, error) { return m.Username, nil }
func (m MockUser) GetEmail(context.Context) (string, error) { return m.Email, nil }
func (m MockUser) GetPassword(context.Context) (string, error) { return m.Password, nil }
func (m MockUser) GetRecoverToken(context.Context) (string, error) { return m.RecoverToken, nil }
func (m MockUser) GetRecoverTokenExpiry(context.Context) (time.Time, error) {
func (m User) GetUsername(context.Context) (string, error) { return m.Username, nil }
func (m User) GetEmail(context.Context) (string, error) { return m.Email, nil }
func (m User) GetPassword(context.Context) (string, error) { return m.Password, nil }
func (m User) GetRecoverToken(context.Context) (string, error) { return m.RecoverToken, nil }
func (m User) GetRecoverTokenExpiry(context.Context) (time.Time, error) {
return m.RecoverTokenExpiry, nil
}
func (m MockUser) GetConfirmToken(context.Context) (string, error) { return m.ConfirmToken, nil }
func (m MockUser) GetConfirmed(context.Context) (bool, error) { return m.Confirmed, nil }
func (m MockUser) GetLocked(context.Context) (bool, error) { return m.Locked, nil }
func (m MockUser) GetAttemptNumber(context.Context) (int, error) { return m.AttemptNumber, nil }
func (m MockUser) GetAttemptTime(context.Context) (time.Time, error) {
func (m User) GetConfirmToken(context.Context) (string, error) { return m.ConfirmToken, nil }
func (m User) GetConfirmed(context.Context) (bool, error) { return m.Confirmed, nil }
func (m User) GetLocked(context.Context) (bool, error) { return m.Locked, nil }
func (m User) GetAttemptNumber(context.Context) (int, error) { return m.AttemptNumber, nil }
func (m User) GetAttemptTime(context.Context) (time.Time, error) {
return m.AttemptTime, nil
}
func (m MockUser) GetOAuthToken(context.Context) (string, error) { return m.OAuthToken, nil }
func (m MockUser) GetOAuthRefresh(context.Context) (string, error) { return m.OAuthRefresh, nil }
func (m MockUser) GetOAuthExpiry(context.Context) (time.Time, error) {
func (m User) GetOAuthToken(context.Context) (string, error) { return m.OAuthToken, nil }
func (m User) GetOAuthRefresh(context.Context) (string, error) { return m.OAuthRefresh, nil }
func (m User) GetOAuthExpiry(context.Context) (time.Time, error) {
return m.OAuthExpiry, nil
}
func (m *MockUser) SetUsername(ctx context.Context, username string) error {
func (m *User) SetUsername(ctx context.Context, username string) error {
m.Username = username
return nil
}
func (m *MockUser) SetEmail(ctx context.Context, email string) error {
func (m *User) SetEmail(ctx context.Context, email string) error {
m.Email = email
return nil
}
func (m *MockUser) SetPassword(ctx context.Context, password string) error {
func (m *User) SetPassword(ctx context.Context, password string) error {
m.Password = password
return nil
}
func (m *MockUser) SetRecoverToken(ctx context.Context, recoverToken string) error {
func (m *User) SetRecoverToken(ctx context.Context, recoverToken string) error {
m.RecoverToken = recoverToken
return nil
}
func (m *MockUser) SetRecoverTokenExpiry(ctx context.Context, recoverTokenExpiry time.Time) error {
func (m *User) SetRecoverTokenExpiry(ctx context.Context, recoverTokenExpiry time.Time) error {
m.RecoverTokenExpiry = recoverTokenExpiry
return nil
}
func (m *MockUser) SetConfirmToken(ctx context.Context, confirmToken string) error {
func (m *User) SetConfirmToken(ctx context.Context, confirmToken string) error {
m.ConfirmToken = confirmToken
return nil
}
func (m *MockUser) SetConfirmed(ctx context.Context, confirmed bool) error {
func (m *User) SetConfirmed(ctx context.Context, confirmed bool) error {
m.Confirmed = confirmed
return nil
}
func (m *MockUser) SetLocked(ctx context.Context, locked bool) error {
func (m *User) SetLocked(ctx context.Context, locked bool) error {
m.Locked = locked
return nil
}
func (m *MockUser) SetAttemptNumber(ctx context.Context, attemptNumber int) error {
func (m *User) SetAttemptNumber(ctx context.Context, attemptNumber int) error {
m.AttemptNumber = attemptNumber
return nil
}
func (m *MockUser) SetAttemptTime(ctx context.Context, attemptTime time.Time) error {
func (m *User) SetAttemptTime(ctx context.Context, attemptTime time.Time) error {
m.AttemptTime = attemptTime
return nil
}
func (m *MockUser) SetOAuthToken(ctx context.Context, oAuthToken string) error {
func (m *User) SetOAuthToken(ctx context.Context, oAuthToken string) error {
m.OAuthToken = oAuthToken
return nil
}
func (m *MockUser) SetOAuthRefresh(ctx context.Context, oAuthRefresh string) error {
func (m *User) SetOAuthRefresh(ctx context.Context, oAuthRefresh string) error {
m.OAuthRefresh = oAuthRefresh
return nil
}
func (m *MockUser) SetOAuthExpiry(ctx context.Context, oAuthExpiry time.Time) error {
func (m *User) SetOAuthExpiry(ctx context.Context, oAuthExpiry time.Time) error {
m.OAuthExpiry = oAuthExpiry
return nil
}
// MockStorer should be valid for any module storer defined in authboss.
type MockStoreLoader struct {
Users map[string]*MockUser
// StoreLoader should be valid for any module storer defined in authboss.
type StoreLoader struct {
Users map[string]*User
RMTokens map[string][]string
}
// NewMockStorer constructor
func NewMockStoreLoader() *MockStoreLoader {
return &MockStoreLoader{
Users: make(map[string]*MockUser),
// NewStoreLoader constructor
func NewStoreLoader() *StoreLoader {
return &StoreLoader{
Users: make(map[string]*User),
RMTokens: make(map[string][]string),
}
}
/*
// TODO(aarondl): What is this?
// AddToken for remember me
func (m *MockStorer) AddToken(key, token string) error {
func (m *Storer) AddToken(key, token string) error {
if len(m.AddTokenErr) > 0 {
return errors.New(m.AddTokenErr)
}
@ -131,7 +131,7 @@ func (m *MockStorer) AddToken(key, token string) error {
}
// DelTokens for a user
func (m *MockStorer) DelTokens(key string) error {
func (m *Storer) DelTokens(key string) error {
if len(m.DelTokensErr) > 0 {
return errors.New(m.DelTokensErr)
}
@ -141,7 +141,7 @@ func (m *MockStorer) DelTokens(key string) error {
}
// UseToken if it exists, deleting it in the process
func (m *MockStorer) UseToken(givenKey, token string) (err error) {
func (m *Storer) UseToken(givenKey, token string) (err error) {
if len(m.UseTokenErr) > 0 {
return errors.New(m.UseTokenErr)
}
@ -158,7 +158,7 @@ func (m *MockStorer) UseToken(givenKey, token string) (err error) {
}
// RecoverUser by the token.
func (m *MockStorer) RecoverUser(token string) (result interface{}, err error) {
func (m *Storer) RecoverUser(token string) (result interface{}, err error) {
if len(m.RecoverUserErr) > 0 {
return nil, errors.New(m.RecoverUserErr)
}
@ -166,7 +166,7 @@ func (m *MockStorer) RecoverUser(token string) (result interface{}, err error) {
for _, user := range m.Users {
if user["recover_token"] == token {
u := &MockUser{}
u := &User{}
if err = user.Bind(u, false); err != nil {
panic(err)
}
@ -179,7 +179,7 @@ func (m *MockStorer) RecoverUser(token string) (result interface{}, err error) {
}
// ConfirmUser via their token
func (m *MockStorer) ConfirmUser(confirmToken string) (result interface{}, err error) {
func (m *Storer) ConfirmUser(confirmToken string) (result interface{}, err error) {
if len(m.ConfirmUserErr) > 0 {
return nil, errors.New(m.ConfirmUserErr)
}
@ -187,7 +187,7 @@ func (m *MockStorer) ConfirmUser(confirmToken string) (result interface{}, err e
for _, user := range m.Users {
if user["confirm_token"] == confirmToken {
u := &MockUser{}
u := &User{}
if err = user.Bind(u, false); err != nil {
panic(err)
}
@ -200,34 +200,34 @@ func (m *MockStorer) ConfirmUser(confirmToken string) (result interface{}, err e
}
*/
// MockFailStorer is used for testing module initialize functions that recover more than the base storer
type MockFailStorer struct {
MockUser
// FailStorer is used for testing module initialize functions that recover more than the base storer
type FailStorer struct {
User
}
// Create fails
func (_ MockFailStorer) Create(context.Context) error {
func (FailStorer) Create(context.Context) error {
return errors.New("fail storer: create")
}
// Put fails
func (_ MockFailStorer) Save(context.Context) error {
// Save fails
func (FailStorer) Save(context.Context) error {
return errors.New("fail storer: put")
}
// Get fails
func (_ MockFailStorer) Load(context.Context) error {
// Load fails
func (FailStorer) Load(context.Context) error {
return errors.New("fail storer: get")
}
// MockClientStorer is used for testing the client stores on context
type MockClientStorer struct {
// ClientStorer is used for testing the client stores on context
type ClientStorer struct {
Values map[string]string
GetShouldFail bool
}
// NewMockClientStorer constructs a MockClientStorer
func NewMockClientStorer(data ...string) *MockClientStorer {
// NewClientStorer constructs a ClientStorer
func NewClientStorer(data ...string) *ClientStorer {
if len(data)%2 != 0 {
panic("It should be a key value list of arguments.")
}
@ -238,11 +238,11 @@ func NewMockClientStorer(data ...string) *MockClientStorer {
values[data[i]] = data[i+1]
}
return &MockClientStorer{Values: values}
return &ClientStorer{Values: values}
}
// Get a key's value
func (m *MockClientStorer) Get(key string) (string, bool) {
func (m *ClientStorer) Get(key string) (string, bool) {
if m.GetShouldFail {
return "", false
}
@ -252,7 +252,7 @@ func (m *MockClientStorer) Get(key string) (string, bool) {
}
// GetErr gets a key's value or err if not exist
func (m *MockClientStorer) GetErr(key string) (string, error) {
func (m *ClientStorer) GetErr(key string) (string, error) {
if m.GetShouldFail {
return "", authboss.ClientDataErr{Name: key}
}
@ -265,13 +265,13 @@ func (m *MockClientStorer) GetErr(key string) (string, error) {
}
// Put a value
func (m *MockClientStorer) Put(key, val string) { m.Values[key] = val }
func (m *ClientStorer) Put(key, val string) { m.Values[key] = val }
// Del a key/value pair
func (m *MockClientStorer) Del(key string) { delete(m.Values, key) }
func (m *ClientStorer) Del(key string) { delete(m.Values, key) }
// MockRequest returns a new mock request with optional key-value body (form-post)
func MockRequest(method string, postKeyValues ...string) *http.Request {
// Request returns a new request with optional key-value body (form-post)
func Request(method string, postKeyValues ...string) *http.Request {
var body io.Reader
location := "http://localhost"
@ -300,19 +300,19 @@ func MockRequest(method string, postKeyValues ...string) *http.Request {
return req
}
// MockMailer helps simplify mailer testing by storing the last sent email
type MockMailer struct {
// Mailer helps simplify mailer testing by storing the last sent email
type Mailer struct {
Last authboss.Email
SendErr string
}
// NewMockMailer constructs a mock mailer
func NewMockMailer() *MockMailer {
return &MockMailer{}
// NewMailer constructs a mailer
func NewMailer() *Mailer {
return &Mailer{}
}
// Send an e-mail
func (m *MockMailer) Send(ctx context.Context, email authboss.Email) error {
func (m *Mailer) Send(ctx context.Context, email authboss.Email) error {
if len(m.SendErr) > 0 {
return errors.New(m.SendErr)
}
@ -321,15 +321,15 @@ func (m *MockMailer) Send(ctx context.Context, email authboss.Email) error {
return nil
}
// MockAfterCallback is a callback that knows if it was called
type MockAfterCallback struct {
// AfterCallback is a callback that knows if it was called
type AfterCallback struct {
HasBeenCalled bool
Fn authboss.After
}
// NewMockAfterCallback constructs a new mockaftercallback.
func NewMockAfterCallback() *MockAfterCallback {
m := MockAfterCallback{}
// NewAfterCallback constructs a new aftercallback.
func NewAfterCallback() *AfterCallback {
m := AfterCallback{}
m.Fn = func(context.Context) error {
m.HasBeenCalled = true
@ -338,3 +338,25 @@ func NewMockAfterCallback() *MockAfterCallback {
return &m
}
// FieldValidator mock
type FieldValidator struct {
FieldName string
Errs []error
Ruleset []string
}
// Field being validated
func (f FieldValidator) Field() string {
return f.FieldName
}
// Errors list
func (f FieldValidator) Errors(in string) []error {
return f.Errs
}
// Rules present
func (f FieldValidator) Rules() []string {
return f.Ruleset
}

View File

@ -11,6 +11,7 @@ import (
type DefaultLogger log.Logger
// LogWriteMaker is used to create a logger from an http request.
// TODO(aarondl): decide what to do with this, should we keep it?
type LogWriteMaker func(http.ResponseWriter, *http.Request) io.Writer
// NewDefaultLogger creates a logger to stdout.

View File

@ -168,24 +168,6 @@ func newMockAPIRequest(postKeyValues ...string) *http.Request {
return req
}
type mockValidator struct {
FieldName string
Errs ErrorList
Ruleset []string
}
func (m mockValidator) Field() string {
return m.FieldName
}
func (m mockValidator) Errors(in string) ErrorList {
return m.Errs
}
func (m mockValidator) Rules() []string {
return m.Ruleset
}
type mockRenderLoader struct{}
func (m mockRenderLoader) Init(names []string) (Renderer, error) {

View File

@ -2,8 +2,6 @@ package authboss
import (
"bytes"
"fmt"
"net/http"
)
const (
@ -11,14 +9,27 @@ const (
ConfirmPrefix = "confirm_"
)
// Validator is anything that can validate a string and provide a list of errors
// and describe its set of rules.
// Validator takes a form name and a set of inputs and returns any validation errors
// for the inputs.
type Validator interface {
// Validate inputs from the named form
Validate(name string, fieldValues map[string]string) []error
}
// FieldValidator is anything that can validate a string and provide a list of errors
// and describe its set of rules.
type FieldValidator interface {
Field() string
Errors(in string) ErrorList
Errors(in string) []error
Rules() []string
}
// FieldError describes an error on a field
type FieldError interface {
Name() string
Err() error
}
// ErrorList is simply a slice of errors with helpers.
type ErrorList []error
@ -46,64 +57,10 @@ func (e ErrorList) Map() map[string][]string {
if !ok {
m[""] = append(m[""], err.Error())
} else {
m[fieldErr.Name] = append(m[fieldErr.Name], fieldErr.Err.Error())
name, err := fieldErr.Name(), fieldErr.Err()
m[name] = append(m[name], err.Error())
}
}
return m
}
// FieldError represents an error that occurs during validation and is always
// attached to field on a form.
type FieldError struct {
Name string
Err error
}
func (f FieldError) Error() string {
return fmt.Sprintf("%s: %v", f.Name, f.Err)
}
// Validate validates a request using the given ruleset.
func Validate(r *http.Request, ruleset []Validator, confirmFields ...string) ErrorList {
errList := make(ErrorList, 0)
for _, validator := range ruleset {
field := validator.Field()
val := r.FormValue(field)
if errs := validator.Errors(val); len(errs) > 0 {
errList = append(errList, errs...)
}
}
for i := 0; i < len(confirmFields)-1; i += 2 {
main := r.FormValue(confirmFields[i])
if len(main) == 0 {
continue
}
confirm := r.FormValue(confirmFields[i+1])
if len(confirm) == 0 || main != confirm {
errList = append(errList, FieldError{confirmFields[i+1], fmt.Errorf("Does not match %s", confirmFields[i])})
}
}
return errList
}
// FilterValidators returns a subset of registered validators
func FilterValidators(validators []Validator, fields ...string) []Validator {
var arr []Validator
for _, validator := range validators {
fieldName := validator.Field()
for _, field := range fields {
if fieldName == field {
arr = append(arr, validator)
}
}
}
return arr
}

View File

@ -6,6 +6,23 @@ import (
"github.com/pkg/errors"
)
type mockFieldError struct {
name string
err error
}
func (m mockFieldError) Name() string {
return m.name
}
func (m mockFieldError) Err() error {
return m.err
}
func (m mockFieldError) Error() string {
return m.err.Error()
}
func TestErrorList_Error(t *testing.T) {
t.Parallel()
@ -23,9 +40,9 @@ func TestErrorList_Map(t *testing.T) {
errAsploded := "asploded"
errList := ErrorList{
FieldError{StoreUsername, errors.New(errNotLong)},
FieldError{StoreUsername, errors.New(errEmail)},
FieldError{StorePassword, errors.New(errNotLong)},
mockFieldError{"username", errors.New(errNotLong)},
mockFieldError{"username", errors.New(errEmail)},
mockFieldError{"password", errors.New(errNotLong)},
errors.New(errAsploded),
}
@ -34,7 +51,7 @@ func TestErrorList_Map(t *testing.T) {
t.Error("Wrong number of fields:", len(m))
}
usernameErrs := m[StoreUsername]
usernameErrs := m["email"]
if len(usernameErrs) != 2 {
t.Error("Wrong number of username errors:", len(usernameErrs))
}
@ -45,7 +62,7 @@ func TestErrorList_Map(t *testing.T) {
t.Error("Wrong username error at 1:", usernameErrs[1])
}
passwordErrs := m[StorePassword]
passwordErrs := m["password"]
if len(passwordErrs) != 1 {
t.Error("Wrong number of password errors:", len(passwordErrs))
}
@ -61,78 +78,3 @@ func TestErrorList_Map(t *testing.T) {
t.Error("Wrong unkown error at 0:", unknownErrs[0])
}
}
func TestValidate(t *testing.T) {
t.Parallel()
req := newMockRequest(StoreUsername, "john", StoreEmail, "john@john.com")
errList := Validate(req, []Validator{
mockValidator{
FieldName: StoreUsername,
Errs: ErrorList{FieldError{StoreUsername, errors.New("must be longer than 4")}},
},
mockValidator{
FieldName: "missing_field",
Errs: ErrorList{FieldError{"missing_field", errors.New("Expected field to exist.")}},
},
mockValidator{
FieldName: StoreEmail, Errs: nil,
},
})
errs := errList.Map()
if errs[StoreUsername][0] != "must be longer than 4" {
t.Error("Expected a different error for username:", errs[StoreUsername][0])
}
if errs["missing_field"][0] != "Expected field to exist." {
t.Error("Expected a different error for missing_field:", errs["missing_field"][0])
}
if _, ok := errs[StoreEmail]; ok {
t.Error("Expected no errors for email.")
}
}
func TestValidate_Confirm(t *testing.T) {
t.Parallel()
req := newMockRequest(StoreUsername, "john", "confirmUsername", "johnny")
errs := Validate(req, nil, StoreUsername, "confirmUsername").Map()
if errs["confirmUsername"][0] != "Does not match username" {
t.Error("Expected a different error for confirmUsername:", errs["confirmUsername"][0])
}
req = newMockRequest(StoreUsername, "john", "confirmUsername", "john")
errs = Validate(req, nil, StoreUsername, "confirmUsername").Map()
if len(errs) != 0 {
t.Error("Expected no errors:", errs)
}
req = newMockRequest(StoreUsername, "john", "confirmUsername", "john")
errs = Validate(req, nil, StoreUsername).Map()
if len(errs) != 0 {
t.Error("Expected no errors:", errs)
}
}
func TestFilterValidators(t *testing.T) {
t.Parallel()
validators := []Validator{
mockValidator{
FieldName: StoreUsername, Errs: ErrorList{FieldError{StoreUsername, errors.New("must be longer than 4")}},
},
mockValidator{
FieldName: StorePassword, Errs: ErrorList{FieldError{StorePassword, errors.New("must be longer than 4")}},
},
}
validators = FilterValidators(validators, StoreUsername)
if len(validators) != 1 {
t.Error("Expected length to be 1")
}
if validators[0].Field() != StoreUsername {
t.Error("Expcted validator for field username", validators[0].Field())
}
}