feat(lagoon): accept string and url rules in lagoon.Validate (quick-261104-f4x)
- string requires a string value (pointers and named string types included), url a URL matching Laravel's pattern (urlPattern, shared with lagoon.ValidateRequest); both skip an empty or blank value - lagoon::validate.string and .url in the en and pl catalogs, English fallbacks for a nil translator - settings saves whose model rules use nullable|string|max:255 or nullable|url|max:500 no longer fail with an unrecognized-rule error
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package lagoon
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -315,3 +316,99 @@ func TestValidateRequiredZeroDates(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ruleCase is one value under one rule string with the exact messages
|
||||
// lagoon.Validate must report for the field (nil when it passes).
|
||||
type ruleCase struct {
|
||||
name string
|
||||
rule string
|
||||
value any
|
||||
want []string
|
||||
}
|
||||
|
||||
func runRuleCases(t *testing.T, field string, cases []ruleCase) {
|
||||
t.Helper()
|
||||
for _, tc := range cases {
|
||||
errs, err := Validate(t.Context(), nil, nil, map[string]string{field: tc.rule}, map[string]any{field: tc.value}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", tc.name, err)
|
||||
}
|
||||
if got := errs[field]; !slices.Equal(got, tc.want) {
|
||||
t.Fatalf("%s: %q on %#v = %q, want %q", tc.name, tc.rule, tc.value, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestValidateStringRule: string accepts a string, a pointer to one and a
|
||||
// named string type, refuses any other value with Laravel's message, and
|
||||
// skips an empty or blank value, which is required's job.
|
||||
func TestValidateStringRule(t *testing.T) {
|
||||
type label string
|
||||
empty, x := "", "x"
|
||||
const rule = "nullable|string|max:255"
|
||||
notString := []string{"The title must be a string."}
|
||||
runRuleCases(t, "title", []ruleCase{
|
||||
{"plain", rule, "hello", nil},
|
||||
{"nil", rule, nil, nil},
|
||||
{"empty", rule, "", nil},
|
||||
{"blank", rule, " ", nil},
|
||||
{"nil pointer", rule, (*string)(nil), nil},
|
||||
{"pointer to empty", rule, &empty, nil},
|
||||
{"pointer to value", rule, &x, nil},
|
||||
{"named type", rule, label("news"), nil},
|
||||
{"int", rule, 5, notString},
|
||||
{"bool", rule, true, notString},
|
||||
{"slice", rule, []string{"a"}, notString},
|
||||
{"no nullable empty", "string", "", nil},
|
||||
{"no nullable nil", "string", nil, nil},
|
||||
{"no nullable int", "string", 5, notString},
|
||||
{"required empty", "required|string", "", []string{"The title field is required."}},
|
||||
})
|
||||
}
|
||||
|
||||
// TestValidateURLRule: url accepts what Laravel's pattern accepts (the one
|
||||
// lagoon.ValidateRequest uses) and skips an empty or blank value.
|
||||
func TestValidateURLRule(t *testing.T) {
|
||||
empty, nope := "", "nope"
|
||||
const rule = "nullable|url|max:500"
|
||||
invalid := []string{"The website format is invalid."}
|
||||
runRuleCases(t, "website", []ruleCase{
|
||||
{"https", rule, "https://example.com/feed.png", nil},
|
||||
{"localhost port query fragment", rule, "http://localhost:8080/x?y=1#z", nil},
|
||||
{"unicode host and path", rule, "https://przykład.pl/ścieżka", nil},
|
||||
{"nil", rule, nil, nil},
|
||||
{"empty", rule, "", nil},
|
||||
{"blank", rule, " ", nil},
|
||||
{"pointer to empty", rule, &empty, nil},
|
||||
{"words", rule, "not a url", invalid},
|
||||
{"no scheme", rule, "example.com", invalid},
|
||||
{"javascript", rule, "javascript:alert(1)", invalid},
|
||||
{"leading space", rule, " https://example.com", invalid},
|
||||
{"trailing space", rule, "https://example.com ", invalid},
|
||||
{"pointer to non-url", rule, &nope, invalid},
|
||||
{"int", rule, 42, invalid},
|
||||
})
|
||||
}
|
||||
|
||||
// TestValidateStringURLTranslated: with a translator the string and url
|
||||
// failures are the lagoon::validate lines of the request locale.
|
||||
func TestValidateStringURLTranslated(t *testing.T) {
|
||||
tr := requestTranslator(t)
|
||||
cases := []struct {
|
||||
field, rule string
|
||||
value any
|
||||
want string
|
||||
}{
|
||||
{"title", "nullable|string|max:255", 5, "title musi być ciągiem znaków."},
|
||||
{"website", "nullable|url|max:500", "not a url", "Format website jest nieprawidłowy."},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
errs, err := Validate(inLocale("pl"), nil, nil, map[string]string{tc.field: tc.rule}, map[string]any{tc.field: tc.value}, tr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := errs[tc.field]; len(got) != 1 || got[0] != tc.want {
|
||||
t.Fatalf("%s %q = %q, want %q", tc.field, tc.rule, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user