3x-ui/web/service/node_test.go

163 lines
4 KiB
Go
Raw Normal View History

package service
import (
"testing"
"github.com/mhsanaei/3x-ui/v3/database/model"
)
func TestNormalizeBasePath(t *testing.T) {
cases := []struct {
in string
want string
}{
{"", "/"},
{" ", "/"},
{"/", "/"},
{"/panel", "/panel/"},
{"panel", "/panel/"},
{"panel/", "/panel/"},
{"/panel/", "/panel/"},
{" /panel ", "/panel/"},
{"/a/b/c", "/a/b/c/"},
}
for _, c := range cases {
t.Run(c.in, func(t *testing.T) {
got := normalizeBasePath(c.in)
if got != c.want {
t.Fatalf("normalizeBasePath(%q) = %q, want %q", c.in, got, c.want)
}
})
}
}
func TestNodeMetricKey(t *testing.T) {
cases := []struct {
id int
metric string
want string
}{
{1, "cpu", "node:1:cpu"},
{42, "mem", "node:42:mem"},
{0, "anything", "node:0:anything"},
}
for _, c := range cases {
got := nodeMetricKey(c.id, c.metric)
if got != c.want {
t.Fatalf("nodeMetricKey(%d, %q) = %q, want %q", c.id, c.metric, got, c.want)
}
}
}
func TestHeartbeatPatch_ToUI_OnlineCopiesFields(t *testing.T) {
p := HeartbeatPatch{
Status: "ignored-source",
LatencyMs: 42,
XrayVersion: "1.8.4",
PanelVersion: "3.0.0",
CpuPct: 12.5,
MemPct: 33.3,
UptimeSecs: 12345,
LastError: "",
}
ui := p.ToUI(true)
if ui.Status != "online" {
t.Fatalf("Status = %q, want online", ui.Status)
}
if ui.LatencyMs != 42 || ui.XrayVersion != "1.8.4" || ui.PanelVersion != "3.0.0" {
t.Fatalf("scalar copy mismatch: %+v", ui)
}
if ui.CpuPct != 12.5 || ui.MemPct != 33.3 || ui.UptimeSecs != 12345 {
t.Fatalf("metric copy mismatch: %+v", ui)
}
if ui.Error != "" {
t.Fatalf("Error = %q, want empty", ui.Error)
}
}
func TestHeartbeatPatch_ToUI_OfflinePreservesError(t *testing.T) {
p := HeartbeatPatch{LastError: "connection refused"}
ui := p.ToUI(false)
if ui.Status != "offline" {
t.Fatalf("Status = %q, want offline", ui.Status)
}
if ui.Error != "connection refused" {
t.Fatalf("Error = %q, want %q", ui.Error, "connection refused")
}
}
func TestNodeService_Normalize_Valid(t *testing.T) {
s := &NodeService{}
n := &model.Node{
Name: " primary ",
ApiToken: " abc ",
Address: "example.com",
Port: 8443,
Scheme: "",
BasePath: "panel",
}
if err := s.normalize(n); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if n.Name != "primary" {
t.Fatalf("Name not trimmed: %q", n.Name)
}
if n.ApiToken != "abc" {
t.Fatalf("ApiToken not trimmed: %q", n.ApiToken)
}
if n.Scheme != "https" {
t.Fatalf("empty Scheme should default to https, got %q", n.Scheme)
}
if n.BasePath != "/panel/" {
t.Fatalf("BasePath = %q, want /panel/", n.BasePath)
}
}
func TestNodeService_Normalize_KeepsValidScheme(t *testing.T) {
s := &NodeService{}
n := &model.Node{Name: "n", Address: "example.com", Port: 80, Scheme: "http"}
if err := s.normalize(n); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if n.Scheme != "http" {
t.Fatalf("Scheme = %q, want http", n.Scheme)
}
}
func TestNodeService_Normalize_RejectsEmptyName(t *testing.T) {
s := &NodeService{}
n := &model.Node{Name: " ", Address: "example.com", Port: 443}
if err := s.normalize(n); err == nil {
t.Fatal("expected error for empty name")
}
}
func TestNodeService_Normalize_RejectsBadHost(t *testing.T) {
s := &NodeService{}
n := &model.Node{Name: "n", Address: "bad host name with spaces", Port: 443}
if err := s.normalize(n); err == nil {
t.Fatal("expected error for invalid host")
}
}
func TestNodeService_Normalize_RejectsOutOfRangePort(t *testing.T) {
s := &NodeService{}
for _, port := range []int{0, -1, 65536, 100000} {
n := &model.Node{Name: "n", Address: "example.com", Port: port}
if err := s.normalize(n); err == nil {
t.Fatalf("expected error for port %d", port)
}
}
}
func TestNodeService_Normalize_OverridesUnknownScheme(t *testing.T) {
s := &NodeService{}
n := &model.Node{Name: "n", Address: "example.com", Port: 443, Scheme: "ftp"}
if err := s.normalize(n); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if n.Scheme != "https" {
t.Fatalf("Scheme = %q, want https", n.Scheme)
}
}