controller: backups IA split — four sub-pages (Áttekintés /backups, Távoli mentés /backups/remote, Alkalmazások /backups/apps, Visszaállítás /backups/restore); sections MOVED verbatim (backups_split_move_check.py enforces vs df7ad37); shared data builder extracted; offbox restore-to-verify relocated to the restore page; flash redirects + tier-3 anchors + tier2 back-link retargeted
Claude-Session: https://claude.ai/code/session_01GzammAMzsJTgpQHqxwM2bC
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/backup"
|
||||
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
|
||||
)
|
||||
|
||||
// v0.124.0 IA split (§10 nav/state): each backups sub-page renders ONLY its mapped sections —
|
||||
// a marker string per section, asserted present on its page and absent on every other page.
|
||||
|
||||
func splitTestData() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"Page": "backups", "Title": "Biztonsági mentés",
|
||||
"Backup": &backup.FullBackupStatus{
|
||||
AppDataInfo: []backup.AppBackupInfo{{StackName: "calibre-web", DisplayName: "Calibre-Web"}},
|
||||
},
|
||||
"AppBackupRows": []AppBackupRow{
|
||||
{StackName: "calibre-web", DisplayName: "Calibre-Web", OffboxEnabled: true, Tier3State: "active"},
|
||||
},
|
||||
"DBSectionState": "dumps",
|
||||
"Offbox": &settings.OffboxTarget{
|
||||
Enabled: true, Host: "nas.local", LastStatus: "ok", EscrowState: "escrowed",
|
||||
},
|
||||
"OffboxConfigured": true,
|
||||
"OffboxApps": []OffboxAppRow{{Name: "calibre-web", DisplayName: "Calibre-Web", Enabled: true}},
|
||||
"OffboxToggledCount": 1,
|
||||
"OffboxQuotaPct": 0,
|
||||
"GuestBackup": map[string]interface{}{"Available": false, "Note": "n/a"},
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupsSplit_SectionsOnExactlyOnePage(t *testing.T) {
|
||||
pages := map[string]string{
|
||||
"backups": renderBackupPage(t, "backups", splitTestData()),
|
||||
"backups_remote": renderBackupPage(t, "backups_remote", splitTestData()),
|
||||
"backups_apps": renderBackupPage(t, "backups_apps", splitTestData()),
|
||||
"backups_restore": renderBackupPage(t, "backups_restore", splitTestData()),
|
||||
}
|
||||
|
||||
// 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
|
||||
"Ellenőrző visszaállítás a távoli tárolóból": "backups_restore", // moved restore-to-verify
|
||||
`action="/backup/offbox/restore"`: "backups_restore", // the MOVED form itself
|
||||
}
|
||||
for marker, home := range markers {
|
||||
for page, html := range pages {
|
||||
has := strings.Contains(html, marker)
|
||||
if page == home && !has {
|
||||
t.Errorf("%s: missing its own section marker %q", page, marker)
|
||||
}
|
||||
if page != home && has {
|
||||
t.Errorf("%s: renders %q which belongs to %s only", page, marker, home)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The tier-3 row actions must deep-link cross-page to the offbox section (the pre-split
|
||||
// #offbox-section anchors would be orphaned on the apps page).
|
||||
func TestBackupsSplit_CrossPageAnchors(t *testing.T) {
|
||||
data := splitTestData()
|
||||
data["AppBackupRows"] = []AppBackupRow{
|
||||
{StackName: "radarr", DisplayName: "Radarr", Tier3State: "off"},
|
||||
}
|
||||
html := renderBackupPage(t, "backups_apps", data)
|
||||
if !strings.Contains(html, `href="/backups/remote#offbox-section"`) {
|
||||
t.Error("tier-3 'Bekapcsolás' must link cross-page to /backups/remote#offbox-section")
|
||||
}
|
||||
if strings.Contains(html, `href="#offbox-section"`) {
|
||||
t.Error("orphaned same-page #offbox-section anchor survives on the apps page")
|
||||
}
|
||||
}
|
||||
|
||||
// Route → handler wiring: each sub-route sets its own .Page id, so the sidebar child renders
|
||||
// active on the right page (backupMgr nil → the empty state renders, the nav still does).
|
||||
func TestBackupsSplit_RoutesSetPageIDs(t *testing.T) {
|
||||
s := testServer(t)
|
||||
s.loadTemplates()
|
||||
|
||||
cases := []struct {
|
||||
path string
|
||||
active string
|
||||
}{
|
||||
{"/backups/remote", `href="/backups/remote" class="active"`},
|
||||
{"/backups/apps", `href="/backups/apps" class="active"`},
|
||||
{"/backups/restore", `href="/backups/restore" class="active"`},
|
||||
}
|
||||
for _, c := range cases {
|
||||
rr := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", c.path, nil)
|
||||
switch c.path {
|
||||
case "/backups/remote":
|
||||
s.backupsRemoteHandler(rr, req)
|
||||
case "/backups/apps":
|
||||
s.backupsAppsHandler(rr, req)
|
||||
case "/backups/restore":
|
||||
s.backupsRestoreHandler(rr, req)
|
||||
}
|
||||
if rr.Code != 200 {
|
||||
t.Fatalf("%s: status %d", c.path, rr.Code)
|
||||
}
|
||||
if !strings.Contains(rr.Body.String(), c.active) {
|
||||
t.Errorf("%s: sidebar child not active (want %q)", c.path, c.active)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user