Files
nadir-agent/internal/modules/system/system_handler_test.go
T
urania 0e041fac5e
build-and-release / release (push) Failing after 2m1s
fix: .minisign for signed releases
2026-06-22 20:03:27 +02:00

228 lines
6.6 KiB
Go

package system
import (
"encoding/json"
"net/http"
"reflect"
"testing"
"nadir/internal/oscmd"
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humago"
"github.com/danielgtaylor/huma/v2/humatest"
)
func TestSystemHandlers(t *testing.T) {
mux := http.NewServeMux()
api := humatest.Wrap(t, humago.New(mux, huma.DefaultConfig("Test", "1.0.0")))
registerHostname(api)
registerTimedate(api)
registerLocale(api)
registerPower(api)
registerInfo(api)
// Mock uname for GET /api/system/info
oscmd.SetMock("uname", func(args []string) oscmd.MockCommand {
if reflect.DeepEqual(args, []string{"-r"}) {
return oscmd.MockCommand{Stdout: "6.9.1-1-test\n", ExitCode: 0}
}
if reflect.DeepEqual(args, []string{"-m"}) {
return oscmd.MockCommand{Stdout: "x86_64\n", ExitCode: 0}
}
return oscmd.MockCommand{ExitCode: 1}
})
defer oscmd.ClearMocks()
// 1. Test GET /api/system/info
resp := api.Get("/api/system/info")
if resp.Code != http.StatusOK {
t.Errorf("get info: got %d, want %d", resp.Code, http.StatusOK)
}
// 2. Test GET & POST /api/system/hostname
oscmd.SetMock("hostnamectl", func(args []string) oscmd.MockCommand {
if reflect.DeepEqual(args, []string{"hostname"}) {
return oscmd.MockCommand{Stdout: "server01\n", ExitCode: 0}
}
if reflect.DeepEqual(args, []string{"set-hostname", "--", "server02"}) {
return oscmd.MockCommand{ExitCode: 0}
}
return oscmd.MockCommand{ExitCode: 1}
})
resp = api.Get("/api/system/hostname")
if resp.Code != http.StatusOK {
t.Errorf("get hostname: got %d, want %d", resp.Code, http.StatusOK)
}
var hostnameRes GetHostnameOutput
if err := json.Unmarshal(resp.Body.Bytes(), &hostnameRes.Body); err != nil {
t.Fatal(err)
}
if hostnameRes.Body.Hostname != "server01" {
t.Errorf("got hostname %q, want %q", hostnameRes.Body.Hostname, "server01")
}
resp = api.Post("/api/system/hostname", struct {
Hostname string `json:"hostname"`
}{
Hostname: "server02",
})
if resp.Code != http.StatusOK {
t.Errorf("set hostname: got %d, want %d", resp.Code, http.StatusOK)
}
// 3. Test GET & POST /api/system/time
oscmd.SetMock("timedatectl", func(args []string) oscmd.MockCommand {
if reflect.DeepEqual(args, []string{"show"}) {
showOut := "Timezone=Europe/Rome\nLocalRTC=no\nNTP=yes\nNTPSynchronized=yes\nCanNTP=yes\n"
return oscmd.MockCommand{Stdout: showOut, ExitCode: 0}
}
if reflect.DeepEqual(args, []string{"list-timezones"}) {
return oscmd.MockCommand{Stdout: "Europe/Rome\nUTC\n", ExitCode: 0}
}
if reflect.DeepEqual(args, []string{"set-timezone", "Europe/Rome"}) {
return oscmd.MockCommand{ExitCode: 0}
}
if reflect.DeepEqual(args, []string{"set-ntp", "true"}) {
return oscmd.MockCommand{ExitCode: 0}
}
if len(args) == 2 && args[0] == "set-time" {
return oscmd.MockCommand{ExitCode: 0}
}
return oscmd.MockCommand{ExitCode: 1}
})
resp = api.Get("/api/system/time")
if resp.Code != http.StatusOK {
t.Errorf("get time: got %d, want %d", resp.Code, http.StatusOK)
}
var timeRes GetTimeOutput
if err := json.Unmarshal(resp.Body.Bytes(), &timeRes.Body); err != nil {
t.Fatal(err)
}
if timeRes.Body.Timezone != "Europe/Rome" || !timeRes.Body.NTP {
t.Errorf("got time settings: %+v", timeRes.Body)
}
resp = api.Get("/api/system/timezones")
if resp.Code != http.StatusOK {
t.Errorf("list timezones: got %d, want %d", resp.Code, http.StatusOK)
}
resp = api.Post("/api/system/timezone", struct {
Timezone string `json:"timezone"`
}{
Timezone: "Europe/Rome",
})
if resp.Code != http.StatusOK {
t.Errorf("set timezone: got %d, want %d", resp.Code, http.StatusOK)
}
resp = api.Post("/api/system/ntp", struct {
Enabled bool `json:"enabled"`
}{
Enabled: true,
})
if resp.Code != http.StatusOK {
t.Errorf("set ntp: got %d, want %d", resp.Code, http.StatusOK)
}
resp = api.Post("/api/system/time", struct {
Time string `json:"time"`
}{
Time: "2026-06-20T12:00:00Z",
})
if resp.Code != http.StatusOK {
t.Errorf("set time: got %d, want %d", resp.Code, http.StatusOK)
}
// 4. Test GET & POST /api/system/locale
oscmd.SetMock("localectl", func(args []string) oscmd.MockCommand {
if reflect.DeepEqual(args, []string{"status"}) {
statusOut := " System Locale: LANG=it_IT.UTF-8\n VC Keymap: it\n X11 Layout: it\n"
return oscmd.MockCommand{Stdout: statusOut, ExitCode: 0}
}
if reflect.DeepEqual(args, []string{"list-locales"}) {
return oscmd.MockCommand{Stdout: "it_IT.UTF-8\nen_US.UTF-8\n", ExitCode: 0}
}
if reflect.DeepEqual(args, []string{"set-locale", "LANG=it_IT.UTF-8"}) {
return oscmd.MockCommand{ExitCode: 0}
}
if reflect.DeepEqual(args, []string{"list-keymaps"}) {
return oscmd.MockCommand{Stdout: "it\nus\n", ExitCode: 0}
}
if reflect.DeepEqual(args, []string{"set-keymap", "it"}) {
return oscmd.MockCommand{ExitCode: 0}
}
return oscmd.MockCommand{ExitCode: 1}
})
resp = api.Get("/api/system/locale")
if resp.Code != http.StatusOK {
t.Errorf("get locale: got %d, want %d", resp.Code, http.StatusOK)
}
var localeRes GetLocaleOutput
if err := json.Unmarshal(resp.Body.Bytes(), &localeRes.Body); err != nil {
t.Fatal(err)
}
if localeRes.Body.Lang != "it_IT.UTF-8" || localeRes.Body.VCKeymap != "it" {
t.Errorf("got locale status: %+v", localeRes.Body)
}
resp = api.Get("/api/system/locales")
if resp.Code != http.StatusOK {
t.Errorf("list locales: got %d, want %d", resp.Code, http.StatusOK)
}
resp = api.Post("/api/system/locale", struct {
Lang string `json:"lang"`
}{
Lang: "it_IT.UTF-8",
})
if resp.Code != http.StatusOK {
t.Errorf("set locale: got %d, want %d", resp.Code, http.StatusOK)
}
resp = api.Get("/api/system/keymaps")
if resp.Code != http.StatusOK {
t.Errorf("list keymaps: got %d, want %d", resp.Code, http.StatusOK)
}
resp = api.Post("/api/system/keymap", struct {
Keymap string `json:"keymap"`
}{
Keymap: "it",
})
if resp.Code != http.StatusOK {
t.Errorf("set keymap: got %d, want %d", resp.Code, http.StatusOK)
}
// 5. Test POST /api/system/reboot and /api/system/poweroff
oscmd.SetMock("shutdown", func(args []string) oscmd.MockCommand {
if reflect.DeepEqual(args, []string{"-r", "now"}) || reflect.DeepEqual(args, []string{"-h", "now"}) {
return oscmd.MockCommand{ExitCode: 0}
}
return oscmd.MockCommand{ExitCode: 1}
})
resp = api.Post("/api/system/reboot", struct {
When string `json:"when"`
}{
When: "now",
})
if resp.Code != http.StatusOK {
t.Errorf("reboot: got %d, want %d", resp.Code, http.StatusOK)
}
resp = api.Post("/api/system/poweroff", struct {
When string `json:"when"`
}{
When: "now",
})
if resp.Code != http.StatusOK {
t.Errorf("poweroff: got %d, want %d", resp.Code, http.StatusOK)
}
}