Add comprehensive test suite
This commit is contained in:
243
internal/handlers/handlers_test.go
Normal file
243
internal/handlers/handlers_test.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCustomFilter_IsBlocked(t *testing.T) {
|
||||
cf := NewCustomFilter()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
subdomain string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "exact match - dws",
|
||||
subdomain: "dws",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "exact match - dubey",
|
||||
subdomain: "dubey",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "exact match - tanishq",
|
||||
subdomain: "tanishq",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "exact match - tdubey",
|
||||
subdomain: "tdubey",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "with hyphens - dubey-web",
|
||||
subdomain: "dubey-web",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "with hyphens - tanishq-dubey",
|
||||
subdomain: "tanishq-dubey",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "leet speak - dub3y",
|
||||
subdomain: "dub3y",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "leet speak - t4nishq",
|
||||
subdomain: "t4nishq",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "leet speak - dw5",
|
||||
subdomain: "dw5",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "combined term - dubeydns",
|
||||
subdomain: "dubeydns",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "combined term - dws-ddns",
|
||||
subdomain: "dws-ddns",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "safe subdomain - myhome",
|
||||
subdomain: "myhome",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "safe subdomain - office",
|
||||
subdomain: "office",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "safe subdomain - server01",
|
||||
subdomain: "server01",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "case insensitive - DWS",
|
||||
subdomain: "DWS",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "case insensitive - Tanishq",
|
||||
subdomain: "Tanishq",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "mixed case - DuBeY",
|
||||
subdomain: "DuBeY",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := cf.IsBlocked(tt.subdomain)
|
||||
if got != tt.want {
|
||||
t.Errorf("IsBlocked(%q) = %v, want %v", tt.subdomain, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCustomFilter_normalize(t *testing.T) {
|
||||
cf := NewCustomFilter()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
text string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "lowercase conversion",
|
||||
text: "DuBeY",
|
||||
want: "dubey",
|
||||
},
|
||||
{
|
||||
name: "remove hyphens",
|
||||
text: "dubey-web",
|
||||
want: "dubeyweb",
|
||||
},
|
||||
{
|
||||
name: "remove underscores",
|
||||
text: "dubey_web",
|
||||
want: "dubeyweb",
|
||||
},
|
||||
{
|
||||
name: "remove dots",
|
||||
text: "dubey.web",
|
||||
want: "dubeyweb",
|
||||
},
|
||||
{
|
||||
name: "remove spaces",
|
||||
text: "dubey web",
|
||||
want: "dubeyweb",
|
||||
},
|
||||
{
|
||||
name: "leet speak conversion",
|
||||
text: "dub3y",
|
||||
want: "dubey",
|
||||
},
|
||||
{
|
||||
name: "leet speak conversion - t4nishq",
|
||||
text: "t4nishq",
|
||||
want: "tanishq",
|
||||
},
|
||||
{
|
||||
name: "leet speak conversion - dw5",
|
||||
text: "dw5",
|
||||
want: "dws",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := cf.normalize(tt.text)
|
||||
if got != tt.want {
|
||||
t.Errorf("normalize(%q) = %q, want %q", tt.text, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsValidSubdomain(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
subdomain string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "valid - simple",
|
||||
subdomain: "myhome",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid - with hyphen",
|
||||
subdomain: "my-home",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid - with numbers",
|
||||
subdomain: "home123",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "valid - minimum length",
|
||||
subdomain: "abc",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - too short",
|
||||
subdomain: "ab",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid - too long",
|
||||
subdomain: "thisisaverylongsubdomainthatexceedsthesixtythreecharacterlimitforsubdomains",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid - starts with hyphen",
|
||||
subdomain: "-myhome",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid - ends with hyphen",
|
||||
subdomain: "myhome-",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains special chars",
|
||||
subdomain: "my_home",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid - contains dot",
|
||||
subdomain: "my.home",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "invalid - empty",
|
||||
subdomain: "",
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := isValidSubdomain(tt.subdomain)
|
||||
if got != tt.want {
|
||||
t.Errorf("isValidSubdomain(%q) = %v, want %v", tt.subdomain, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
45
internal/handlers/validation.go
Normal file
45
internal/handlers/validation.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Register custom validators
|
||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
v.RegisterValidation("alphanumdash", alphanumdashValidator)
|
||||
}
|
||||
}
|
||||
|
||||
// alphanumdashValidator validates that a string contains only alphanumeric characters and hyphens
|
||||
var alphanumdashValidator validator.Func = func(fl validator.FieldLevel) bool {
|
||||
value := fl.Field().String()
|
||||
// Allow alphanumeric and hyphens only
|
||||
match := regexp.MustCompile(`^[a-zA-Z0-9-]+$`).MatchString(value)
|
||||
return match
|
||||
}
|
||||
|
||||
// IsValidSubdomainFormat checks if a subdomain string is valid (exported for use in validation)
|
||||
func IsValidSubdomainFormat(subdomain string) bool {
|
||||
// Must be at least 3 characters
|
||||
if len(subdomain) < 3 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Must not exceed 63 characters
|
||||
if len(subdomain) > 63 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Must not start or end with hyphen
|
||||
if strings.HasPrefix(subdomain, "-") || strings.HasSuffix(subdomain, "-") {
|
||||
return false
|
||||
}
|
||||
|
||||
// Must contain only alphanumeric and hyphens
|
||||
return regexp.MustCompile(`^[a-zA-Z0-9-]+$`).MatchString(subdomain)
|
||||
}
|
||||
Reference in New Issue
Block a user