package stacks import ( "errors" "io" "log" "testing" ) // v0.151.0 (S-2): the `ip -4 -o addr show eth0` parse. Isolated because it is the one part of the // connect-address path that can silently produce a PLAUSIBLE WRONG string — and a wrong address on // that page is worse than no address, which is the whole reason the card exists. func TestParseIPv4FromIPAddrOutput(t *testing.T) { // Verbatim from the live demo guest (felhom-samba, host netns), backslash-continuation and all. const live = `2: eth0 inet 192.168.0.104/24 brd 192.168.0.255 scope global dynamic eth0\ valid_lft 1486sec preferred_lft 1486sec` ok := []struct{ name, in, want string }{ {"live demo output", live, "192.168.0.104"}, {"no lifetime suffix", "2: eth0 inet 10.0.0.5/8 brd 10.255.255.255 scope global eth0", "10.0.0.5"}, {"secondary address after loopback line", "1: lo inet 127.0.0.1/8 scope host lo\n2: eth0 inet 172.16.4.2/16 scope global eth0", "172.16.4.2"}, } for _, tc := range ok { got, err := parseIPv4FromIPAddrOutput(tc.in) if err != nil { t.Errorf("%s: unexpected error %v", tc.name, err) continue } if got != tc.want { t.Errorf("%s: got %q, want %q", tc.name, got, tc.want) } } // Every one of these would put a USELESS or MISLEADING address in front of a customer, so each // must be an error the caller turns into an omitted line — never a best-effort string. bad := []struct{ name, in string }{ {"empty", ""}, {"interface exists but has no address", "2: eth0 "}, {"loopback only", "1: lo inet 127.0.0.1/8 scope host lo"}, {"unspecified", "2: eth0 inet 0.0.0.0/0 scope global eth0"}, {"link-local (DHCP never answered)", "2: eth0 inet 169.254.11.9/16 scope link eth0"}, {"IPv6 only", "2: eth0 inet6 fe80::be24:11ff:fede:1be7/64 scope link"}, {"garbage", "docker: Error response from daemon: No such container: felhom-samba"}, {"inet with no value", "2: eth0 inet"}, } for _, tc := range bad { if got, err := parseIPv4FromIPAddrOutput(tc.in); err == nil { t.Errorf("%s: must be an error, got %q", tc.name, got) } } } // SambaLANAddress swallows the error into "" — the page omits a line rather than failing — and // passes a good address straight through. func TestSambaLANAddressFailsQuiet(t *testing.T) { m := &Manager{logger: log.New(io.Discard, "", 0)} m.sambaAddrFn = func() (string, error) { return "", errors.New("no such container") } if got := m.SambaLANAddress(); got != "" { t.Errorf("error path: got %q, want empty", got) } m.sambaAddrFn = func() (string, error) { return "192.0.2.10", nil } if got := m.SambaLANAddress(); got != "192.0.2.10" { t.Errorf("happy path: got %q, want 192.0.2.10", got) } }