Files
dnshelper/hostfile/managed_test.go
T

361 lines
10 KiB
Go

package hostfile_test
import (
"strings"
"testing"
"ekdns/hostfile"
)
// ---------------------------------------------------------------------------
// ParseManagedBlock tests
// ---------------------------------------------------------------------------
func TestParseManagedBlock_NoManagedBlock(t *testing.T) {
lines := []string{
"127.0.0.1 localhost",
"# some comment",
"1.2.3.4 example.com",
}
prefix, managed, postfix, has, err := hostfile.ParseManagedBlock(lines)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if has {
t.Fatal("expected HasManagedBlock=false")
}
if len(managed) != 0 {
t.Errorf("expected empty managed, got %v", managed)
}
if len(postfix) != 0 {
t.Errorf("expected empty postfix, got %v", postfix)
}
if len(prefix) != len(lines) {
t.Errorf("expected all lines in prefix, got %v", prefix)
}
}
func TestParseManagedBlock_ValidBlock(t *testing.T) {
lines := []string{
"127.0.0.1 localhost",
hostfile.StartMarker,
"1.1.1.1\thost1",
"2.2.2.2\thost2",
hostfile.EndMarker,
"# trailing comment",
}
prefix, managed, postfix, has, err := hostfile.ParseManagedBlock(lines)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !has {
t.Fatal("expected HasManagedBlock=true")
}
if len(prefix) != 1 || prefix[0] != "127.0.0.1 localhost" {
t.Errorf("unexpected prefix: %v", prefix)
}
if len(managed) != 2 {
t.Errorf("expected 2 managed lines, got %v", managed)
}
if len(postfix) != 1 || postfix[0] != "# trailing comment" {
t.Errorf("unexpected postfix: %v", postfix)
}
}
func TestParseManagedBlock_StartMarkerAtLine0(t *testing.T) {
lines := []string{
hostfile.StartMarker,
"1.1.1.1\thost1",
hostfile.EndMarker,
}
prefix, managed, postfix, has, err := hostfile.ParseManagedBlock(lines)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !has {
t.Fatal("expected HasManagedBlock=true")
}
if len(prefix) != 0 {
t.Errorf("expected empty prefix when start marker is at line 0, got %v", prefix)
}
if len(managed) != 1 || managed[0] != "1.1.1.1\thost1" {
t.Errorf("unexpected managed: %v", managed)
}
if len(postfix) != 0 {
t.Errorf("expected empty postfix, got %v", postfix)
}
}
func TestParseManagedBlock_CorruptStartWithoutEnd(t *testing.T) {
lines := []string{
"127.0.0.1 localhost",
hostfile.StartMarker,
"1.1.1.1\thost1",
}
_, _, _, _, err := hostfile.ParseManagedBlock(lines)
if err == nil {
t.Fatal("expected error for start marker without end marker")
}
if !strings.Contains(err.Error(), "start marker found") {
t.Errorf("error should mention 'start marker found', got: %v", err)
}
if !strings.Contains(err.Error(), "no end marker") {
t.Errorf("error should mention 'no end marker', got: %v", err)
}
}
func TestParseManagedBlock_CorruptEndWithoutStart(t *testing.T) {
lines := []string{
"127.0.0.1 localhost",
"1.1.1.1\thost1",
hostfile.EndMarker,
}
_, _, _, _, err := hostfile.ParseManagedBlock(lines)
if err == nil {
t.Fatal("expected error for end marker without start marker")
}
if !strings.Contains(err.Error(), "end marker found") {
t.Errorf("error should mention 'end marker found', got: %v", err)
}
if !strings.Contains(err.Error(), "no start marker") {
t.Errorf("error should mention 'no start marker', got: %v", err)
}
}
func TestParseManagedBlock_CorruptEndBeforeStart(t *testing.T) {
lines := []string{
"127.0.0.1 localhost",
hostfile.EndMarker,
"1.1.1.1\thost1",
hostfile.StartMarker,
}
_, _, _, _, err := hostfile.ParseManagedBlock(lines)
if err == nil {
t.Fatal("expected error for end marker before start marker")
}
if !strings.Contains(err.Error(), "end marker") {
t.Errorf("error should mention 'end marker', got: %v", err)
}
if !strings.Contains(err.Error(), "before start marker") {
t.Errorf("error should mention 'before start marker', got: %v", err)
}
}
func TestParseManagedBlock_CorruptDuplicateMarkers(t *testing.T) {
lines := []string{
hostfile.StartMarker,
"1.1.1.1\thost1",
hostfile.EndMarker,
hostfile.StartMarker, // duplicate
"2.2.2.2\thost2",
hostfile.EndMarker,
}
_, _, _, _, err := hostfile.ParseManagedBlock(lines)
if err == nil {
t.Fatal("expected error for duplicate markers")
}
if !strings.Contains(err.Error(), "duplicate") {
t.Errorf("error should mention 'duplicate', got: %v", err)
}
}
// ---------------------------------------------------------------------------
// AddEntries tests
// ---------------------------------------------------------------------------
func TestAddEntries_AddsNewEntries(t *testing.T) {
existing := []string{"1.1.1.1\thost1"}
entries := []hostfile.DNSEntry{{IP: "2.2.2.2", Hostname: "host2"}}
result := hostfile.AddEntries(existing, entries)
if len(result) != 2 {
t.Fatalf("expected 2 entries, got %d: %v", len(result), result)
}
if result[0] != "1.1.1.1\thost1" {
t.Errorf("unexpected entry[0]: %q", result[0])
}
if result[1] != "2.2.2.2\thost2" {
t.Errorf("unexpected entry[1]: %q", result[1])
}
}
func TestAddEntries_ReplacesExistingHostname(t *testing.T) {
existing := []string{"1.1.1.1\thost1", "2.2.2.2\thost2"}
// Replace host1 with a new IP
entries := []hostfile.DNSEntry{{IP: "9.9.9.9", Hostname: "host1"}}
result := hostfile.AddEntries(existing, entries)
// host1 should be replaced, host2 preserved
if len(result) != 2 {
t.Fatalf("expected 2 entries, got %d: %v", len(result), result)
}
found := false
for _, line := range result {
if strings.Contains(line, "1.1.1.1") && strings.Contains(line, "host1") {
t.Errorf("old entry for host1 should have been replaced: %v", result)
}
if strings.Contains(line, "9.9.9.9") && strings.Contains(line, "host1") {
found = true
}
}
if !found {
t.Errorf("new entry for host1 not found: %v", result)
}
}
func TestAddEntries_Deduplicates(t *testing.T) {
existing := []string{"1.1.1.1\thost1"}
entries := []hostfile.DNSEntry{
{IP: "2.2.2.2", Hostname: "host2"},
{IP: "2.2.2.2", Hostname: "host2"}, // duplicate
}
result := hostfile.AddEntries(existing, entries)
count := 0
for _, line := range result {
if line == "2.2.2.2\thost2" {
count++
}
}
if count != 1 {
t.Errorf("expected exactly 1 host2 entry after dedup, got %d: %v", count, result)
}
}
// ---------------------------------------------------------------------------
// AssembleContent tests
// ---------------------------------------------------------------------------
func TestAssembleContent_WithManagedBlock(t *testing.T) {
prefix := []string{"127.0.0.1 localhost"}
managed := []string{"1.1.1.1\thost1"}
postfix := []string{"# end"}
result := hostfile.AssembleContent(prefix, managed, postfix)
expected := []string{
"127.0.0.1 localhost",
hostfile.StartMarker,
"1.1.1.1\thost1",
hostfile.EndMarker,
"# end",
}
if len(result) != len(expected) {
t.Fatalf("expected %d lines, got %d: %v", len(expected), len(result), result)
}
for i, line := range expected {
if result[i] != line {
t.Errorf("line %d: expected %q, got %q", i, line, result[i])
}
}
}
func TestAssembleContent_EmptyManagedOmitsBlock(t *testing.T) {
prefix := []string{"127.0.0.1 localhost"}
postfix := []string{"# end"}
result := hostfile.AssembleContent(prefix, nil, postfix)
if len(result) != 2 {
t.Fatalf("expected 2 lines (no managed block), got %d: %v", len(result), result)
}
for _, line := range result {
if strings.Contains(line, "ekDNSHelper") {
t.Errorf("managed block markers should be absent when managed is empty: %v", result)
}
}
}
// ---------------------------------------------------------------------------
// RemoveByHostname tests (T018)
// ---------------------------------------------------------------------------
func TestRemoveByHostname_SingleHost(t *testing.T) {
existing := []string{
"1.1.1.1\thost1",
"2.2.2.2\thost2",
}
result := hostfile.RemoveByHostname(existing, []string{"host1"})
if len(result) != 1 {
t.Fatalf("expected 1 entry, got %d: %v", len(result), result)
}
if result[0] != "2.2.2.2\thost2" {
t.Errorf("expected host2 entry to remain, got %q", result[0])
}
}
func TestRemoveByHostname_MultipleHosts(t *testing.T) {
existing := []string{
"1.1.1.1\thost1",
"2.2.2.2\thost2",
"3.3.3.3\thost3",
}
result := hostfile.RemoveByHostname(existing, []string{"host1", "host2"})
if len(result) != 1 {
t.Fatalf("expected 1 entry, got %d: %v", len(result), result)
}
if result[0] != "3.3.3.3\thost3" {
t.Errorf("expected host3 entry to remain, got %q", result[0])
}
}
func TestRemoveByHostname_HostNotFound(t *testing.T) {
existing := []string{
"1.1.1.1\thost1",
}
result := hostfile.RemoveByHostname(existing, []string{"host2"})
if len(result) != 1 {
t.Fatalf("expected content unchanged (1 entry), got %d: %v", len(result), result)
}
if result[0] != "1.1.1.1\thost1" {
t.Errorf("expected host1 entry unchanged, got %q", result[0])
}
}
func TestRemoveByHostname_AllHostsRemoved(t *testing.T) {
existing := []string{
"1.1.1.1\thost1",
}
result := hostfile.RemoveByHostname(existing, []string{"host1"})
if len(result) != 0 {
t.Errorf("expected empty slice, got %d entries: %v", len(result), result)
}
}
func TestRemoveByHostname_RegressionNoDuplicates(t *testing.T) {
// This is the R-004 regression test. The legacy code's loop-order bug
// produced duplicates when removing multiple hosts at once.
existing := []string{
"1.1.1.1\thost1",
"2.2.2.2\thost2",
"3.3.3.3\thost3",
}
result := hostfile.RemoveByHostname(existing, []string{"host1", "host2"})
if len(result) != 1 {
t.Fatalf("expected exactly 1 entry after multi-host removal, got %d: %v", len(result), result)
}
if result[0] != "3.3.3.3\thost3" {
t.Errorf("expected only host3 to remain, got %q", result[0])
}
}
// ---------------------------------------------------------------------------
// RemoveAll tests (T018)
// ---------------------------------------------------------------------------
func TestRemoveAll_ReturnEmptySlice(t *testing.T) {
result := hostfile.RemoveAll()
if len(result) != 0 {
t.Errorf("expected empty result, got %d entries: %v", len(result), result)
}
}
func TestRemoveAll_AssembleContentOmitsBlock(t *testing.T) {
// When RemoveAll result is passed to AssembleContent, no managed block markers
// should appear in the assembled output.
prefix := []string{"127.0.0.1 localhost"}
postfix := []string{"# trailing"}
result := hostfile.AssembleContent(prefix, hostfile.RemoveAll(), postfix)
if len(result) != 2 {
t.Fatalf("expected 2 lines with no managed block, got %d: %v", len(result), result)
}
for _, line := range result {
if strings.Contains(line, "ekDNSHelper") {
t.Errorf("managed block markers should be absent after RemoveAll: %v", result)
}
}
}