R-1261: the line hold holds every running seeder, drops the record of an app removed while held, and puts back the qBittorrent slot it held (alternative speed). Red-proved. Commit only.
gates / gates (push) Successful in 1m29s

This commit is contained in:
admin committed 2026-10-25 20:38:28 +01:00
1 parent fe9837708a
commit a79a292d72
7 files changed
+475 -88

No files matched your search

+29 -9
View File
@@ -15,20 +15,29 @@ import (
// fakeTorrent is a torrent app's limit in memory. failWrite fails the next write AFTER storing it (a write that
// took while its read-back failed); failRead fails reads.
type fakeTorrent struct {
name string // "" = qbittorrent
limit HeldLimit
writes []HeldLimit
failWrite bool
failRead bool
}
func (f *fakeTorrent) Stack() string { return "qbittorrent" }
func (f *fakeTorrent) HoldValue() HeldLimit { return HeldLimit{Bytes: 1024} }
func (f *fakeTorrent) Stack() string {
if f.name != "" {
return f.name
}
return "qbittorrent"
}
func (f *fakeTorrent) HoldValue(HeldLimit) HeldLimit { return HeldLimit{Bytes: 1024} }
func (f *fakeTorrent) ReadLimit(context.Context) (HeldLimit, error) {
if f.failRead {
return HeldLimit{}, errors.New("connection refused")
}
return f.limit, nil
}
func (f *fakeTorrent) ReadLimitLike(ctx context.Context, _ HeldLimit) (HeldLimit, error) {
return f.ReadLimit(ctx)
}
func (f *fakeTorrent) WriteLimit(_ context.Context, l HeldLimit) (HeldLimit, error) {
f.limit = l
f.writes = append(f.writes, l)
@@ -68,7 +77,14 @@ func newHoldRig(t *testing.T) *holdRig {
return r
}
func (r *holdRig) record() *LineHold { return decode(r.store.GetUpwatch()).Hold }
// record is the single held app's record (nil when none) — the rigs below hold one app unless they say otherwise.
func (r *holdRig) record() *LineHold {
hs := heldList(decode(r.store.GetUpwatch()))
if len(hs) == 0 {
return nil
}
return &hs[0]
}
// R-1250, the brief's three cases: the old limit — the household's own — comes back exactly after a measured test,
// after a failed one, and after a refused one; during the test the app holds the test value and the record exists.
@@ -237,15 +253,19 @@ func TestHold_QBLimitRuleSkipsAHeldLimit(t *testing.T) {
// The two apps' real APIs: qBittorrent's global limit, Transmission's session-get/session-set with the session-id
// handshake. The test value is never zero (zero is „no limit").
func TestHold_TheTwoAppsReadAndWriteTheirLimit(t *testing.T) {
logins, expire, limit := 0, false, int64(2_499_584)
srv := fakeQBServer(t, "pw-canary", &logins, &expire, &limit)
fq := &fakeQBPrefs{up: 2_499_584, alt: 10_240}
srv := fq.server(t, "pw-canary")
defer srv.Close()
qb := &QBittorrent{Box: envBox{"qbittorrent/QBT_ADMIN_PASSWORD": "pw-canary"}, URL: srv.URL}
if l, err := qb.ReadLimit(context.Background()); err != nil || l.Bytes != 2_499_584 {
if l, err := qb.ReadLimit(context.Background()); err != nil || l != (HeldLimit{Bytes: 2_499_584}) {
t.Fatalf("qBittorrent read %+v %v", l, err)
}
if l, err := qb.WriteLimit(context.Background(), qb.HoldValue()); err != nil || l != (HeldLimit{Bytes: 1024}) || limit != 1024 {
t.Fatalf("qBittorrent hold: %+v %v, stored %d", l, err, limit)
if l, err := qb.WriteLimit(context.Background(), qb.HoldValue(HeldLimit{})); err != nil || l != (HeldLimit{Bytes: 1024}) || fq.up != 1024 {
t.Fatalf("qBittorrent hold: %+v %v, stored %d", l, err, fq.up)
}
fq.altMode = true
if l, err := qb.ReadLimit(context.Background()); err != nil || l != (HeldLimit{Bytes: 10_240, Alt: true}) {
t.Fatalf("qBittorrent read in alternative mode %+v %v, want the alt slot", l, err)
}
tr := struct {
@@ -289,7 +309,7 @@ func TestHold_TheTwoAppsReadAndWriteTheirLimit(t *testing.T) {
if err != nil || prev != (HeldLimit{KBps: 500}) {
t.Fatalf("Transmission read %+v %v, want 500 kB/s switched off", prev, err)
}
if l, err := tm.WriteLimit(context.Background(), tm.HoldValue()); err != nil || l != (HeldLimit{Enabled: true, KBps: 1}) {
if l, err := tm.WriteLimit(context.Background(), tm.HoldValue(HeldLimit{})); err != nil || l != (HeldLimit{Enabled: true, KBps: 1}) {
t.Fatalf("Transmission hold: %+v %v", l, err)
}
if l, err := tm.WriteLimit(context.Background(), prev); err != nil || l != prev || tr.enabled || tr.up != 500 {