From 06de0e83847c7e1b9efd92871e6fa6993f1586c4 Mon Sep 17 00:00:00 2001 From: Muse Date: Fri, 23 Oct 2026 02:47:01 +0000 Subject: [PATCH] B5: update stats plumbing test to the corrected registration contract --- .../service/traffic_stats_switches_test.go | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/internal/web/service/traffic_stats_switches_test.go b/internal/web/service/traffic_stats_switches_test.go index 09dbfe0..f2cd18c 100644 --- a/internal/web/service/traffic_stats_switches_test.go +++ b/internal/web/service/traffic_stats_switches_test.go @@ -14,9 +14,13 @@ import ( // UpdateAllSetting under the settings.trafficStatsSwitches key) is the // same value the sing-box generator consumes // (SingboxService.GetTrafficStatsSwitches), and the generated agent -// config's experimental/v2ray_api stats block follows the B2 category -// gates (a category appears only when both of its direction switches -// are on; all four off → no experimental block at all). +// config carries an experimental/v2ray_api stats block whenever at +// least one category gate is on (both direction switches; all four +// off → no experimental block at all). Since the ITEM6 B5 fix, the +// block always lists inbounds, users and outbounds together — the +// core registers no counters otherwise — and the per-category +// counting policy is enforced by the master at recording time, not +// by omission here. func TestTrafficStatsSwitchesPlumbing(t *testing.T) { dbDir := t.TempDir() if err := database.InitDB(filepath.Join(dbDir, "x-ui.db")); err != nil { @@ -82,8 +86,8 @@ func TestTrafficStatsSwitchesPlumbing(t *testing.T) { } if stats := statsOf(gen()); stats == nil || stats["inbounds"] == nil { t.Errorf("default: stats.inbounds missing") - } else if _, ok := stats["outbounds"]; ok { - t.Errorf("default: stats.outbounds generated although outbound switches are off") + } else if _, ok := stats["outbounds"]; !ok { + t.Errorf("default: stats.outbounds must accompany the block (core registers no counters without all three lists)") } // All four off: read path returns the saved string verbatim, the @@ -105,16 +109,17 @@ func TestTrafficStatsSwitchesPlumbing(t *testing.T) { t.Errorf("all-off: experimental block generated anyway: %v", cfg["experimental"]) } - // Inbound half-on (uplink only) closes the inbound category; - // outbound both-on opens the outbound category. + // Inbound half-on (uplink only) closes the inbound category gate; + // outbound both-on opens its gate. Either gate on → the block is + // generated with all three lists (registration requirement). mixed := `{"inboundUplink":true,"inboundDownlink":false,"outboundUplink":true,"outboundDownlink":true}` save(mixed) stats := statsOf(gen()) if stats == nil { t.Fatalf("mixed: no stats block although outbound category is on") } - if _, ok := stats["inbounds"]; ok { - t.Errorf("mixed: inbound half-on still generated stats.inbounds") + if _, ok := stats["inbounds"]; !ok { + t.Errorf("mixed: stats.inbounds must accompany the block (registration requirement; recording gate enforces the policy)") } if _, ok := stats["outbounds"]; !ok { t.Errorf("mixed: outbound both-on did not generate stats.outbounds")