Files
bee/audit/internal/app/app_test.go
2026-03-13 16:52:16 +03:00

280 lines
7.4 KiB
Go

package app
import (
"errors"
"testing"
"bee/audit/internal/platform"
)
type fakeNetwork struct {
listInterfacesFn func() ([]platform.InterfaceInfo, error)
defaultRouteFn func() string
dhcpOneFn func(string) (string, error)
dhcpAllFn func() (string, error)
setStaticIPv4Fn func(platform.StaticIPv4Config) (string, error)
}
func (f fakeNetwork) ListInterfaces() ([]platform.InterfaceInfo, error) {
return f.listInterfacesFn()
}
func (f fakeNetwork) DefaultRoute() string {
return f.defaultRouteFn()
}
func (f fakeNetwork) DHCPOne(iface string) (string, error) {
return f.dhcpOneFn(iface)
}
func (f fakeNetwork) DHCPAll() (string, error) {
return f.dhcpAllFn()
}
func (f fakeNetwork) SetStaticIPv4(cfg platform.StaticIPv4Config) (string, error) {
return f.setStaticIPv4Fn(cfg)
}
type fakeServices struct {
serviceStatusFn func(string) (string, error)
serviceDoFn func(string, platform.ServiceAction) (string, error)
}
func (f fakeServices) ListBeeServices() ([]string, error) {
return nil, nil
}
func (f fakeServices) ServiceStatus(name string) (string, error) {
return f.serviceStatusFn(name)
}
func (f fakeServices) ServiceDo(name string, action platform.ServiceAction) (string, error) {
return f.serviceDoFn(name, action)
}
type fakeExports struct{}
func (f fakeExports) ListRemovableTargets() ([]platform.RemovableTarget, error) {
return nil, nil
}
func (f fakeExports) ExportFileToTarget(src string, target platform.RemovableTarget) (string, error) {
return "", nil
}
type fakeTools struct {
tailFileFn func(string, int) string
checkToolsFn func([]string) []platform.ToolStatus
}
func (f fakeTools) TailFile(path string, lines int) string {
return f.tailFileFn(path, lines)
}
func (f fakeTools) CheckTools(names []string) []platform.ToolStatus {
return f.checkToolsFn(names)
}
type fakeSAT struct {
runFn func(string) (string, error)
}
func (f fakeSAT) RunNvidiaAcceptancePack(baseDir string) (string, error) {
return f.runFn(baseDir)
}
func TestNetworkStatusFormatsInterfacesAndRoute(t *testing.T) {
t.Parallel()
a := &App{
network: fakeNetwork{
listInterfacesFn: func() ([]platform.InterfaceInfo, error) {
return []platform.InterfaceInfo{
{Name: "eth0", State: "UP", IPv4: []string{"10.0.0.2/24"}},
{Name: "eth1", State: "DOWN", IPv4: nil},
}, nil
},
defaultRouteFn: func() string { return "10.0.0.1" },
},
}
result, err := a.NetworkStatus()
if err != nil {
t.Fatalf("NetworkStatus error: %v", err)
}
if result.Title != "Network status" {
t.Fatalf("title=%q want %q", result.Title, "Network status")
}
if want := "- eth0: state=UP ip=10.0.0.2/24"; !contains(result.Body, want) {
t.Fatalf("body missing %q\nbody=%s", want, result.Body)
}
if want := "- eth1: state=DOWN ip=(no IPv4)"; !contains(result.Body, want) {
t.Fatalf("body missing %q\nbody=%s", want, result.Body)
}
if want := "Default route: 10.0.0.1"; !contains(result.Body, want) {
t.Fatalf("body missing %q\nbody=%s", want, result.Body)
}
}
func TestNetworkStatusPropagatesListError(t *testing.T) {
t.Parallel()
a := &App{
network: fakeNetwork{
listInterfacesFn: func() ([]platform.InterfaceInfo, error) {
return nil, errors.New("boom")
},
defaultRouteFn: func() string { return "" },
},
}
result, err := a.NetworkStatus()
if err == nil {
t.Fatal("expected error")
}
if result.Title != "Network status" {
t.Fatalf("title=%q want %q", result.Title, "Network status")
}
}
func TestParseStaticIPv4ConfigAndDefaults(t *testing.T) {
t.Parallel()
a := &App{
network: fakeNetwork{
defaultRouteFn: func() string { return " 192.168.1.1 " },
listInterfacesFn: func() ([]platform.InterfaceInfo, error) {
return nil, nil
},
dhcpOneFn: func(string) (string, error) { return "", nil },
dhcpAllFn: func() (string, error) { return "", nil },
setStaticIPv4Fn: func(platform.StaticIPv4Config) (string, error) { return "", nil },
},
}
defaults := a.DefaultStaticIPv4FormFields("eth0")
if len(defaults) != 4 {
t.Fatalf("len(defaults)=%d want 4", len(defaults))
}
if defaults[1] != "24" || defaults[2] != "192.168.1.1" {
t.Fatalf("unexpected defaults: %#v", defaults)
}
cfg := a.ParseStaticIPv4Config("eth0", []string{
" 10.10.0.5 ",
" 23 ",
" 10.10.0.1 ",
" 1.1.1.1 8.8.8.8 ",
})
if cfg.Interface != "eth0" || cfg.Address != "10.10.0.5" || cfg.Prefix != "23" || cfg.Gateway != "10.10.0.1" {
t.Fatalf("unexpected cfg: %#v", cfg)
}
if len(cfg.DNS) != 2 || cfg.DNS[0] != "1.1.1.1" || cfg.DNS[1] != "8.8.8.8" {
t.Fatalf("unexpected dns: %#v", cfg.DNS)
}
}
func TestServiceActionResults(t *testing.T) {
t.Parallel()
a := &App{
services: fakeServices{
serviceStatusFn: func(name string) (string, error) {
return "active", nil
},
serviceDoFn: func(name string, action platform.ServiceAction) (string, error) {
return string(action) + " ok", nil
},
},
}
statusResult, err := a.ServiceStatusResult("bee-audit")
if err != nil {
t.Fatalf("ServiceStatusResult error: %v", err)
}
if statusResult.Title != "service: bee-audit" || statusResult.Body != "active" {
t.Fatalf("unexpected status result: %#v", statusResult)
}
actionResult, err := a.ServiceActionResult("bee-audit", platform.ServiceRestart)
if err != nil {
t.Fatalf("ServiceActionResult error: %v", err)
}
if actionResult.Title != "service: bee-audit" || actionResult.Body != "restart ok" {
t.Fatalf("unexpected action result: %#v", actionResult)
}
}
func TestToolCheckAndLogTailResults(t *testing.T) {
t.Parallel()
a := &App{
tools: fakeTools{
tailFileFn: func(path string, lines int) string {
return path
},
checkToolsFn: func(names []string) []platform.ToolStatus {
return []platform.ToolStatus{
{Name: "dmidecode", OK: true, Path: "/usr/bin/dmidecode"},
{Name: "smartctl", OK: false},
}
},
},
}
toolsResult := a.ToolCheckResult([]string{"dmidecode", "smartctl"})
if toolsResult.Title != "Required tools" {
t.Fatalf("title=%q want %q", toolsResult.Title, "Required tools")
}
if want := "- dmidecode: OK (/usr/bin/dmidecode)"; !contains(toolsResult.Body, want) {
t.Fatalf("body missing %q\nbody=%s", want, toolsResult.Body)
}
if want := "- smartctl: MISSING"; !contains(toolsResult.Body, want) {
t.Fatalf("body missing %q\nbody=%s", want, toolsResult.Body)
}
logResult := a.AuditLogTailResult()
if logResult.Title != "Audit log tail" {
t.Fatalf("title=%q want %q", logResult.Title, "Audit log tail")
}
if want := DefaultAuditLogPath + "\n\n" + DefaultAuditJSONPath; logResult.Body != want {
t.Fatalf("body=%q want %q", logResult.Body, want)
}
}
func TestRunNvidiaAcceptancePackResult(t *testing.T) {
t.Parallel()
a := &App{
sat: fakeSAT{
runFn: func(baseDir string) (string, error) {
if baseDir != "/tmp/sat" {
t.Fatalf("baseDir=%q want %q", baseDir, "/tmp/sat")
}
return "/tmp/sat/out.tar.gz", nil
},
},
}
result, err := a.RunNvidiaAcceptancePackResult("/tmp/sat")
if err != nil {
t.Fatalf("RunNvidiaAcceptancePackResult error: %v", err)
}
if result.Title != "NVIDIA SAT" || result.Body != "Archive written to /tmp/sat/out.tar.gz" {
t.Fatalf("unexpected result: %#v", result)
}
}
func contains(haystack, needle string) bool {
return len(needle) == 0 || (len(haystack) >= len(needle) && (haystack == needle || containsAt(haystack, needle)))
}
func containsAt(haystack, needle string) bool {
for i := 0; i+len(needle) <= len(haystack); i++ {
if haystack[i:i+len(needle)] == needle {
return true
}
}
return false
}