Files
felhom-controller/controller/internal/stacks/samba_lanaddr_test.go
T
admin badf17bebd v0.151.0 — the Megosztás page stops reloading, and says how to connect
S-1: /sharing/status coerced idle->running on the PHASE channel, so the first
poll of every steady-state page load reported a terminal job that never ran and
the client's repaint-reload fired ~1.2s apart, forever. The coercion's real duty
(liveness must never be contradicted) belongs to the 'running' LEVEL field
beside it, and is now pinned by its own regression test.

S-4 core: a terminal 'running' is served exactly once, so a REAL bring-up cannot
re-arm the reload on the page it just caused. failed/needs_password/in-flight
are never consumed. Unified async-job feedback stays the ROADMAP item.

S-2/S-5: new connect card with the Windows form, the Mac form and the direct
smb://<IP>, read from the SAMBA container's netns (the controller is on a docker
bridge and would answer 172.x). Derived per render, cached nowhere - the address
is a DHCP lease. Underivable => the line is omitted.

sharing.html's <script> block is byte-identical to v0.150.0. Red-proofed three
ways. 23/23 packages green.
2026-07-20 10:46:21 +02:00

66 lines
2.7 KiB
Go

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 <NO-CARRIER>"},
{"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)
}
}