Files
3x-ui-agent/internal/agent/probe_windows_test.go
T
Muse 75f10b27fa windows probe-only agent + agent version display unification + reality sni from sing-box fragment
Scope B (Windows agent):
- probe.go split into shared core + probe_linux.go (verbatim Linux
  sampler, build-tagged) + probe_windows.go (Win32 sampler: GetSystemTimes
  CPU diff, GlobalMemoryStatusEx, fixed-drive df totals, GetIfTable2
  counted set, MachineGuid boot identity, registry facts; load and
  TCP/UDP counts stay zero - x/sys v0.47.0 exports no wrapper)
- update intents are acknowledged and skipped on Windows (the release
  assets are Linux binaries; Windows nodes update via install-agent.ps1)
- cmd/xui-agent-windows: SCM service host (svc.Run + mgr install/
  uninstall/start/stop) mirroring main.go's agent lifecycle wiring
- install-agent.ps1: downloads the exe, writes probeOnly agent.json,
  registers and starts the service, rerun-safe

Scope C (version display):
- config.GetAgentDisplayVersion (and the master getter) compose from
  the bare number so a V-form injection no longer doubles the letter
  (AV2.1.0 -> A2.1.0)
- node list view repairs stored AV-prefixed reports at assembly so
  deployed agents read uniformly without an update

Scope D (production fix):
- share-link reality sni: when the inbound has a sing-box fragment,
  sni is the fragment's tls.server_name - the only name the sing-box
  REALITY server serves. The previous rule picked a uniformly random
  serverNames entry per generation, which xray tolerated but sing-box
  refuses as an invalid connection. sid keeps its random pick: the
  converter carries the whole shortIds list into the fragment, so
  every listed sid is served.
2026-10-23 13:22:51 +00:00

100 lines
3.0 KiB
Go

//go:build windows
package agent
import (
"testing"
"golang.org/x/sys/windows"
)
// The Windows sampler's pure helpers: the Win32 calls themselves need
// a real machine, but the accounting math layered on their results is
// pinned here.
func TestFiletime100nsAndCPUSampleDiff(t *testing.T) {
lo := windows.Filetime{LowDateTime: 0x00000005, HighDateTime: 0x00000002}
if got := filetime100ns(lo); got != 0x200000005 {
t.Fatalf("filetime100ns = %#x, want 0x200000005", got)
}
// The shared differ over Windows-style counters (kernel includes
// idle): idle advanced by the full window -> 0% busy; then a
// window where only a quarter of the time is idle -> 75%.
c := &probeCollector{}
first := &cpuSample{total: 1000, idle: 400}
if got := c.cpuPercent(first); got != 0 {
t.Fatalf("first sample = %v, want 0 (baseline only)", got)
}
idleAll := &cpuSample{total: 2000, idle: 1400}
if got := c.cpuPercent(idleAll); got != 0 {
t.Fatalf("all-idle window = %v, want 0", got)
}
busy := &cpuSample{total: 3000, idle: 1650}
if got := c.cpuPercent(busy); got != 75 {
t.Fatalf("busy window = %v, want 75", got)
}
}
func TestUsedFromTotalAvail(t *testing.T) {
if got := usedFromTotalAvail(100, 30); got != 70 {
t.Fatalf("used = %d, want 70", got)
}
if got := usedFromTotalAvail(100, 100); got != 0 {
t.Fatalf("used = %d, want 0", got)
}
if got := usedFromTotalAvail(100, 130); got != 0 {
t.Fatalf("used saturates, got %d", got)
}
}
func TestSumDriveSpaceDfSemantics(t *testing.T) {
total, used := sumDriveSpace([]driveSpace{
{total: 1000, free: 250},
{total: 2000, free: 2000},
{total: 500, free: 900}, // inconsistent source clamps to full
})
if total != 3500 || used != 1250 {
t.Fatalf("sumDriveSpace = %d/%d, want 3500/1250", total, used)
}
}
func TestSplitDriveStrings(t *testing.T) {
mk := func(ss ...string) []uint16 {
var out []uint16
for _, s := range ss {
for _, r := range s {
out = append(out, uint16(r))
}
out = append(out, 0)
}
return append(out, 0)
}
got := splitDriveStrings(mk(`C:\`, `D:\`))
if len(got) != 2 || got[0] != `C:\` || got[1] != `D:\` {
t.Fatalf("splitDriveStrings = %q, want [C:\\ D:\\]", got)
}
if got := splitDriveStrings(mk()); len(got) != 0 {
t.Fatalf("empty buffer = %q, want none", got)
}
}
func TestWinIfaceCountsRule(t *testing.T) {
cases := []struct {
name string
row winIfaceRow
want bool
}{
{"up ethernet", winIfaceRow{ifType: windows.IF_TYPE_ETHERNET_CSMACD, operUp: true}, true},
{"up wifi", winIfaceRow{ifType: windows.IF_TYPE_IEEE80211, operUp: true}, true},
{"down ethernet", winIfaceRow{ifType: windows.IF_TYPE_ETHERNET_CSMACD, operUp: false}, false},
{"loopback", winIfaceRow{ifType: windows.IF_TYPE_SOFTWARE_LOOPBACK, operUp: true}, false},
{"tunnel", winIfaceRow{ifType: 131, operUp: true}, false},
{"ppp", winIfaceRow{ifType: 23, operUp: true}, false},
}
for _, tc := range cases {
if got := winIfaceCounts(tc.row); got != tc.want {
t.Errorf("%s: winIfaceCounts = %v, want %v", tc.name, got, tc.want)
}
}
}