初始提交: Gitea 项目代码

This commit is contained in:
root
2026-05-30 22:47:36 +08:00
commit f288f76350
6116 changed files with 776822 additions and 0 deletions
+245
View File
@@ -0,0 +1,245 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package validation
import (
"fmt"
"io"
"regexp"
"strings"
"gitea.dev/modules/auth"
"gitea.dev/modules/git"
"gitea.dev/modules/glob"
"gitea.dev/modules/json"
"gitea.com/go-chi/binding"
)
const (
// ErrGitRefName is git reference name error
ErrGitRefName = "GitRefNameError"
// ErrGlobPattern is returned when glob pattern is invalid
ErrGlobPattern = "GlobPattern"
// ErrRegexPattern is returned when a regex pattern is invalid
ErrRegexPattern = "RegexPattern"
// ErrUsername is username error
ErrUsername = "UsernameError"
// ErrInvalidGroupTeamMap is returned when a group team mapping is invalid
ErrInvalidGroupTeamMap = "InvalidGroupTeamMap"
// ErrInvalidBadgeSlug is returned when a badge slug is invalid
ErrInvalidBadgeSlug = "InvalidBadgeSlug"
)
type jsonProvider struct{}
func (j jsonProvider) Marshal(v any) ([]byte, error) { return json.Marshal(v) }
func (j jsonProvider) Unmarshal(data []byte, v any) error { return json.Unmarshal(data, v) }
func (j jsonProvider) NewDecoder(reader io.Reader) binding.JSONDecoder {
return json.NewDecoder(reader)
}
func (j jsonProvider) NewEncoder(writer io.Writer) binding.JSONEncoder {
return json.NewEncoder(writer)
}
// AddBindingRules adds additional binding rules
func AddBindingRules() {
binding.JSONProvider = jsonProvider{}
addGitRefNameBindingRule()
addValidURLBindingRule()
addValidSiteURLBindingRule()
addGlobPatternRule()
addRegexPatternRule()
addGlobOrRegexPatternRule()
addUsernamePatternRule()
addValidGroupTeamMapRule()
addSlugPatternRule()
}
func addGitRefNameBindingRule() {
// Git refname validation rule
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "GitRefName"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if !git.IsValidRefPattern(str) {
errs.Add([]string{name}, ErrGitRefName, "GitRefName")
return false, errs
}
return true, errs
},
})
}
func addValidURLBindingRule() {
// URL validation rule
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "ValidUrl"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if len(str) != 0 && !IsValidURL(str) {
errs.Add([]string{name}, binding.ERR_URL, "Url")
return false, errs
}
return true, errs
},
})
}
func addValidSiteURLBindingRule() {
// URL validation rule
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "ValidSiteUrl"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if len(str) != 0 && !IsValidSiteURL(str) {
errs.Add([]string{name}, binding.ERR_URL, "Url")
return false, errs
}
return true, errs
},
})
}
func addSlugPatternRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "BadgeSlug"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if !IsValidBadgeSlug(str) {
errs.Add([]string{name}, ErrInvalidBadgeSlug, "invalid badge slug")
return false, errs
}
return true, errs
},
})
}
func addGlobPatternRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "GlobPattern"
},
IsValid: globPatternValidator,
})
}
func globPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if len(str) != 0 {
if _, err := glob.Compile(str); err != nil {
errs.Add([]string{name}, ErrGlobPattern, err.Error())
return false, errs
}
}
return true, errs
}
func addRegexPatternRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "RegexPattern"
},
IsValid: regexPatternValidator,
})
}
func regexPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if _, err := regexp.Compile(str); err != nil {
errs.Add([]string{name}, ErrRegexPattern, err.Error())
return false, errs
}
return true, errs
}
func addGlobOrRegexPatternRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "GlobOrRegexPattern"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := strings.TrimSpace(fmt.Sprintf("%v", val))
if len(str) >= 2 && strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") {
return regexPatternValidator(errs, name, str[1:len(str)-1])
}
return globPatternValidator(errs, name, val)
},
})
}
func addUsernamePatternRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "Username"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
if !IsValidUsername(str) {
errs.Add([]string{name}, ErrUsername, "invalid username")
return false, errs
}
return true, errs
},
})
}
func addValidGroupTeamMapRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return rule == "ValidGroupTeamMap"
},
IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) {
_, err := auth.UnmarshalGroupTeamMapping(fmt.Sprintf("%v", val))
if err != nil {
errs.Add([]string{name}, ErrInvalidGroupTeamMap, err.Error())
return false, errs
}
return true, errs
},
})
}
func portOnly(hostport string) string {
_, after, ok := strings.Cut(hostport, ":")
if !ok {
return ""
}
if _, after2, ok2 := strings.Cut(hostport, "]:"); ok2 {
return after2
}
if strings.Contains(hostport, "]") {
return ""
}
return after
}
func validPort(p string) bool {
for _, r := range []byte(p) {
if r < '0' || r > '9' {
return false
}
}
return true
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package validation
import (
"net/http"
"net/http/httptest"
"testing"
"gitea.com/go-chi/binding"
chi "github.com/go-chi/chi/v5"
"github.com/stretchr/testify/assert"
)
const (
testRoute = "/test"
)
type (
validationTestCase struct {
description string
data any
expectedErrors binding.Errors
}
TestForm struct {
BranchName string `form:"BranchName" binding:"GitRefName"`
URL string `form:"ValidUrl" binding:"ValidUrl"`
GlobPattern string `form:"GlobPattern" binding:"GlobPattern"`
RegexPattern string `form:"RegexPattern" binding:"RegexPattern"`
}
)
func performValidationTest(t *testing.T, testCase validationTestCase) {
httpRecorder := httptest.NewRecorder()
m := chi.NewRouter()
m.Post(testRoute, func(resp http.ResponseWriter, req *http.Request) {
actual := binding.Validate(req, testCase.data)
// see https://github.com/stretchr/testify/issues/435
if actual == nil {
actual = binding.Errors{}
}
assert.Equal(t, testCase.expectedErrors, actual)
})
req, err := http.NewRequest(http.MethodPost, testRoute, nil)
if err != nil {
panic(err)
}
req.Header.Add("Content-Type", "x-www-form-urlencoded")
m.ServeHTTP(httpRecorder, req)
switch httpRecorder.Code {
case http.StatusNotFound:
panic("Routing is messed up in test fixture (got 404): check methods and paths")
case http.StatusInternalServerError:
panic("Something bad happened on '" + testCase.description + "'")
}
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package validation
import (
"testing"
"gitea.dev/modules/glob"
"gitea.com/go-chi/binding"
)
func getGlobPatternErrorString(pattern string) string {
// It would be unwise to rely on that glob
// compilation errors don't ever change.
if _, err := glob.Compile(pattern); err != nil {
return err.Error()
}
return ""
}
func Test_GlobPatternValidation(t *testing.T) {
AddBindingRules()
globValidationTestCases := []validationTestCase{
{
description: "Empty glob pattern",
data: TestForm{
GlobPattern: "",
},
expectedErrors: binding.Errors{},
},
{
description: "Valid glob",
data: TestForm{
GlobPattern: "{master,release*}",
},
expectedErrors: binding.Errors{},
},
{
description: "Invalid glob",
data: TestForm{
GlobPattern: "[a-",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"GlobPattern"},
Classification: ErrGlobPattern,
Message: getGlobPatternErrorString("[a-"),
},
},
},
}
for _, testCase := range globValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {
performValidationTest(t, testCase)
})
}
}
+110
View File
@@ -0,0 +1,110 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package validation
import (
"net/url"
"regexp"
"slices"
"strings"
"sync"
"gitea.dev/modules/glob"
"gitea.dev/modules/setting"
)
type globalVarsStruct struct {
externalTrackerRegex *regexp.Regexp
validUsernamePattern *regexp.Regexp
invalidUsernamePattern *regexp.Regexp
validBadgeSlugPattern *regexp.Regexp
invalidBadgeSlugPattern *regexp.Regexp
}
var globalVars = sync.OnceValue(func() *globalVarsStruct {
return &globalVarsStruct{
externalTrackerRegex: regexp.MustCompile(`({?)(?:user|repo|index)+?(}?)`),
validUsernamePattern: regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`),
invalidUsernamePattern: regexp.MustCompile(`[-._]{2,}|[-._]$`), // No consecutive or trailing non-alphanumeric chars
validBadgeSlugPattern: regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9._-]*$`),
invalidBadgeSlugPattern: regexp.MustCompile(`[-._]{2,}|[-._]$`),
}
})
// IsValidURL checks if URL is valid
func IsValidURL(uri string) bool {
if u, err := url.ParseRequestURI(uri); err != nil ||
(u.Scheme != "http" && u.Scheme != "https") ||
!validPort(portOnly(u.Host)) {
return false
}
return true
}
// IsValidSiteURL checks if URL is valid
func IsValidSiteURL(uri string) bool {
u, err := url.ParseRequestURI(uri)
if err != nil {
return false
}
if !validPort(portOnly(u.Host)) {
return false
}
return slices.Contains(setting.Service.ValidSiteURLSchemes, u.Scheme)
}
// IsEmailDomainListed checks whether the domain of an email address
// matches a list of domains
func IsEmailDomainListed(globs []glob.Glob, email string) bool {
if len(globs) == 0 {
return false
}
n := strings.LastIndex(email, "@")
if n <= 0 {
return false
}
domain := strings.ToLower(email[n+1:])
for _, g := range globs {
if g.Match(domain) {
return true
}
}
return false
}
// IsValidExternalTrackerURLFormat checks if URL matches required syntax for external trackers
func IsValidExternalTrackerURLFormat(uri string) bool {
if !IsValidURL(uri) {
return false
}
vars := globalVars()
// check for typoed variables like /{index/ or /[repo}
for _, match := range vars.externalTrackerRegex.FindAllStringSubmatch(uri, -1) {
if (match[1] == "{" || match[2] == "}") && (match[1] != "{" || match[2] != "}") {
return false
}
}
return true
}
// IsValidUsername checks if username is valid
func IsValidUsername(name string) bool {
// It is difficult to find a single pattern that is both readable and effective,
// but it's easier to use positive and negative checks.
vars := globalVars()
return vars.validUsernamePattern.MatchString(name) && !vars.invalidUsernamePattern.MatchString(name)
}
func IsValidBadgeSlug(slug string) bool {
vars := globalVars()
return vars.validBadgeSlugPattern.MatchString(slug) && !vars.invalidBadgeSlugPattern.MatchString(slug)
}
+162
View File
@@ -0,0 +1,162 @@
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package validation
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_IsValidURL(t *testing.T) {
cases := []struct {
description string
url string
valid bool
}{
{
description: "Empty URL",
url: "",
valid: false,
},
{
description: "Loopback IPv4 URL",
url: "http://127.0.1.1:5678/",
valid: true,
},
{
description: "Loopback IPv6 URL",
url: "https://[::1]/",
valid: true,
},
{
description: "Missing semicolon after schema",
url: "http//meh/",
valid: false,
},
}
for _, testCase := range cases {
t.Run(testCase.description, func(t *testing.T) {
assert.Equal(t, testCase.valid, IsValidURL(testCase.url))
})
}
}
func Test_IsValidExternalTrackerURLFormat(t *testing.T) {
cases := []struct {
description string
url string
valid bool
}{
{
description: "Correct external tracker URL with all placeholders",
url: "https://github.com/{user}/{repo}/issues/{index}",
valid: true,
},
{
description: "Local external tracker URL with all placeholders",
url: "https://127.0.0.1/{user}/{repo}/issues/{index}",
valid: true,
},
{
description: "External tracker URL with typo placeholder",
url: "https://github.com/{user}/{repo/issues/{index}",
valid: false,
},
{
description: "External tracker URL with typo placeholder",
url: "https://github.com/[user}/{repo/issues/{index}",
valid: false,
},
{
description: "External tracker URL with typo placeholder",
url: "https://github.com/{user}/repo}/issues/{index}",
valid: false,
},
{
description: "External tracker URL missing optional placeholder",
url: "https://github.com/{user}/issues/{index}",
valid: true,
},
{
description: "External tracker URL missing optional placeholder",
url: "https://github.com/{repo}/issues/{index}",
valid: true,
},
{
description: "External tracker URL missing optional placeholder",
url: "https://github.com/issues/{index}",
valid: true,
},
{
description: "External tracker URL missing optional placeholder",
url: "https://github.com/issues/{user}",
valid: true,
},
{
description: "External tracker URL with similar placeholder names test",
url: "https://github.com/user/repo/issues/{index}",
valid: true,
},
}
for _, testCase := range cases {
t.Run(testCase.description, func(t *testing.T) {
assert.Equal(t, testCase.valid, IsValidExternalTrackerURLFormat(testCase.url))
})
}
}
func TestIsValidUsername(t *testing.T) {
tests := []struct {
arg string
want bool
}{
{arg: "a", want: true},
{arg: "abc", want: true},
{arg: "0.b-c", want: true},
{arg: "a.b-c_d", want: true},
{arg: "", want: false},
{arg: ".abc", want: false},
{arg: "abc.", want: false},
{arg: "a..bc", want: false},
{arg: "a...bc", want: false},
{arg: "a.-bc", want: false},
{arg: "a._bc", want: false},
{arg: "a_-bc", want: false},
{arg: "a/bc", want: false},
{arg: "☁️", want: false},
{arg: "-", want: false},
{arg: "--diff", want: false},
{arg: "-im-here", want: false},
{arg: "a space", want: false},
}
for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) {
assert.Equalf(t, tt.want, IsValidUsername(tt.arg), "IsValidUsername(%v)", tt.arg)
})
}
}
func TestIsValidBadgeSlug(t *testing.T) {
tests := []struct {
arg string
want bool
}{
{arg: "badge-1", want: true},
{arg: "badge.slug", want: true},
{arg: "new", want: true},
{arg: "Badge_1", want: true},
{arg: "a..b", want: false},
{arg: "a/b", want: false},
{arg: "Awesome!", want: false},
{arg: "Emoji 💯", want: false},
}
for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) {
assert.Equalf(t, tt.want, IsValidBadgeSlug(tt.arg), "IsValidBadgeSlug(%v)", tt.arg)
})
}
}
+264
View File
@@ -0,0 +1,264 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package validation
import (
"testing"
"gitea.com/go-chi/binding"
)
func Test_GitRefNameValidation(t *testing.T) {
AddBindingRules()
gitRefNameValidationTestCases := []validationTestCase{
{
description: "Reference name contains only characters",
data: TestForm{
BranchName: "test",
},
expectedErrors: binding.Errors{},
},
{
description: "Reference name contains single slash",
data: TestForm{
BranchName: "feature/test",
},
expectedErrors: binding.Errors{},
},
{
description: "Reference name has allowed special characters",
data: TestForm{
BranchName: "debian/1%1.6.0-2",
},
expectedErrors: binding.Errors{},
},
{
description: "Reference name contains backslash",
data: TestForm{
BranchName: "feature\\test",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name starts with dot",
data: TestForm{
BranchName: ".test",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name ends with dot",
data: TestForm{
BranchName: "test.",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name starts with slash",
data: TestForm{
BranchName: "/test",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name ends with slash",
data: TestForm{
BranchName: "test/",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name ends with .lock",
data: TestForm{
BranchName: "test.lock",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name contains multiple consecutive dots",
data: TestForm{
BranchName: "te..st",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name contains multiple consecutive slashes",
data: TestForm{
BranchName: "te//st",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name is single @",
data: TestForm{
BranchName: "@",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name has @{",
data: TestForm{
BranchName: "branch@{",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name has unallowed special character ~",
data: TestForm{
BranchName: "~debian/1%1.6.0-2",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name has unallowed special character *",
data: TestForm{
BranchName: "*debian/1%1.6.0-2",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name has unallowed special character ?",
data: TestForm{
BranchName: "?debian/1%1.6.0-2",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name has unallowed special character ^",
data: TestForm{
BranchName: "^debian/1%1.6.0-2",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name has unallowed special character :",
data: TestForm{
BranchName: "debian:jessie",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name has unallowed special character (whitespace)",
data: TestForm{
BranchName: "debian jessie",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
{
description: "Reference name has unallowed special character [",
data: TestForm{
BranchName: "debian[jessie",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"BranchName"},
Classification: ErrGitRefName,
Message: "GitRefName",
},
},
},
}
for _, testCase := range gitRefNameValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {
performValidationTest(t, testCase)
})
}
}
+59
View File
@@ -0,0 +1,59 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package validation
import (
"regexp"
"testing"
"gitea.com/go-chi/binding"
)
func getRegexPatternErrorString(pattern string) string {
if _, err := regexp.Compile(pattern); err != nil {
return err.Error()
}
return ""
}
func Test_RegexPatternValidation(t *testing.T) {
AddBindingRules()
regexValidationTestCases := []validationTestCase{
{
description: "Empty regex pattern",
data: TestForm{
RegexPattern: "",
},
expectedErrors: binding.Errors{},
},
{
description: "Valid regex",
data: TestForm{
RegexPattern: `(\d{1,3})+`,
},
expectedErrors: binding.Errors{},
},
{
description: "Invalid regex",
data: TestForm{
RegexPattern: "[a-",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"RegexPattern"},
Classification: ErrRegexPattern,
Message: getRegexPatternErrorString("[a-"),
},
},
},
}
for _, testCase := range regexValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {
performValidationTest(t, testCase)
})
}
}
+110
View File
@@ -0,0 +1,110 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package validation
import (
"testing"
"gitea.com/go-chi/binding"
)
func Test_ValidURLValidation(t *testing.T) {
AddBindingRules()
urlValidationTestCases := []validationTestCase{
{
description: "Empty URL",
data: TestForm{
URL: "",
},
expectedErrors: binding.Errors{},
},
{
description: "URL without port",
data: TestForm{
URL: "http://test.lan/",
},
expectedErrors: binding.Errors{},
},
{
description: "URL with port",
data: TestForm{
URL: "http://test.lan:3000/",
},
expectedErrors: binding.Errors{},
},
{
description: "URL with IPv6 address without port",
data: TestForm{
URL: "http://[::1]/",
},
expectedErrors: binding.Errors{},
},
{
description: "URL with IPv6 address with port",
data: TestForm{
URL: "http://[::1]:3000/",
},
expectedErrors: binding.Errors{},
},
{
description: "Invalid URL",
data: TestForm{
URL: "http//test.lan/",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"URL"},
Classification: binding.ERR_URL,
Message: "Url",
},
},
},
{
description: "Invalid schema",
data: TestForm{
URL: "ftp://test.lan/",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"URL"},
Classification: binding.ERR_URL,
Message: "Url",
},
},
},
{
description: "Invalid port",
data: TestForm{
URL: "http://test.lan:3x4/",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"URL"},
Classification: binding.ERR_URL,
Message: "Url",
},
},
},
{
description: "Invalid port with IPv6 address",
data: TestForm{
URL: "http://[::1]:3x4/",
},
expectedErrors: binding.Errors{
binding.Error{
FieldNames: []string{"URL"},
Classification: binding.ERR_URL,
Message: "Url",
},
},
},
}
for _, testCase := range urlValidationTestCases {
t.Run(testCase.description, func(t *testing.T) {
performValidationTest(t, testCase)
})
}
}