Files
NexusGate/agent-go/singboxversion_test.go
T
aaaamoooo bb974e2ed7 fix(agent): fall back to install record for sing-box core version (item 12)
Locally built sing-box v1.13.21 carries no version stamp and reports
'unknown', leaving the panel core-version column empty. The agent probe
now falls back to /etc/nexusgate/sing-box/version when the binary
reports no version; agent-singbox.sh writes that record (version
without the v prefix) on every successful install path, including the
already-installed early exit. Agent version stays 0.6.8.
2026-10-21 10:36:09 +00:00

125 lines
4.1 KiB
Go

package main
// Tests for the sing-box version probe (round-2 item 4): parsing of
// `sing-box version` output and the mtime/size-keyed cache that keeps
// heartbeats from spawning the binary every round.
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestParseSingBoxVersion(t *testing.T) {
cases := []struct{ in, want string }{
{"sing-box version 1.13.21\n\nEnvironment: go1.24.6 linux/amd64\n", "1.13.21"},
{"sing-box version 1.13.21", "1.13.21"},
{"sing-box version unknown\n", ""},
{"", ""},
{"garbage output\n", ""},
{"sing-box version\n", ""},
}
for _, c := range cases {
if got := parseSingBoxVersion(c.in); got != c.want {
t.Fatalf("parseSingBoxVersion(%q) = %q, want %q", c.in, got, c.want)
}
}
}
func writeFakeSingBox(t *testing.T, dir, version string) (binPath, countPath string) {
t.Helper()
binPath = filepath.Join(dir, "fake-sing-box")
countPath = filepath.Join(dir, "count")
script := "#!/bin/sh\necho x >> \"" + countPath + "\"\necho 'sing-box version " + version + "'\n"
if err := os.WriteFile(binPath, []byte(script), 0o755); err != nil {
t.Fatal(err)
}
return binPath, countPath
}
func TestSingBoxVersionProbeCachesByMtime(t *testing.T) {
dir := t.TempDir()
binPath, countPath := writeFakeSingBox(t, dir, "1.2.3")
oldBin := cfg.SingBoxBin
cfg.SingBoxBin = binPath
defer func() { cfg.SingBoxBin = oldBin }()
singBoxVersionCache.probed = false
defer func() { singBoxVersionCache.probed = false }()
count := func() int {
data, _ := os.ReadFile(countPath)
return strings.Count(string(data), "x")
}
if got := singBoxVersion(); got != "1.2.3" {
t.Fatalf("first probe = %q, want 1.2.3", got)
}
if got := singBoxVersion(); got != "1.2.3" {
t.Fatalf("cached probe = %q, want 1.2.3", got)
}
if n := count(); n != 1 {
t.Fatalf("binary executed %d times across two heartbeats, want 1 (cached)", n)
}
// Replacing the binary (engine install/update) must trigger one re-probe.
writeFakeSingBox(t, dir, "1.2.4")
if got := singBoxVersion(); got != "1.2.4" {
t.Fatalf("probe after binary replacement = %q, want 1.2.4", got)
}
if n := count(); n != 2 {
t.Fatalf("binary executed %d times after replacement, want 2", n)
}
}
// Round-3 item 12: when the binary reports "unknown" (the locally built
// sing-box has no version stamp), the probe falls back to the install
// record written by agent-singbox.sh.
func TestSingBoxVersionFallsBackToInstallRecord(t *testing.T) {
dir := t.TempDir()
binPath, _ := writeFakeSingBox(t, dir, "unknown")
recordPath := filepath.Join(dir, "version")
if err := os.WriteFile(recordPath, []byte("1.13.21\n"), 0o644); err != nil {
t.Fatal(err)
}
oldBin, oldRecord := cfg.SingBoxBin, cfg.SingBoxVersionFile
cfg.SingBoxBin, cfg.SingBoxVersionFile = binPath, recordPath
defer func() { cfg.SingBoxBin, cfg.SingBoxVersionFile = oldBin, oldRecord }()
singBoxVersionCache.probed = false
defer func() { singBoxVersionCache.probed = false }()
if got := singBoxVersion(); got != "1.13.21" {
t.Fatalf("fallback probe = %q, want 1.13.21", got)
}
// A stamped binary wins over the record.
writeFakeSingBox(t, dir, "1.13.22")
if got := singBoxVersion(); got != "1.13.22" {
t.Fatalf("stamped probe = %q, want 1.13.22 (record must not shadow it)", got)
}
// A garbage record is ignored.
if err := os.WriteFile(recordPath, []byte("not-a-version\n"), 0o644); err != nil {
t.Fatal(err)
}
writeFakeSingBox(t, dir, "unknown")
if got := singBoxVersion(); got != "" {
t.Fatalf("garbage record: got %q, want empty", got)
}
if got := installedSingBoxVersion(); got != "" {
t.Fatalf("installedSingBoxVersion(garbage) = %q, want empty", got)
}
if err := os.WriteFile(recordPath, []byte("v1.13.21\n"), 0o644); err != nil {
t.Fatal(err)
}
if got := installedSingBoxVersion(); got != "1.13.21" {
t.Fatalf("installedSingBoxVersion(v-prefixed) = %q, want 1.13.21", got)
}
}
func TestSingBoxVersionMissingBinary(t *testing.T) {
oldBin := cfg.SingBoxBin
cfg.SingBoxBin = filepath.Join(t.TempDir(), "does-not-exist")
defer func() { cfg.SingBoxBin = oldBin }()
if got := singBoxVersion(); got != "" {
t.Fatalf("missing binary: got %q, want empty", got)
}
}