Files
3x-ui-agent/internal/web/service/probe/meta_v201_test.go
T
Rogue 29de9761c8
CI / go-test (push) Waiting to run
CI / postgres-durable-first (push) Waiting to run
CI / codegen (push) Waiting to run
CI / govulncheck (push) Waiting to run
CI / race (push) Waiting to run
CI / fuzz-smoke (push) Waiting to run
CI / golangci (push) Waiting to run
CI / frontend (push) Waiting to run
Docs CI / build (push) Waiting to run
Docs Deploy (GitHub Pages) / build (push) Waiting to run
Docs Deploy (GitHub Pages) / deploy (push) Blocked by required conditions
Release 3X-UI / build (386) (push) Waiting to run
Release 3X-UI / build (amd64) (push) Waiting to run
Release 3X-UI / build (arm64) (push) Waiting to run
Release 3X-UI / build (armv5) (push) Waiting to run
Release 3X-UI / build (armv6) (push) Waiting to run
Release 3X-UI / build (armv7) (push) Waiting to run
Release 3X-UI / build (s390x) (push) Waiting to run
Release 3X-UI / Build for Windows (amd64) (push) Waiting to run
Release 3X-UI / Publish rolling dev release (push) Blocked by required conditions
CodeQL Advanced / Analyze (go) (push) Waiting to run
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
V2.0.1: listen override fix, sing-box core fetch, probe settings page + fast report, spx removal, core mem env
- 0: sing-box gen overrides fragment listen/port from the inbound row
  (empty listen -> "::"), Update/Add recompute the stored fragment;
  non-loopback E2E against the real core binary.
- 1: agent fetches the sing-box core from the Gitea release when
  missing, self-checks it, and only then latches the switch.
- 2: node list shows the sing-box core version (heartbeat field,
  persisted on the node row), xray version as fallback.
- 3: public-page fallback returns 404 to anonymous visitors when the
  page is disabled.
- 4: settings restart hint bar removed, dead i18n keys cleaned.
- 5: two PUT calls missing JSON headers fixed (audit: only those two),
  batch probe-task creation endpoint + UI.
- 6: cf-probe retirement card and coverage pipeline removed.
- 7: generated vless links no longer carry spx (panel export + sub);
  parser still accepts spx; golden fixtures updated.
- probe batch: dedicated probe settings tab (public-page settings,
  tasks, per-node meta), retention_days editable, per-node traffic
  calibration + stat-interface pinning, runtime metrics split into a
  lightweight fast report (default 3s, 1-60s adjustable).
- 8: sing-box core processes start with GOMEMLIMIT=32MiB, GOGC=50.
- 9: dead /getTwoFactorEnable docs entries removed (frontend call and
  contract entry already gone at base; verified by grep).
2026-10-23 07:53:11 +00:00

109 lines
3.8 KiB
Go

