361 lines
10 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|