package main // Tests for CustomerSummary (round-2 item 1): the six counts on the // customers page, with depleted/depletingSoon predicates aligned to // 3x-ui's depletedExpr/expiringExpr (client_paging.go) — including its // panel-configurable expireDiff (days) / trafficDiff (GB) thresholds, // which default to 0 and then count nobody as depleting soon. import ( "testing" "time" ) func summaryFixture(now time.Time) *Data { iso := func(t time.Time) *string { s := t.UTC().Format(time.RFC3339); return &s } manual := "manual" expiredReason := "expired" gb := float64(1073741824) return &Data{Customers: []*Customer{ // active + online + plain {ID: "cus_a", Name: "A", Status: "active", LastOnlineAt: iso(now)}, // active, traffic exhausted {ID: "cus_b", Name: "B", Status: "active", TrafficLimitBytes: 1000, UsedBytes: 1000}, // active, 0.5GB remaining of 2GB {ID: "cus_c", Name: "C", Status: "active", TrafficLimitBytes: 2 * gb, UsedBytes: int64(1.5 * gb)}, // active, expires in 2 days {ID: "cus_d", Name: "D", Status: "active", ExpiresAt: iso(now.Add(48 * time.Hour))}, // active, expires in 10 days {ID: "cus_e", Name: "E", Status: "active", ExpiresAt: iso(now.Add(240 * time.Hour))}, // manually disabled {ID: "cus_f", Name: "F", Status: "suspended", SuspendReason: &manual, SuspendReasonPresent: true}, // expired (auto-suspended): depleted, not "disabled" {ID: "cus_g", Name: "G", Status: "suspended", SuspendReason: &expiredReason, SuspendReasonPresent: true, ExpiresAt: iso(now.Add(-time.Hour))}, // active, unlimited with heavy usage: never depleted {ID: "cus_h", Name: "H", Status: "active", UsedBytes: int64(9 * gb)}, }} } func TestCustomerSummaryDefaultThresholds(t *testing.T) { now := time.Now() d := summaryFixture(now) s := CustomerSummary(d, now.UnixMilli()) want := map[string]int{"total": 8, "online": 1, "depleted": 2, "depletingSoon": 0, "disabled": 1, "enabled": 6} for k, v := range want { if s[k] != v { t.Fatalf("summary[%s] = %v, want %d (full: %v)", k, s[k], v, s) } } } func TestCustomerSummaryWithThresholds(t *testing.T) { now := time.Now() d := summaryFixture(now) d.Settings.ExpireDiff = 3 // days d.Settings.TrafficDiff = 1 // GB s := CustomerSummary(d, now.UnixMilli()) // C (0.5GB remaining < 1GB) and D (expiry in 2d < 3d) become depleting soon; // E (10d) does not. Nothing else moves. if s["depletingSoon"] != 2 { t.Fatalf("depletingSoon = %v, want 2 (full: %v)", s["depletingSoon"], s) } if s["depleted"] != 2 || s["enabled"] != 6 || s["disabled"] != 1 { t.Fatalf("other counts moved: %v", s) } }