mirror of
https://github.com/lukaszraczylo/lolcathost.git
synced 2026-06-05 23:29:18 +00:00
437 lines
9.2 KiB
Go
437 lines
9.2 KiB
Go
package config
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestValidateDomain(t *testing.T) {
|
|
tests := []struct {
|
|
domain string
|
|
valid bool
|
|
}{
|
|
{"example.com", true},
|
|
{"sub.example.com", true},
|
|
{"my-app.example.com", true},
|
|
{"localhost", true},
|
|
{"a.b.c.d.example.com", true},
|
|
{"example123.com", true},
|
|
|
|
{"", false},
|
|
{"-example.com", false},
|
|
{"example-.com", false},
|
|
{"example.c", false}, // TLD too short
|
|
{"example", false}, // No TLD
|
|
{".example.com", false},
|
|
{"example..com", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.domain, func(t *testing.T) {
|
|
result := ValidateDomain(tt.domain)
|
|
assert.Equal(t, tt.valid, result, "domain: %s", tt.domain)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateIP(t *testing.T) {
|
|
tests := []struct {
|
|
ip string
|
|
valid bool
|
|
}{
|
|
// Valid IPv4
|
|
{"127.0.0.1", true},
|
|
{"192.168.1.1", true},
|
|
{"0.0.0.0", true},
|
|
{"255.255.255.255", true},
|
|
|
|
// Valid IPv6
|
|
{"::1", true},
|
|
{"2001:db8::1", true},
|
|
{"fe80::1", true},
|
|
{"::ffff:192.168.1.1", true},
|
|
|
|
// Invalid
|
|
{"", false},
|
|
{"256.0.0.1", false},
|
|
{"192.168.1", false},
|
|
{"not-an-ip", false},
|
|
{"192.168.1.1.1", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.ip, func(t *testing.T) {
|
|
result := ValidateIP(tt.ip)
|
|
assert.Equal(t, tt.valid, result, "ip: %s", tt.ip)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateAlias(t *testing.T) {
|
|
tests := []struct {
|
|
alias string
|
|
valid bool
|
|
}{
|
|
{"my-alias", true},
|
|
{"myalias", true},
|
|
{"my_alias", true},
|
|
{"alias123", true},
|
|
{"a", true},
|
|
{"a-b_c-d", true},
|
|
|
|
{"", false},
|
|
{"-startswithdash", false},
|
|
{"_startswithunderscore", false},
|
|
{"has spaces", false},
|
|
{"has.dot", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.alias, func(t *testing.T) {
|
|
result := ValidateAlias(tt.alias)
|
|
assert.Equal(t, tt.valid, result, "alias: %s", tt.alias)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsBlockedDomain(t *testing.T) {
|
|
tests := []struct {
|
|
domain string
|
|
blocked bool
|
|
}{
|
|
// Blocked domains
|
|
{"apple.com", true},
|
|
{"icloud.com", true},
|
|
{"sub.apple.com", true},
|
|
{"deep.sub.icloud.com", true},
|
|
{"APPLE.COM", true}, // Case insensitive
|
|
|
|
// Allowed domains
|
|
{"example.com", false},
|
|
{"myapp.com", false},
|
|
{"applestore.com", false}, // Not a subdomain
|
|
{"notapple.com", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.domain, func(t *testing.T) {
|
|
result := IsBlockedDomain(tt.domain)
|
|
assert.Equal(t, tt.blocked, result, "domain: %s", tt.domain)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetBlockedDomains(t *testing.T) {
|
|
domains := GetBlockedDomains()
|
|
assert.NotEmpty(t, domains)
|
|
assert.Contains(t, domains, "apple.com")
|
|
assert.Contains(t, domains, "icloud.com")
|
|
}
|
|
|
|
func TestValidateConfig(t *testing.T) {
|
|
t.Run("valid config", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Settings: Settings{
|
|
AutoApply: true,
|
|
FlushMethod: FlushMethodAuto,
|
|
},
|
|
Groups: []Group{
|
|
{
|
|
Name: "development",
|
|
Hosts: []Host{
|
|
{Domain: "example.com", IP: "127.0.0.1", Alias: "example", Enabled: true},
|
|
},
|
|
},
|
|
},
|
|
Presets: []Preset{
|
|
{Name: "local", Enable: []string{"example"}, Disable: []string{}},
|
|
},
|
|
}
|
|
|
|
err := ValidateConfig(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
t.Run("nil config", func(t *testing.T) {
|
|
err := ValidateConfig(nil)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("invalid flush method", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Settings: Settings{FlushMethod: "invalid"},
|
|
}
|
|
err := ValidateConfig(cfg)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("empty group name", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Groups: []Group{{Name: "", Hosts: []Host{}}},
|
|
}
|
|
err := ValidateConfig(cfg)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("invalid domain", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Groups: []Group{
|
|
{
|
|
Name: "dev",
|
|
Hosts: []Host{
|
|
{Domain: "invalid", IP: "127.0.0.1", Alias: "test", Enabled: true},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err := ValidateConfig(cfg)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("blocked domain", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Groups: []Group{
|
|
{
|
|
Name: "dev",
|
|
Hosts: []Host{
|
|
{Domain: "apple.com", IP: "127.0.0.1", Alias: "test", Enabled: true},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err := ValidateConfig(cfg)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("invalid IP", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Groups: []Group{
|
|
{
|
|
Name: "dev",
|
|
Hosts: []Host{
|
|
{Domain: "example.com", IP: "invalid", Alias: "test", Enabled: true},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err := ValidateConfig(cfg)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("invalid alias", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Groups: []Group{
|
|
{
|
|
Name: "dev",
|
|
Hosts: []Host{
|
|
{Domain: "example.com", IP: "127.0.0.1", Alias: "-invalid", Enabled: true},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err := ValidateConfig(cfg)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("duplicate alias", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Groups: []Group{
|
|
{
|
|
Name: "dev",
|
|
Hosts: []Host{
|
|
{Domain: "a.com", IP: "127.0.0.1", Alias: "same", Enabled: true},
|
|
{Domain: "b.com", IP: "127.0.0.1", Alias: "same", Enabled: true},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
err := ValidateConfig(cfg)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("empty preset name", func(t *testing.T) {
|
|
cfg := &Config{
|
|
Groups: []Group{
|
|
{
|
|
Name: "dev",
|
|
Hosts: []Host{
|
|
{Domain: "example.com", IP: "127.0.0.1", Alias: "test", Enabled: true},
|
|
},
|
|
},
|
|
},
|
|
Presets: []Preset{
|
|
{Name: "", Enable: []string{}},
|
|
},
|
|
}
|
|
err := ValidateConfig(cfg)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("preset with unknown alias is allowed", func(t *testing.T) {
|
|
// Unknown aliases in presets are now allowed (they're simply skipped when applied)
|
|
// This allows presets to survive when hosts are removed from the config
|
|
cfg := &Config{
|
|
Groups: []Group{
|
|
{
|
|
Name: "dev",
|
|
Hosts: []Host{
|
|
{Domain: "example.com", IP: "127.0.0.1", Alias: "test", Enabled: true},
|
|
},
|
|
},
|
|
},
|
|
Presets: []Preset{
|
|
{Name: "local", Enable: []string{"unknown"}},
|
|
},
|
|
}
|
|
err := ValidateConfig(cfg)
|
|
assert.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
func TestValidationError(t *testing.T) {
|
|
err := &ValidationError{Field: "test.field", Message: "test message"}
|
|
assert.Equal(t, "test.field: test message", err.Error())
|
|
}
|
|
|
|
func TestValidateSettings(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
method FlushMethod
|
|
wantErr bool
|
|
}{
|
|
{"auto", FlushMethodAuto, false},
|
|
{"dscacheutil", FlushMethodDscacheutil, false},
|
|
{"killall", FlushMethodKillall, false},
|
|
{"both", FlushMethodBoth, false},
|
|
{"empty", "", false},
|
|
{"invalid", "invalid", true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
settings := &Settings{FlushMethod: tt.method}
|
|
err := validateSettings(settings)
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Matrix testing for domain validation
|
|
func TestValidateDomain_Matrix(t *testing.T) {
|
|
prefixes := []string{"", "sub.", "a.b."}
|
|
domains := []string{"example", "my-app", "test123"}
|
|
tlds := []string{".com", ".io", ".co.uk", ".dev"}
|
|
|
|
for _, prefix := range prefixes {
|
|
for _, domain := range domains {
|
|
for _, tld := range tlds {
|
|
fullDomain := prefix + domain + tld
|
|
t.Run(fullDomain, func(t *testing.T) {
|
|
result := ValidateDomain(fullDomain)
|
|
assert.True(t, result, "expected %s to be valid", fullDomain)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Matrix testing for IP validation
|
|
func TestValidateIP_Matrix(t *testing.T) {
|
|
octets := []string{"0", "127", "192", "255"}
|
|
|
|
for _, o1 := range octets {
|
|
for _, o2 := range octets {
|
|
for _, o3 := range octets {
|
|
for _, o4 := range octets {
|
|
ip := o1 + "." + o2 + "." + o3 + "." + o4
|
|
t.Run(ip, func(t *testing.T) {
|
|
result := ValidateIP(ip)
|
|
assert.True(t, result, "expected %s to be valid", ip)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Benchmark tests
|
|
func BenchmarkValidateDomain(b *testing.B) {
|
|
domains := []string{
|
|
"example.com",
|
|
"sub.example.com",
|
|
"very.long.subdomain.chain.example.com",
|
|
}
|
|
|
|
for _, domain := range domains {
|
|
b.Run(domain, func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
ValidateDomain(domain)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkValidateIP(b *testing.B) {
|
|
ips := []string{
|
|
"127.0.0.1",
|
|
"192.168.1.1",
|
|
"::1",
|
|
"2001:db8::1",
|
|
}
|
|
|
|
for _, ip := range ips {
|
|
b.Run(ip, func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
ValidateIP(ip)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkIsBlockedDomain(b *testing.B) {
|
|
domains := []string{
|
|
"example.com", // not blocked
|
|
"apple.com", // blocked
|
|
"sub.icloud.com", // blocked subdomain
|
|
}
|
|
|
|
for _, domain := range domains {
|
|
b.Run(domain, func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
IsBlockedDomain(domain)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkValidateConfig(b *testing.B) {
|
|
cfg := &Config{
|
|
Settings: Settings{AutoApply: true, FlushMethod: FlushMethodAuto},
|
|
Groups: []Group{
|
|
{
|
|
Name: "development",
|
|
Hosts: []Host{
|
|
{Domain: "a.example.com", IP: "127.0.0.1", Alias: "a", Enabled: true},
|
|
{Domain: "b.example.com", IP: "127.0.0.1", Alias: "b", Enabled: true},
|
|
{Domain: "c.example.com", IP: "127.0.0.1", Alias: "c", Enabled: false},
|
|
},
|
|
},
|
|
},
|
|
Presets: []Preset{
|
|
{Name: "local", Enable: []string{"a", "b"}, Disable: []string{"c"}},
|
|
},
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
err := ValidateConfig(cfg)
|
|
require.NoError(b, err)
|
|
}
|
|
}
|