package upwatch import ( "context" "encoding/json" "sync" "testing" "time" ) type memStore struct { mu sync.Mutex raw json.RawMessage } func (m *memStore) GetUpwatch() json.RawMessage { m.mu.Lock(); defer m.mu.Unlock(); return m.raw } func (m *memStore) UpdateUpwatch(fn func(json.RawMessage) (json.RawMessage, error)) error { m.mu.Lock() defer m.mu.Unlock() n, err := fn(m.raw) if err == nil { m.raw = n } return err } // D41 (operator 2026-10-24): qBittorrent gets 50 % of the host's TESTED line — never of the push floor, which read // 10 Mbit/s on demo-hp's 90 Mbit/s line. The watch judges against the higher of the two. // RED-PROOF: let QBLimitNow use s.Line() → the push floor alone sets a limit → fails; let line() ignore the test → // Line() stays at the floor → fails. func TestService_QBLimitFromTheTestedLineOnly(t *testing.T) { logins, expire, limit := 0, false, int64(0) srv := fakeQBServer(t, "pw-canary", &logins, &expire, &limit) defer srv.Close() st := &memStore{} floor := LineRecord{Peak10Mbps: 10, Counts: true} if err := st.UpdateUpwatch(func(json.RawMessage) (json.RawMessage, error) { return json.Marshal(State{Line: []LineRecord{floor}}) }); err != nil { t.Fatal(err) } var tested float64 at := time.Date(2026, 10, 24, 6, 31, 0, 0, time.UTC) svc := &Service{Store: st, QB: &QBittorrent{Box: envBox{"qbittorrent/QBT_ADMIN_PASSWORD": "pw-canary"}, URL: srv.URL}, QBShare: 0.5, Measured: func(context.Context) (float64, time.Time, bool) { return tested, at, tested > 0 }} svc.QBLimitNow(context.Background()) if limit != 0 { t.Fatalf("no test yet, push floor 10 Mbit/s: qBittorrent limited to %d B/s — the floor must not set a limit", limit) } if l, ok := svc.Line(); !ok || l != 10 { t.Fatalf("watch line before a test = %.1f %v, want the push floor 10", l, ok) } if v := svc.View(); !v.LineKnown || v.LineTested || v.LineMbps != 10 { t.Fatalf("view before a test = %+v", v) } tested = 90.7 svc.QBLimitNow(context.Background()) if want := int64(5668750) / 1024 * 1024; limit != want { t.Fatalf("tested 90.7 Mbit/s: limit %d B/s, want 50 %% = %d (stored in KiB/s)", limit, want) } if l, ok := svc.Line(); !ok || l != 90.7 { t.Fatalf("watch line after the test = %.1f %v, want 90.7", l, ok) } if v := svc.View(); !v.LineTested || v.LineMbps != 90.7 || v.QBLimitBps != limit { t.Fatalf("view after the test = %+v", v) } // The agent stops answering: the last tested value stays (an agent restart must not drop the limit). tested = 0 svc.RefreshMeasured(context.Background()) if l, ok := svc.MeasuredLine(); !ok || l != 90.7 { t.Fatalf("after a failed read: %.1f %v, want the last value kept", l, ok) } }