package main import ( "errors" "strings" "testing" ) const sample = `{ "system": { "hostName": "router", "stateVersion": "26.11" }, "router": { "wan": { "interface": "eth1" }, "lans": { "main": { "interface": "eth2", "internet": true, "ipv4": { "address": "192.168.10.1" }, "dhcp": { "pool": { "start": "192.168.10.100", "end": "192.168.10.199" }, "reservations": { "nas": { "mac": "aa:bb:cc:dd:ee:ff", "address": "192.168.10.5" } } }, "ipv6": { "address": "fd00::1/64" } } }, "wireguard": { "vpn": { "listenPort": 51820 } } } }` func mustParse(t *testing.T) Settings { t.Helper() s, err := parseSettings([]byte(sample)) if err != nil { t.Fatal(err) } return s } func TestFormReadsSettings(t *testing.T) { f := mustParse(t).form() if f.HostName != "router" || f.WAN.Interface != "eth1" || f.WAN.Type != "dhcp" { t.Fatalf("unexpected form: %+v", f) } if len(f.LANs) != 1 || f.LANs[0].Name != "main" || !f.LANs[0].DHCP || !f.LANs[0].Internet || f.LANs[0].PoolEnd != "192.168.10.199" { t.Fatalf("unexpected LANs: %+v", f.LANs) } } func TestApplyKeepsUnknownKeys(t *testing.T) { s := mustParse(t) f := s.form() f.LANs[0].PoolEnd = "192.168.10.150" if _, err := s.apply(f, "/var/lib/router/pppoe-password"); err != nil { t.Fatal(err) } out, _ := s.marshal() for _, want := range []string{`"192.168.10.150"`, `"reservations"`, `"fd00::1/64"`, `"wireguard"`, `"stateVersion"`} { if !strings.Contains(string(out), want) { t.Errorf("%s missing after apply:\n%s", want, out) } } } func TestApplyPPPoE(t *testing.T) { s := mustParse(t) f := s.form() f.WAN.Type = "pppoe" f.WAN.PPPoEUser = "user@isp" f.WAN.PPPoEVLAN = "7" if _, err := s.apply(f, "/pw"); err == nil { t.Fatal("PPPoE without any password was accepted") } s = mustParse(t) f.WAN.PPPoEPassword = "secret" pw, err := s.apply(f, "/pw") if err != nil || pw != "secret" { t.Fatalf("apply: %q %v", pw, err) } if got := str(s, "router", "wan", "ipv4", "pppoe", "passwordFile"); got != "/pw" { t.Errorf("passwordFile = %q", got) } out, _ := s.marshal() if strings.Contains(string(out), "secret") { t.Error("the password ended up in the settings") } if !strings.Contains(string(out), `"vlan": 7`) { t.Errorf("vlan is not a number:\n%s", out) } } func TestApplyValidates(t *testing.T) { s := mustParse(t) f := s.form() f.LANs[0].Address = "not-an-ip" f.LANs[0].VLAN = "5000" f.LANs = append(f.LANs, LANForm{Name: "Bad Name", Interface: "eth3", Address: "10.0.0.1"}) _, err := s.apply(f, "/pw") var fe FormErrors if !errors.As(err, &fe) { t.Fatalf("expected form errors, got %v", err) } for _, k := range []string{"lans[0].address", "lans[0].vlan", "lans[1].name"} { if fe[k] == "" { t.Errorf("no error for %s: %v", k, fe) } } } func TestApplyAddsAndRemovesLANs(t *testing.T) { s := mustParse(t) f := s.form() f.LANs = append(f.LANs, LANForm{Name: "guest", Interface: "eth2", VLAN: "20", Address: "192.168.30.1", DHCP: true, PoolStart: "192.168.30.100", PoolEnd: "192.168.30.199"}) if _, err := s.apply(f, "/pw"); err != nil { t.Fatal(err) } if got := s.form().LANs; len(got) != 2 || got[0].Name != "guest" || got[0].VLAN != "20" { t.Fatalf("after adding: %+v", got) } f = s.form() f.LANs[0].Remove = true if _, err := s.apply(f, "/pw"); err != nil { t.Fatal(err) } if got := s.form().LANs; len(got) != 1 || got[0].Name != "main" { t.Fatalf("after removing: %+v", got) } f = s.form() f.LANs[0].Remove = true if _, err := s.apply(f, "/pw"); err == nil { t.Fatal("removing the last LAN was accepted") } }