a9c1997fbc
gates / gates (push) Successful in 1m17s
internal/upwatch: exact per-container counters from the Docker Engine API, per-app judging (upload >= max(5 Mbit/s, 25% of the line) for 15 min; CPU >= max(150%, 50% x cores) for 30 min), explainers qBittorrent / Transmission / Plex, app_unexplained_upload / app_unexplained_cpu + _cleared (hub 0.169.0). The off-site push is sampled once a second for the line (a lower bound). qBittorrent limit = 50% of the line, the household's limit always wins. 20 red-proofs. Spike + design: felhom.eu audits/SPIKE-upload-watch-2026-10-23.md, architecture/08 section 9. Not released.
56 lines
2.3 KiB
Go
56 lines
2.3 KiB
Go
package web
|
|
|
|
import (
|
|
"html"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-controller/internal/upwatch"
|
|
)
|
|
|
|
// The upload watch's household line: qBittorrent's page says the limit while the BOX owns it — in the reader's
|
|
// language — and says nothing when the household owns it (the view then carries 0), when no view is wired, or on
|
|
// another app's page.
|
|
// RED-PROOF: drop the `.QBLimitNote` card from app_shell.html → the first case fails; drop the slug check in the
|
|
// handler → crafty's page carries the line → fails.
|
|
func TestUpwatch_QBLimitLineOnQBittorrentsPageOnly(t *testing.T) {
|
|
s, _ := credsHarness(t)
|
|
sd := filepath.Join(s.cfg.Paths.StacksDir, "qbittorrent")
|
|
if err := os.MkdirAll(sd, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
os.WriteFile(filepath.Join(sd, ".felhom.yml"), []byte("display_name: qBittorrent\nslug: qbittorrent\n"), 0o644)
|
|
os.WriteFile(filepath.Join(sd, "docker-compose.yml"), []byte("services: {}\n"), 0o644)
|
|
os.WriteFile(filepath.Join(sd, "app.yaml"), []byte("deployed: true\n"), 0o644)
|
|
_ = s.stackMgr.ScanStacks()
|
|
s.loadTemplates()
|
|
render := func(app, lang string) string {
|
|
rr := httptest.NewRecorder()
|
|
s.appPageHandler(rr, httptest.NewRequest("GET", "/apps/"+app+"?lang="+lang, nil), app, "")
|
|
return html.UnescapeString(rr.Body.String())
|
|
}
|
|
view := upwatch.View{LineMbps: 100, LineKnown: true, QBLimitBps: 6249472}
|
|
s.SetUpwatchView(func() upwatch.View { return view })
|
|
hu, en := asciiFold(render("qbittorrent", "hu")), render("qbittorrent", "en")
|
|
if !strings.Contains(hu, "data-qb-limit") || !strings.Contains(hu, "legfeljebb 50 Mbit/s") || !strings.Contains(hu, "atallithatod") {
|
|
t.Fatalf("the Hungarian qBittorrent page lacks the limit line")
|
|
}
|
|
if !strings.Contains(en, "uploads at most 50 Mbit/s") {
|
|
t.Fatalf("the English qBittorrent page lacks the limit line")
|
|
}
|
|
if strings.Contains(render("crafty", "hu"), "data-qb-limit") {
|
|
t.Fatal("another app's page carries qBittorrent's limit line")
|
|
}
|
|
view.QBLimitBps = 0 // the household owns it
|
|
if strings.Contains(render("qbittorrent", "hu"), "data-qb-limit") {
|
|
t.Fatal("a household-owned limit is described as the box's")
|
|
}
|
|
s.upwatchView = nil
|
|
if strings.Contains(render("qbittorrent", "hu"), "data-qb-limit") {
|
|
t.Fatal("no view wired, yet a line")
|
|
}
|
|
}
|