test: Add 22 unit tests across dnsmasq, haproxy, services
dnsmasq (6 new, 30.8% → 32%): - Empty instances, multiple instances, nil config, configured IP - No leaked empty entries (address=//, local=//) - Empty fields don't produce broken entries Also fixed a bug: empty Domain/InternalDomain fields in commented-out entries (no LB IP case) now guarded against producing # local=// and # address=// directives. haproxy (7 new): - No HTTPS frontend with no routes - HTTP-only and L4-only route scenarios - CertsDir customization and default - Custom TCP route ports - Health check directive in L7 backends services (8 new, 72.7% → 75.8%): - Not-found errors for Get, Deregister, Update - DeregisterBySource partial match (source AND backend) - Overwrite preserves file count - Empty dir and non-YAML file handling Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package dnsmasq
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -438,3 +439,131 @@ func TestGenerate_MixedServicesAndInstances(t *testing.T) {
|
||||
t.Errorf("unexpected empty local=// in output:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// Test: empty instances list produces base config with no address= entries
|
||||
func TestGenerate_EmptyInstances(t *testing.T) {
|
||||
g := NewConfigGenerator("/tmp/test-dnsmasq.conf")
|
||||
globalCfg := &config.GlobalConfig{}
|
||||
|
||||
out := g.Generate(globalCfg, []config.InstanceConfig{})
|
||||
|
||||
// Should have base config skeleton
|
||||
if !strings.Contains(out, "bind-interfaces") {
|
||||
t.Errorf("expected bind-interfaces in config, got:\n%s", out)
|
||||
}
|
||||
if !strings.Contains(out, "server=1.1.1.1") {
|
||||
t.Errorf("expected upstream DNS server, got:\n%s", out)
|
||||
}
|
||||
// Should not have any address= entries
|
||||
if strings.Contains(out, "address=/") {
|
||||
t.Errorf("expected no address= entries with empty instances, got:\n%s", out)
|
||||
}
|
||||
// Should not have any local=/ entries
|
||||
if strings.Contains(out, "local=/") {
|
||||
t.Errorf("expected no local=/ entries with empty instances, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// Test: multiple instances each get their own entries
|
||||
func TestGenerate_MultipleInstances(t *testing.T) {
|
||||
g := NewConfigGenerator("/tmp/test-dnsmasq.conf")
|
||||
globalCfg := &config.GlobalConfig{}
|
||||
|
||||
instances := []config.InstanceConfig{
|
||||
instanceWith("cloud1.example.com", "internal1.example.com", "10.0.0.1"),
|
||||
instanceWith("cloud2.example.com", "internal2.example.com", "10.0.0.2"),
|
||||
instanceWith("cloud3.example.com", "internal3.example.com", "10.0.0.3"),
|
||||
}
|
||||
|
||||
out := g.Generate(globalCfg, instances)
|
||||
|
||||
for i, inst := range instances {
|
||||
ip := []string{"10.0.0.1", "10.0.0.2", "10.0.0.3"}[i]
|
||||
if !strings.Contains(out, fmt.Sprintf("address=/%s/%s", inst.Cloud.Domain, ip)) {
|
||||
t.Errorf("expected address entry for %s, got:\n%s", inst.Cloud.Domain, out)
|
||||
}
|
||||
if !strings.Contains(out, fmt.Sprintf("address=/%s/%s", inst.Cloud.InternalDomain, ip)) {
|
||||
t.Errorf("expected address entry for %s, got:\n%s", inst.Cloud.InternalDomain, out)
|
||||
}
|
||||
if !strings.Contains(out, fmt.Sprintf("local=/%s/", inst.Cloud.InternalDomain)) {
|
||||
t.Errorf("expected local=/ entry for %s, got:\n%s", inst.Cloud.InternalDomain, out)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test: nil config doesn't panic, uses auto-detect for IP
|
||||
func TestGenerate_NilConfig(t *testing.T) {
|
||||
g := NewConfigGenerator("/tmp/test-dnsmasq.conf")
|
||||
|
||||
// Should not panic with nil config
|
||||
out := g.Generate(nil, []config.InstanceConfig{})
|
||||
|
||||
if !strings.Contains(out, "bind-interfaces") {
|
||||
t.Errorf("expected valid config with nil cfg, got:\n%s", out)
|
||||
}
|
||||
if !strings.Contains(out, "server=1.1.1.1") {
|
||||
t.Errorf("expected upstream DNS server with nil cfg, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// Test: when cfg.Cloud.Dnsmasq.IP is set, uses that instead of auto-detect
|
||||
func TestGenerate_ConfiguredDnsmasqIP(t *testing.T) {
|
||||
g := NewConfigGenerator("/tmp/test-dnsmasq.conf")
|
||||
cfg := &config.GlobalConfig{}
|
||||
cfg.Cloud.Dnsmasq.IP = "192.168.8.100"
|
||||
|
||||
out := g.Generate(cfg, []config.InstanceConfig{})
|
||||
|
||||
if !strings.Contains(out, "listen-address=192.168.8.100") {
|
||||
t.Errorf("expected configured dnsmasq IP in listen-address, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// Test: verify no address=// or local=// entries appear in any test output
|
||||
func TestGenerate_NoLeakedEmptyEntries(t *testing.T) {
|
||||
g := NewConfigGenerator("/tmp/test-dnsmasq.conf")
|
||||
cfg := &config.GlobalConfig{}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
instances []config.InstanceConfig
|
||||
}{
|
||||
{"empty", nil},
|
||||
{"domain_only", []config.InstanceConfig{instanceWith("example.com", "", "10.0.0.1")}},
|
||||
{"internal_only", []config.InstanceConfig{instanceWith("", "internal.example.com", "10.0.0.1")}},
|
||||
{"both", []config.InstanceConfig{instanceWith("example.com", "internal.example.com", "10.0.0.1")}},
|
||||
{"no_lb_ip", []config.InstanceConfig{instanceWith("example.com", "internal.example.com", "")}},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
out := g.Generate(cfg, tc.instances)
|
||||
if strings.Contains(out, "address=//") {
|
||||
t.Errorf("leaked empty address=// in output:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "local=//") {
|
||||
t.Errorf("leaked empty local=// in output:\n%s", out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Test: GenerateInstanceConfig with all fields empty doesn't produce broken entries
|
||||
func TestGenerateInstanceConfig_EmptyFields(t *testing.T) {
|
||||
g := NewConfigGenerator("/tmp/test-dnsmasq.conf")
|
||||
var inst config.InstanceConfig
|
||||
// All fields empty: no domain, no internal domain, no LB IP
|
||||
|
||||
out := g.GenerateInstanceConfig(inst)
|
||||
|
||||
if strings.Contains(out, "address=//") {
|
||||
t.Errorf("empty fields produced broken address=// entry:\n%s", out)
|
||||
}
|
||||
if strings.Contains(out, "local=//") {
|
||||
t.Errorf("empty fields produced broken local=// entry:\n%s", out)
|
||||
}
|
||||
// Should still contain the header comment
|
||||
if !strings.Contains(out, "DNS configuration for instance") {
|
||||
t.Errorf("expected header comment in output, got:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user