style: gofmt normalization — no logic changes

gofmt -w across the controller tree (46 files) so gofmt -l is empty — disarms the
formatting landmine where a targeted edit + accidental gofmt -w swept ~46 unrelated
files. Pure formatting: whitespace + gofmt's optional-semicolon removal in reflowed
inline closures. One doc comment reworded ('' -> 'the empty string') to avoid gofmt's
Go-1.19 doc-comment typographic substitition ('' -> curly quote) muddying its meaning.
No build/vet/test behavior change.
This commit is contained in:
2026-07-25 07:37:02 +02:00
parent 0a582ea07b
commit 2487681396
46 changed files with 221 additions and 193 deletions
@@ -59,8 +59,8 @@ func TestAppExportDomainUsesCustomerDomainNotCSRFToken(t *testing.T) {
}
}
// The empty-subdomain branch still yields '' — an app without a subdomain must not get a link to
// a bare domain (the truthiness guard is what produces that, and the fix must not disturb it).
// The empty-subdomain branch still yields the empty string — an app without a subdomain must not get
// a link to a bare domain (the truthiness guard is what produces that, and the fix must not disturb it).
func TestAppExportDomainEmptyWithoutSubdomain(t *testing.T) {
line := exportScriptLine(t, renderExport(t, ""))
@@ -236,11 +236,11 @@ func TestBackupsTemplate_Tier3Live(t *testing.T) {
html := renderBackupPage(t, "backups_apps", data)
for _, want := range []string{
"Sikeres", // active badge (LastStatus ok)
"Sikeres", // active badge (LastStatus ok)
"restic → u629488-sub1.your-storagebox.de", // the real off-box host
"Utolsó:", // a relative last-run time is shown
"Kikapcsolva", // radarr off row
"Bekapcsolás", // radarr enable link
"Utolsó:", // a relative last-run time is shown
"Kikapcsolva", // radarr off row
"Bekapcsolás", // radarr enable link
`href="/backups/remote#offbox-section"`, // the cross-page anchor jump target (IA split)
} {
if !strings.Contains(html, want) {
@@ -257,13 +257,13 @@ func TestBackupsTemplate_Tier3Live(t *testing.T) {
}
}
for _, banned := range []string{
"hamarosan", // the dead placeholder copy
"Tier2DriveGroups", // dead template field
"ResticPassword", // dead template field
"restic-pw", // dead element id
"toggleTier", // dead JS fn
"details-tier", // dead Részletek markup
"Részletek", // the removed card title
"hamarosan", // the dead placeholder copy
"Tier2DriveGroups", // dead template field
"ResticPassword", // dead template field
"restic-pw", // dead element id
"toggleTier", // dead JS fn
"details-tier", // dead Részletek markup
"Részletek", // the removed card title
"2026-07-11T02:00:00Z", // raw RFC3339 must never leak (timeAgoStr wraps it)
} {
if strings.Contains(html, banned) {
+12 -12
View File
@@ -43,18 +43,18 @@ func TestBackupsSplit_SectionsOnExactlyOnePage(t *testing.T) {
// marker → the ONE page it belongs to
markers := map[string]string{
"Tárhely áttekintés": "backups", // Section 0
"Rendszermentés (teljes mentés)": "backups", // whole-guest
">Adatmentés<": "backups", // stat cards (neutral branch)
`id="offbox-section"`: "backups_remote", // the offbox anchor target
"Távoli mentési cél beállítása": "backups_remote", // manual-target form
"Mely alkalmazások mentődnek": "backups_remote", // toggle list
"<h3>Ütemezés</h3>": "backups_apps", // schedule
"<h3>Adatbázisok</h3>": "backups_apps", // databases
"Alkalmazások mentési állapota": "backups_apps", // per-app rows
`id="restore-app"`: "backups_restore", // restore panel
"Visszaállítás a távoli tárolóból": "backups_restore", // the offsite restore list
`href="/backups/restore/app?name=`: "backups_restore", // R-48: the ONE entry per app
"Tárhely áttekintés": "backups", // Section 0
"Rendszermentés (teljes mentés)": "backups", // whole-guest
">Adatmentés<": "backups", // stat cards (neutral branch)
`id="offbox-section"`: "backups_remote", // the offbox anchor target
"Távoli mentési cél beállítása": "backups_remote", // manual-target form
"Mely alkalmazások mentődnek": "backups_remote", // toggle list
"<h3>Ütemezés</h3>": "backups_apps", // schedule
"<h3>Adatbázisok</h3>": "backups_apps", // databases
"Alkalmazások mentési állapota": "backups_apps", // per-app rows
`id="restore-app"`: "backups_restore", // restore panel
"Visszaállítás a távoli tárolóból": "backups_restore", // the offsite restore list
`href="/backups/restore/app?name=`: "backups_restore", // R-48: the ONE entry per app
}
for marker, home := range markers {
for page, html := range pages {
+1 -1
View File
@@ -152,7 +152,7 @@ func (s *Server) escrowPreflightAPIHandler(w http.ResponseWriter, r *http.Reques
agentOK := escrowAgentSupported(agent.AgentVersion())
escrowJSON(w, http.StatusOK, map[string]any{
"ok": pf.OK && agentOK, "items": pf.Items,
"agent_supported": agentOK,
"agent_supported": agentOK,
"offbox_configured": s.backupMgr != nil && s.backupMgr.OffboxConfigured(),
"escrow_state": escrowState,
"stale": s.escrowStale(),
@@ -50,13 +50,13 @@ func TestFabDownload_TraversalGuardAndCleanup(t *testing.T) {
}
refused := []string{
"..%2Fdecoy.fab", // ../decoy.fab
"..%5Cdecoy.fab", // ..\decoy.fab
"%2Fetc%2Fpasswd", // absolute path
"sub%2Fx.fab", // separator
"..", // bare traversal
".hidden.fab", // not the exporter's naming (leading dot)
"x.txt", // not a .fab
"..%2Fdecoy.fab", // ../decoy.fab
"..%5Cdecoy.fab", // ..\decoy.fab
"%2Fetc%2Fpasswd", // absolute path
"sub%2Fx.fab", // separator
"..", // bare traversal
".hidden.fab", // not the exporter's naming (leading dot)
"x.txt", // not a .fab
}
for _, f := range refused {
if rr := fetchDownload(s, f); rr.Code != http.StatusBadRequest {
+15 -13
View File
@@ -29,23 +29,25 @@ func (p *fabWebProvider) GetStackDir(string) (string, bool) { return p.stackDir,
func (p *fabWebProvider) GetStackComposePath(string) (string, bool) {
return filepath.Join(p.stackDir, "docker-compose.yml"), true
}
func (p *fabWebProvider) GetStackHDDMounts(string) []string { return []string{appbackup.UserdataDir(p.hddPath)} }
func (p *fabWebProvider) GetStackHDDPath(string) string { return p.hddPath }
func (p *fabWebProvider) GetStackHDDMounts(string) []string {
return []string{appbackup.UserdataDir(p.hddPath)}
}
func (p *fabWebProvider) GetStackHDDPath(string) string { return p.hddPath }
func (p *fabWebProvider) GetStackClassifiedBinds(string) ([]appbackup.ClassifiedBind, bool) {
return p.binds, true
}
func (p *fabWebProvider) IsStackRunning(string) bool { return false }
func (p *fabWebProvider) StopStack(string) error { return nil }
func (p *fabWebProvider) StartStack(string) error { return nil }
func (p *fabWebProvider) GetStackDisplayName(n string) string { return n }
func (p *fabWebProvider) GetStackNeedsHDD(string) bool { return true }
func (p *fabWebProvider) GetDockerVolumes(string) []string { return nil }
func (p *fabWebProvider) IsStackDeployed(string) bool { return true }
func (p *fabWebProvider) GetDecryptedEnv(string) map[string]string { return nil }
func (p *fabWebProvider) GetStacksBaseDir() string { return p.stacksDir }
func (p *fabWebProvider) IsStackRunning(string) bool { return false }
func (p *fabWebProvider) StopStack(string) error { return nil }
func (p *fabWebProvider) StartStack(string) error { return nil }
func (p *fabWebProvider) GetStackDisplayName(n string) string { return n }
func (p *fabWebProvider) GetStackNeedsHDD(string) bool { return true }
func (p *fabWebProvider) GetDockerVolumes(string) []string { return nil }
func (p *fabWebProvider) IsStackDeployed(string) bool { return true }
func (p *fabWebProvider) GetDecryptedEnv(string) map[string]string { return nil }
func (p *fabWebProvider) GetStacksBaseDir() string { return p.stacksDir }
func (p *fabWebProvider) SaveEncryptedAppConfig(string, map[string]string) error { return nil }
func (p *fabWebProvider) RefreshStacks() error { return nil }
func (p *fabWebProvider) RemoveStackVolumes(string) error { return nil }
func (p *fabWebProvider) RefreshStacks() error { return nil }
func (p *fabWebProvider) RemoveStackVolumes(string) error { return nil }
// waitExportDone polls the exporter until the active job finishes.
func waitExportDone(t *testing.T, e *appexport.Exporter) {
@@ -13,8 +13,8 @@ func TestSkipFileBrowserPath(t *testing.T) {
path string
want bool
}{
{StableParentDir + "/flash", false}, // external + mounted → keep
{StableParentDir + "/usb", true}, // external + NOT mounted → skip
{StableParentDir + "/flash", false}, // external + mounted → keep
{StableParentDir + "/usb", true}, // external + NOT mounted → skip
{"/mnt/sys_drive/felhom-data", false}, // system path (not under StableParentDir) → never skip
}
for _, c := range cases {
@@ -34,9 +34,9 @@ func TestFbNeedsRecreate(t *testing.T) {
compose := []byte("services:\n filebrowser:\n image: x\n")
cases := []struct {
name string
oldCfg, newCfg, oldCmp, newCmp []byte
want bool
name string
oldCfg, newCfg, oldCmp, newCmp []byte
want bool
}{
{"unchanged → no recreate", cfg, cfg, compose, compose, false},
{"config differs → recreate", cfg, []byte("sources:\n - /srv/hdd\n"), compose, compose, true},
+1 -1
View File
@@ -174,7 +174,7 @@ func (s *Server) templateFuncMap() template.FuncMap {
},
// isOperational returns true for any state where the stack has containers
// and is not stopped/exited — used by templates for showing action buttons
"isOperational": isOperationalState,
"isOperational": isOperationalState,
"routeUnpublished": routeUnpublished,
"logoURL": func(slug string) string {
return s.cfg.AppLogoURL(slug)
+9 -9
View File
@@ -344,15 +344,15 @@ func (s *Server) driveGateLoop() {
// retried → the app is stuck Exited forever even after the bind lands. The agent reports a `guest_boot_id`
// that changes on every guest boot but is stable across a controller-only restart; the controller
// persists the last-seen value. When it changes (the controller restarts WITH the guest), this:
// 1. gathers the deployed drive-backed apps' stable drive paths,
// 2. GATES on the REAL live in-guest bind — `pollLiveBinds`/`driveBindLive` wait (bounded) until each
// drive's stable path is an actual mountpoint in the controller's own /mnt (rslave) view, which is
// exactly when docker can recreate the app. (The old code sampled the agent's BoundUnderParent
// ONCE during fast startup, raced the ~18s rebind, recreated nothing, and persisted the boot-id —
// burning its one-shot. That is the bug this fixes.)
// 3. recreates every deployed drive-backed app whose bind is now live (`shouldRecreateOnBoot` is
// state-independent, so a stuck-Exited create-time-failure app is included), then persists the
// new boot-id.
// 1. gathers the deployed drive-backed apps' stable drive paths,
// 2. GATES on the REAL live in-guest bind — `pollLiveBinds`/`driveBindLive` wait (bounded) until each
// drive's stable path is an actual mountpoint in the controller's own /mnt (rslave) view, which is
// exactly when docker can recreate the app. (The old code sampled the agent's BoundUnderParent
// ONCE during fast startup, raced the ~18s rebind, recreated nothing, and persisted the boot-id —
// burning its one-shot. That is the bug this fixes.)
// 3. recreates every deployed drive-backed app whose bind is now live (`shouldRecreateOnBoot` is
// state-independent, so a stuck-Exited create-time-failure app is included), then persists the
// new boot-id.
//
// Apps on a drive that never goes live within the window are left to the normal gate (stop→return→
// restart). Single-flight (runs once, before the periodic gate, in driveGateLoop). Best-effort.
+7 -7
View File
@@ -204,7 +204,7 @@ func TestRecreateDriveBackedApps_SyncsFileBrowserAfterRecreate(t *testing.T) {
flash := "/mnt/felhom-drives/felhom-flash"
present := map[string]bool{flash: true}
stacks := []bootStack{
{name: "romm", deployed: true, hdd: flash, hasContainers: true}, // drive-backed, live → recreate
{name: "romm", deployed: true, hdd: flash, hasContainers: true}, // drive-backed, live → recreate
{name: "actualbudget", deployed: true, hdd: "/mnt/sys_drive/felhom-data", hasContainers: true}, // SSD → not recreated
{name: "stranded", deployed: true, hdd: "/mnt/felhom-drives/felhom-usb", hasContainers: true}, // drive-backed, bind NOT live → skipped
}
@@ -247,12 +247,12 @@ func TestStablePathForName(t *testing.T) {
// gate. Both fail here.
func TestPlanDriveGates(t *testing.T) {
paths := []settings.StoragePath{
{Path: "/mnt/felhom-drives/usb"}, // present + connected → no action
{Path: "/mnt/felhom-drives/flash"}, // ABSENT + connected → STOP
{Path: "/mnt/felhom-drives/back", Disconnected: true}, // present + disconnected → RETURN
{Path: "/mnt/felhom-drives/gone", Disconnected: true}, // ABSENT + disconnected → no action (steady)
{Path: "/mnt/felhom-drives/dead", Decommissioned: true}, // decommissioned → never touched
{Path: "/mnt/sys_drive/felhom-data"}, // INTERNAL SSD (absent from agent) → never gated
{Path: "/mnt/felhom-drives/usb"}, // present + connected → no action
{Path: "/mnt/felhom-drives/flash"}, // ABSENT + connected → STOP
{Path: "/mnt/felhom-drives/back", Disconnected: true}, // present + disconnected → RETURN
{Path: "/mnt/felhom-drives/gone", Disconnected: true}, // ABSENT + disconnected → no action (steady)
{Path: "/mnt/felhom-drives/dead", Decommissioned: true}, // decommissioned → never touched
{Path: "/mnt/sys_drive/felhom-data"}, // INTERNAL SSD (absent from agent) → never gated
}
disks := []agentapi.DiskInfo{
// present = BoundUnderParent (the usable-in-guest signal), not merely State==attached.
+7 -7
View File
@@ -24,13 +24,13 @@ func lcStack(name, lifecycle string, deployed bool) stacks.Stack {
// withdrawing an app dangerous in the first place.
func TestVisibleCatalogStacks(t *testing.T) {
in := []stacks.Stack{
lcStack("bookstack", "", false), // available, not deployed → OFFERED
lcStack("immich", "", true), // available, deployed → shown
lcStack("plant-it", "abandoned", false), // withdrawn, not deployed → HIDDEN
lcStack("plant-it-run", "abandoned", true),// withdrawn, DEPLOYED → shown
lcStack("oldapp", "hidden", false), // withdrawn, not deployed → HIDDEN
lcStack("oldapp-run", "hidden", true), // withdrawn, DEPLOYED → shown
lcStack("typoapp", "bogus", false), // unknown → available → OFFERED (fail-open)
lcStack("bookstack", "", false), // available, not deployed → OFFERED
lcStack("immich", "", true), // available, deployed → shown
lcStack("plant-it", "abandoned", false), // withdrawn, not deployed → HIDDEN
lcStack("plant-it-run", "abandoned", true), // withdrawn, DEPLOYED → shown
lcStack("oldapp", "hidden", false), // withdrawn, not deployed → HIDDEN
lcStack("oldapp-run", "hidden", true), // withdrawn, DEPLOYED → shown
lcStack("typoapp", "bogus", false), // unknown → available → OFFERED (fail-open)
}
got := map[string]bool{}
for _, st := range visibleCatalogStacks(in) {
+5 -5
View File
@@ -65,11 +65,11 @@ func TestMobileCSS_StripRemovedDrawerPresent(t *testing.T) {
// The off-canvas drawer + no-JS fallback are present in the mobile block.
for _, want := range []string{
".js .sidebar", // JS path: sidebar becomes an off-canvas drawer
".js .sidebar", // JS path: sidebar becomes an off-canvas drawer
"translateX(-100%)", // parked off-canvas until opened
".no-js .sidebar", // no-JS path: sidebar renders static inline
"body.nav-open", // scroll lock while the drawer is open
".mobile-topbar", // the sticky top bar is styled inside the mobile block
".no-js .sidebar", // no-JS path: sidebar renders static inline
"body.nav-open", // scroll lock while the drawer is open
".mobile-topbar", // the sticky top bar is styled inside the mobile block
} {
if !strings.Contains(block, want) {
t.Errorf("768px block missing %q — the drawer/fallback rules are incomplete", want)
@@ -82,7 +82,7 @@ func TestMobileCSS_StripRemovedDrawerPresent(t *testing.T) {
t.Errorf("stylesheet missing base rule for %q", want)
}
}
if !strings.Contains(css, ".mobile-topbar { display: none") &&
if !strings.Contains(css, ".mobile-topbar { display: none") &&
!strings.Contains(css, ".mobile-topbar{display:none") {
t.Error("no desktop-default `.mobile-topbar { display:none }` — the top bar would leak onto desktop")
}
@@ -49,7 +49,10 @@ func TestNetAddGate_OldAgent_RefusedUpFront(t *testing.T) {
verifyErr: &agentapi.StatusError{Path: "/netstorage/verify-status", Code: http.StatusNotFound},
}
s.netAgentFn = func() (netAgent, error) { return agent, nil }
s.netProbeFn = func(context.Context, string) probeOutcome { t.Error("probe must never run on a gated add"); return probeOutcome{} }
s.netProbeFn = func(context.Context, string) probeOutcome {
t.Error("probe must never run on a gated add")
return probeOutcome{}
}
w := postNetAdd(t, s, "media")
if w.Code != http.StatusPreconditionFailed {
@@ -221,11 +221,12 @@ func TestRunStorageInit_Success(t *testing.T) {
// F6 (VALIDATION-n100) — initialize must end in a USABLE (mounted+registered) drive even when the
// client disconnects mid-format. Two halves:
// RED-PROOF: runStorageInit on a CANCELLED context (the disconnect) aborts at the mount step →
// the device is formatted but NOT registered (the N100-observed state). This is exactly what
// the pre-fix handler did (it ran the chain on r.Context()).
// FIX: startStorageInit runs the chain on a DETACHED context → it registers regardless of the
// client, and leaves EXACTLY ONE registry entry (marker-last, Scenario B).
//
// RED-PROOF: runStorageInit on a CANCELLED context (the disconnect) aborts at the mount step →
// the device is formatted but NOT registered (the N100-observed state). This is exactly what
// the pre-fix handler did (it ran the chain on r.Context()).
// FIX: startStorageInit runs the chain on a DETACHED context → it registers regardless of the
// client, and leaves EXACTLY ONE registry entry (marker-last, Scenario B).
func TestStorageInit_DetachedSurvivesClientDisconnect(t *testing.T) {
newMock := func() *mockAgent {
return &mockAgent{
+4 -4
View File
@@ -29,9 +29,9 @@ type storageInitJob struct {
Where string `json:"where,omitempty"` // the registered stable path (done only)
Phase string `json:"phase"` // formatting | mounting | registering | done | failed | needs_confirmation | refused
Error string `json:"error,omitempty"`
Reason string `json:"reason,omitempty"` // refusal/confirm reason (Hungarian, from the agent)
DurableID string `json:"durable_id,omitempty"` // needs_confirmation: the durable id to confirm against
Opsign string `json:"opsign,omitempty"` // refused: the operator opsign command
Reason string `json:"reason,omitempty"` // refusal/confirm reason (Hungarian, from the agent)
DurableID string `json:"durable_id,omitempty"` // needs_confirmation: the durable id to confirm against
Opsign string `json:"opsign,omitempty"` // refused: the operator opsign command
StartedAt time.Time `json:"started_at"`
UpdatedAt time.Time `json:"updated_at"`
}
@@ -54,7 +54,7 @@ const storageInitDeadline = 65 * time.Minute
// storageInitParams carries the validated init request into the detached job.
type storageInitParams struct {
device, fstype, where, label, durableID string
setDefault, confirmed bool
setDefault, confirmed bool
}
// storageInitState is the single-flight slot (netAddState shape: acquire/release/set/snapshot,
@@ -28,13 +28,13 @@ func TestStorageNetworkTemplate_CanonicalClasses(t *testing.T) {
html := buf.String()
for _, want := range []string{
`class="form-control"`, // the canonical input class
`class="form-group"`, // the canonical field wrapper
"SMB (Synology, QNAP", // protocol-honest ordering: SMB listed first
"NFS (TrueNAS, Linux szerver)", // no bare "ajánlott" claim
"Árva", // the orphan badge renders
"minden felhasználó leképezése", // the Route-A guidance block
"ns-hostid", // the live computed host-id span
`class="form-control"`, // the canonical input class
`class="form-group"`, // the canonical field wrapper
"SMB (Synology, QNAP", // protocol-honest ordering: SMB listed first
"NFS (TrueNAS, Linux szerver)", // no bare "ajánlott" claim
"Árva", // the orphan badge renders
"minden felhasználó leképezése", // the Route-A guidance block
"ns-hostid", // the live computed host-id span
"/api/storage/netstorage/add/status", // the poll-driven progress wiring
} {
if !strings.Contains(html, want) {
@@ -42,8 +42,8 @@ func TestStorageNetworkTemplate_CanonicalClasses(t *testing.T) {
}
}
for _, banned := range []string{
"form-row", // nonexistent class — the old unstyled-look root cause
"form-input", // nonexistent class
"form-row", // nonexistent class — the old unstyled-look root cause
"form-input", // nonexistent class
`<summary class="btn`, // the summary-styled-as-button hack
} {
if strings.Contains(html, banned) {