168 lines
4.2 KiB
Go
168 lines
4.2 KiB
Go
package peer
|
|
|
|
import (
|
|
"net/netip"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// writeTempHosts creates a temp hosts file with the given content and returns
|
|
// its path.
|
|
func writeTempHosts(t *testing.T, content string) string {
|
|
t.Helper()
|
|
path := filepath.Join(t.TempDir(), "hosts")
|
|
if err := os.WriteFile(path, []byte(content), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
// readManagedSection returns the lines between the begin/end markers for the
|
|
// given localDomain, plus everything outside the section ("outside").
|
|
func readManagedSection(t *testing.T, path, localDomain string) (inside, outside []string) {
|
|
t.Helper()
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
begin := hostsBegin + localDomain
|
|
end := hostsEnd + localDomain
|
|
|
|
inSection := false
|
|
for _, line := range strings.Split(string(raw), "\n") {
|
|
switch {
|
|
case strings.HasPrefix(line, begin):
|
|
inSection = true
|
|
case strings.HasPrefix(line, end):
|
|
inSection = false
|
|
case inSection:
|
|
if f := strings.Join(strings.Fields(line), " "); f != "" {
|
|
inside = append(inside, f)
|
|
}
|
|
default:
|
|
if f := strings.Join(strings.Fields(line), " "); f != "" {
|
|
outside = append(outside, f)
|
|
}
|
|
}
|
|
}
|
|
return inside, outside
|
|
}
|
|
|
|
func peer(name string) *Peer {
|
|
return &Peer{Name: name}
|
|
}
|
|
|
|
func TestUpdateHosts_AddsSection(t *testing.T) {
|
|
path := writeTempHosts(t, "127.0.0.1 localhost\n")
|
|
|
|
peers := map[netip.Addr]*Peer{
|
|
netip.MustParseAddr("10.11.12.1"): peer("hub"),
|
|
netip.MustParseAddr("10.11.12.10"): peer("laptop"),
|
|
}
|
|
|
|
if err := updateHosts(path, "mynet.local", peers); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
inside, outside := readManagedSection(t, path, "mynet.local")
|
|
|
|
sort.Strings(inside)
|
|
want := []string{
|
|
"10.11.12.1 hub.mynet.local",
|
|
"10.11.12.10 laptop.mynet.local",
|
|
}
|
|
if strings.Join(inside, "\n") != strings.Join(want, "\n") {
|
|
t.Errorf("managed section = %v, want %v", inside, want)
|
|
}
|
|
|
|
if !contains(outside, "127.0.0.1 localhost") {
|
|
t.Errorf("original content lost; outside = %v", outside)
|
|
}
|
|
}
|
|
|
|
func TestUpdateHosts_ReplacesExistingSection(t *testing.T) {
|
|
path := writeTempHosts(t, "127.0.0.1 localhost\n")
|
|
|
|
// First write.
|
|
first := map[netip.Addr]*Peer{
|
|
netip.MustParseAddr("10.11.12.1"): peer("hub"),
|
|
}
|
|
if err := updateHosts(path, "mynet.local", first); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
// Second write with a different set of peers.
|
|
second := map[netip.Addr]*Peer{
|
|
netip.MustParseAddr("10.11.12.20"): peer("phone"),
|
|
}
|
|
if err := updateHosts(path, "mynet.local", second); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
inside, outside := readManagedSection(t, path, "mynet.local")
|
|
|
|
if len(inside) != 1 || inside[0] != "10.11.12.20 phone.mynet.local" {
|
|
t.Errorf("section not replaced; inside = %v", inside)
|
|
}
|
|
if contains(inside, "10.11.12.1 hub.mynet.local") {
|
|
t.Errorf("stale entry remained; inside = %v", inside)
|
|
}
|
|
if !contains(outside, "127.0.0.1 localhost") {
|
|
t.Errorf("original content lost; outside = %v", outside)
|
|
}
|
|
}
|
|
|
|
func TestUpdateHosts_SkipsEmptyNames(t *testing.T) {
|
|
path := writeTempHosts(t, "127.0.0.1 localhost\n")
|
|
|
|
peers := map[netip.Addr]*Peer{
|
|
netip.MustParseAddr("10.11.12.1"): peer("hub"),
|
|
netip.MustParseAddr("10.11.12.99"): peer(""), // no name
|
|
}
|
|
if err := updateHosts(path, "mynet.local", peers); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
inside, _ := readManagedSection(t, path, "mynet.local")
|
|
if len(inside) != 1 || inside[0] != "10.11.12.1 hub.mynet.local" {
|
|
t.Errorf("expected only named peer; inside = %v", inside)
|
|
}
|
|
}
|
|
|
|
func TestUpdateHosts_Idempotent(t *testing.T) {
|
|
path := writeTempHosts(t, "127.0.0.1 localhost\n")
|
|
|
|
peers := map[netip.Addr]*Peer{
|
|
netip.MustParseAddr("10.11.12.1"): peer("hub"),
|
|
}
|
|
if err := updateHosts(path, "mynet.local", peers); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
first, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := updateHosts(path, "mynet.local", peers); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if string(first) != string(second) {
|
|
t.Errorf("repeated update changed file:\nfirst:\n%s\nsecond:\n%s", first, second)
|
|
}
|
|
}
|
|
|
|
func contains(ss []string, s string) bool {
|
|
for _, x := range ss {
|
|
if x == s {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|