Files

157 lines
4.3 KiB
Go
Raw Permalink Normal View History

// SPDX-License-Identifier: MIT
package cli
import (
"bytes"
"context"
"errors"
"os"
"strings"
"testing"
"prx/internal/config"
"prx/internal/execx"
"prx/internal/proto"
)
func TestCheckMark(t *testing.T) {
cases := []struct {
check proto.Check
want string
}{
{proto.Check{OK: true}, "ok"},
{proto.Check{OK: false, Warn: true}, "warn"},
{proto.Check{OK: false}, "FAIL"},
{proto.Check{OK: false, Fatal: true}, "FAIL"},
}
for _, tc := range cases {
if got := checkMark(tc.check); got != tc.want {
t.Errorf("checkMark(%+v) = %q, want %q", tc.check, got, tc.want)
}
}
}
func TestBuildPlanMapsConfig(t *testing.T) {
cfg := config.Default()
cfg.Network.WANInterface = "wlan0"
cfg.Network.WANGateway = "192.168.1.1"
cfg.Network.WANGateway6 = "2001:db8::1"
cfg.Health.Probe = "9.9.9.9:443"
cfg.Bypass.ExtraCIDRs = []string{"203.0.113.0/24"}
a := &app{}
plan := a.buildPlan(cfg, proto.ActionApply)
if plan.Action != proto.ActionApply {
t.Errorf("action = %q", plan.Action)
}
if plan.WANInterface != "wlan0" || plan.WANGateway4 != "192.168.1.1" || plan.WANGateway6 != "2001:db8::1" {
t.Errorf("network fields not mapped: %+v", plan)
}
if plan.HealthProbe != "9.9.9.9:443" {
t.Errorf("health probe not mapped: %q", plan.HealthProbe)
}
if len(plan.BypassCIDRs) != 1 || plan.BypassCIDRs[0] != "203.0.113.0/24" {
t.Errorf("bypass CIDRs not mapped: %v", plan.BypassCIDRs)
}
if plan.StatePath != config.DefaultStatePath {
t.Errorf("state path not mapped: %q", plan.StatePath)
}
}
func TestPrintResultFormatsChecks(t *testing.T) {
buf := &bytes.Buffer{}
a := &app{out: buf}
a.printResult(proto.Result{
Messages: []string{"done"},
Checks: []proto.Check{
{Name: "nft", OK: true, Detail: "present"},
{Name: "tun", OK: false, Warn: true},
},
})
out := buf.String()
for _, needle := range []string{" done", "[ok] nft: present", "[warn] tun"} {
if !strings.Contains(out, needle) {
t.Errorf("output missing %q:\n%s", needle, out)
}
}
}
func TestFormatResultError(t *testing.T) {
if err := formatResultError(proto.Result{Error: "boom"}); err == nil || err.Error() != "boom" {
t.Fatalf("formatResultError = %v", err)
}
if err := formatResultError(proto.Result{}); err == nil {
t.Fatal("expected a generic error when Result.Error is empty")
}
}
func TestNftVersionWarning(t *testing.T) {
cases := []struct {
in string
warn bool
}{
{"nftables v0.9.3 (Lester Gomes)\n", true},
{"nftables v0.9.4 (Fearless Fred)\n", false},
{"nftables v0.9.0 (Topsy)\n", true},
{"nftables v1.0.9 (Sunday, 1 September 2024)\n", false},
{"nftables v2.0.1\n", false},
{"unparseable output", false},
}
for _, tc := range cases {
got := nftVersionWarning(tc.in)
if tc.warn && got == "" {
t.Errorf("nftVersionWarning(%q) = \"\", want a warning", tc.in)
}
if !tc.warn && got != "" {
t.Errorf("nftVersionWarning(%q) = %q, want no warning", tc.in, got)
}
}
}
func TestIsPermissionError(t *testing.T) {
if !isPermissionError(errors.New("nft list: exit status 1: Error: Operation not permitted")) {
t.Error("expected a permission error to be recognised")
}
if isPermissionError(errors.New("exec: nft: executable file not found in $PATH")) {
t.Error("a missing binary must not be reported as a permission error")
}
}
// privilegeCheck must always return a named check and, for a non-root caller,
// never panic even when nothing is installed.
func TestPrivilegeCheckAlwaysNamed(t *testing.T) {
a := &app{}
c := a.privilegeCheck(context.Background(), &execx.Fake{})
if c.Name != "privilege" {
t.Fatalf("check name = %q, want %q", c.Name, "privilege")
}
if os.Geteuid() == 0 && !c.OK {
t.Fatalf("root should pass the privilege check: %+v", c)
}
}
func TestPackageHint(t *testing.T) {
for bin, want := range map[string]string{
"nft": "install nftables",
"ip": "install iproute2",
"systemctl": "install systemd",
"journalctl": "install systemd",
} {
if got := packageHint(bin); got != want {
t.Errorf("packageHint(%q) = %q, want %q", bin, got, want)
}
}
}
// errorText(nil) must be empty so a valid state is not reported as unreadable.
func TestErrorText(t *testing.T) {
if got := errorText(nil); got != "" {
t.Fatalf("errorText(nil) = %q, want \"\"", got)
}
if got := errorText(errors.New("boom")); got != "boom" {
t.Fatalf("errorText(err) = %q, want %q", got, "boom")
}
}