package probe
import (
"database/sql"
"path/filepath"
"testing"
"time"
)
// V2.0.1 probe batch, storage side: the calibration columns and the
// counted-interface pin round-trip through probe_meta, pre-V2.0.1
// databases gain the columns on open, and the traffic reads apply
// the calibration (floored at zero) on every figure.
func TestMetaCalibrationAndIfaceRoundTrip(t *testing.T) {
st := newTestStore(t)
meta := DefaultMeta(7)
meta.TrafficCalUpGB = 2.5
meta.TrafficCalDownGB = -1.25
meta.StatIface = "eth0"
if err := st.UpsertMeta(meta); err != nil {
t.Fatalf("upsert: %v", err)
}
back, found, err := st.Meta(7)
if err != nil || !found {
t.Fatalf("meta read: found=%v err=%v", found, err)
}
if back.TrafficCalUpGB != 2.5 || back.TrafficCalDownGB != -1.25 || back.StatIface != "eth0" {
t.Errorf("round trip = up %v down %v iface %q", back.TrafficCalUpGB, back.TrafficCalDownGB, back.StatIface)
}
}
func TestOpenMigratesLegacyMetaColumns(t *testing.T) {
path := filepath.Join(t.TempDir(), "probe.db")
db, err := sql.Open("sqlite3", path)
if err != nil {
t.Fatalf("open raw: %v", err)
}
// A pre-V2.0.1 probe_meta: the original 16 columns only (shape
// copied from the V2.0.0 schema in git history).
if _, err := db.Exec(`CREATE TABLE probe_meta (
agent_id INTEGER PRIMARY KEY, price REAL NOT NULL DEFAULT 0,
currency TEXT NOT NULL DEFAULT 'USD', billing_cycle TEXT NOT NULL DEFAULT 'monthly',
expires_at TEXT, traffic_limit INTEGER NOT NULL DEFAULT 0,
traffic_mode TEXT NOT NULL DEFAULT 'sum', traffic_reset_day INTEGER NOT NULL DEFAULT 1,
public INTEGER NOT NULL DEFAULT 1, sort INTEGER NOT NULL DEFAULT 0,
group_name TEXT NOT NULL DEFAULT '', public_remark TEXT NOT NULL DEFAULT '',
notify INTEGER NOT NULL DEFAULT 0, country_pin TEXT NOT NULL DEFAULT '',
ipv4_pin TEXT NOT NULL DEFAULT '', ipv6_pin TEXT NOT NULL DEFAULT '')`); err != nil {
t.Fatalf("create legacy table: %v", err)
}
if _, err := db.Exec(`INSERT INTO probe_meta (agent_id, group_name) VALUES (3, 'legacy')`); err != nil {
t.Fatalf("seed legacy row: %v", err)
}
_ = db.Close()
st, err := Open(path)
if err != nil {
t.Fatalf("open legacy db: %v", err)
}
defer st.Close()
back, found, err := st.Meta(3)
if err != nil || !found {
t.Fatalf("meta read: found=%v err=%v", found, err)
}
if back.GroupName != "legacy" || back.TrafficCalUpGB != 0 || back.TrafficCalDownGB != 0 || back.StatIface != "" {
t.Errorf("legacy row after migration = %+v", back)
}
back.StatIface = "ens3"
if err := st.UpsertMeta(back); err != nil {
t.Fatalf("upsert after migration: %v", err)
}
}
func TestTrafficCalibrationAppliedAndFloored(t *testing.T) {
st := newTestStore(t)
now := time.Now()
// Book 3 GB down / 1 GB up of counted traffic (baseline, delta).
book(t, st, 5, "k1/fp", 0, 0, now, nil, 0)
book(t, st, 5, "k1/fp", 3_000_000_000, 1_000_000_000, now.Add(time.Minute), nil, now.Unix())
meta := DefaultMeta(5)
meta.TrafficCalUpGB = -2 // correction exceeds the counted upload
meta.TrafficCalDownGB = 0.5 // half a GB the counters missed
if err := st.UpsertMeta(meta); err != nil {
t.Fatalf("upsert: %v", err)
}
all, err := st.AllTraffic()
if err != nil {
t.Fatalf("AllTraffic: %v", err)
}
tr := all[5]
if tr.TotalRx != 3_500_000_000 || tr.MonthRx != 3_500_000_000 || tr.DayRx != 3_500_000_000 {
t.Errorf("rx figures after +0.5 GB calibration = %d/%d/%d, want 3500000000 each", tr.TotalRx, tr.MonthRx, tr.DayRx)
}
if tr.TotalTx != 0 || tr.MonthTx != 0 || tr.DayTx != 0 {
t.Errorf("tx figures after -2 GB calibration = %d/%d/%d, want 0 (floored)", tr.TotalTx, tr.MonthTx, tr.DayTx)
}
one, found, err := st.TrafficFor(5)
if err != nil || !found {
t.Fatalf("TrafficFor: found=%v err=%v", found, err)
}
if one.TotalRx != tr.TotalRx || one.TotalTx != tr.TotalTx {
t.Errorf("TrafficFor disagrees with AllTraffic: %+v vs %+v", one, tr)
}
}