fix: stabilize software updates
This commit is contained in:
1 parent
fc59092030
commit
26a960c6bd
25 files changed
+487
-146
No files matched your search
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
@@ -204,6 +205,31 @@ func TestRedirectNoticePreservesQueryAndAddsKind(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseFormAcceptsMultipartUpdateFields(t *testing.T) {
|
||||
t.Parallel()
|
||||
var body bytes.Buffer
|
||||
writer := multipart.NewWriter(&body)
|
||||
if err := writer.WriteField("csrf", "csrf-token"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := writer.WriteField("version", "v1.4.2"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := writer.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
request := httptest.NewRequest(http.MethodPost, "/system/update", &body)
|
||||
request.Header.Set("Content-Type", writer.FormDataContentType())
|
||||
response := httptest.NewRecorder()
|
||||
server := &Server{}
|
||||
if err := server.parseForm(response, request); err != nil {
|
||||
t.Fatalf("multipart update form was rejected: status=%d body=%s", response.Code, response.Body.String())
|
||||
}
|
||||
if request.FormValue("csrf") != "csrf-token" || request.FormValue("version") != "v1.4.2" {
|
||||
t.Fatalf("multipart fields were not parsed: %#v", request.Form)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginErrorRendersAsToast(t *testing.T) {
|
||||
t.Parallel()
|
||||
templates, err := loadTemplates()
|
||||
@@ -242,6 +268,7 @@ func TestDashboardRendersLiveUpdateHooks(t *testing.T) {
|
||||
t.Fatalf("load templates: %v", err)
|
||||
}
|
||||
var rendered bytes.Buffer
|
||||
now := time.Date(2026, time.August, 29, 11, 12, 0, 0, time.UTC)
|
||||
data := viewData{
|
||||
Title: "Übersicht",
|
||||
Page: "dashboard",
|
||||
@@ -251,7 +278,8 @@ func TestDashboardRendersLiveUpdateHooks(t *testing.T) {
|
||||
Dashboard: model.Dashboard{
|
||||
Settings: model.Settings{UpdaterEnabled: true, IPv6Enabled: true},
|
||||
SchedulerRunning: true,
|
||||
RecentRuns: []model.UpdateRun{{ID: 1, StartedAt: time.Now(), Status: "running"}},
|
||||
RecentRuns: []model.UpdateRun{{ID: 1, StartedAt: now, Status: "running"}},
|
||||
Zones: []model.Zone{{ID: 7, Name: "example.de", LastSyncAt: now}},
|
||||
},
|
||||
}
|
||||
if err := templates["dashboard"].ExecuteTemplate(&rendered, "dashboard", data); err != nil {
|
||||
@@ -264,7 +292,10 @@ func TestDashboardRendersLiveUpdateHooks(t *testing.T) {
|
||||
`data-dashboard-ipv6`,
|
||||
`data-recent-runs aria-live="polite" aria-busy="true"`,
|
||||
`Aktualisierung läuft`,
|
||||
`features-24`,
|
||||
`features-27`,
|
||||
`data-local-time="1788001920"`,
|
||||
`dashboard-zone-item`,
|
||||
`DNS-Zone · example.de`,
|
||||
} {
|
||||
if !strings.Contains(body, expected) {
|
||||
t.Errorf("rendered dashboard is missing %q", expected)
|
||||
@@ -480,13 +511,14 @@ func TestSoftwareUpdateRendersAdminNotificationAndProgressUI(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
admin := model.User{ID: 1, Username: "admin", DisplayName: "Administrator", Role: "admin", Active: true}
|
||||
updateState := appupdate.State{CurrentVersion: "v1.4.0", LatestVersion: "v2.0.0", UpdateAvailable: true, AgentAvailable: true, AgentCompatible: true, AgentVersion: "v1.4.0", AgentProtocol: 2, CredentialConfigured: true}
|
||||
checkedAt := time.Date(2026, time.August, 29, 11, 12, 0, 0, time.UTC)
|
||||
updateState := appupdate.State{CurrentVersion: "v1.4.0", LatestVersion: "v2.0.0", LastCheckedAt: checkedAt, UpdateAvailable: true, AgentAvailable: true, AgentCompatible: true, AgentVersion: "v1.4.0", AgentProtocol: 2, CredentialConfigured: true, CredentialUsername: "registry-user"}
|
||||
var notification bytes.Buffer
|
||||
accountData := viewData{Title: "Mein Konto", Page: "account", CSRF: "csrf", User: admin, IsAdmin: true, SoftwareUpdate: updateState}
|
||||
if err := templates["security"].ExecuteTemplate(¬ification, "security", accountData); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, expected := range []string{"Neue Version verfügbar", "v2.0.0 ist bereit", `data-software-update-form`, `data-software-update-overlay`} {
|
||||
for _, expected := range []string{"Neue Version verfügbar", "v2.0.0 ist bereit", `data-software-update-banner="v2.0.0"`, `data-software-update-form`, `data-software-update-overlay`} {
|
||||
if !strings.Contains(notification.String(), expected) {
|
||||
t.Errorf("global update notification is missing %q", expected)
|
||||
}
|
||||
@@ -497,7 +529,7 @@ func TestSoftwareUpdateRendersAdminNotificationAndProgressUI(t *testing.T) {
|
||||
if err := templates["system"].ExecuteTemplate(&page, "system", systemData); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, expected := range []string{"Software-Update", "Jetzt aktualisieren", "Image laden", "Dienst wechseln", "Healthcheck", "Update-Zugang ersetzen"} {
|
||||
for _, expected := range []string{"Software-Update", "Jetzt aktualisieren", "Image laden", "Dienst wechseln", "Healthcheck", "Mit Gitea verbunden", "@registry-user", "Zugang ändern", "Passwort/Token", `<details class="update-credential-editor">`, `data-software-update-title`, `data-software-update-progress-label`, `data-software-update-progress-track`, `data-software-update-percent`, `data-local-time="1788001920"`} {
|
||||
if !strings.Contains(page.String(), expected) {
|
||||
t.Errorf("software update page is missing %q", expected)
|
||||
}
|
||||
@@ -507,7 +539,7 @@ func TestSoftwareUpdateRendersAdminNotificationAndProgressUI(t *testing.T) {
|
||||
if err := templates["system"].ExecuteTemplate(&freshPage, "system", freshData); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, expected := range []string{"Nach Update suchen", "Installiert", "Verfügbar", "Zugang fehlt", "Update-Zugang einrichten"} {
|
||||
for _, expected := range []string{"Nach Update suchen", "Installiert", "Status", "Zugang fehlt", "Update-Zugang einrichten"} {
|
||||
if !strings.Contains(freshPage.String(), expected) {
|
||||
t.Errorf("fresh software update page is missing %q", expected)
|
||||
}
|
||||
@@ -515,6 +547,14 @@ func TestSoftwareUpdateRendersAdminNotificationAndProgressUI(t *testing.T) {
|
||||
if strings.Contains(freshPage.String(), "Update-Kanal") {
|
||||
t.Error("confusing legacy update channel box is still rendered")
|
||||
}
|
||||
var currentPage bytes.Buffer
|
||||
currentData := viewData{Title: "Systemupdate", Page: "system", CSRF: "csrf", User: admin, IsAdmin: true, SoftwareUpdate: appupdate.State{CurrentVersion: "v1.4.1", LatestVersion: "v1.4.1", LastCheckedAt: checkedAt, AgentAvailable: true, AgentCompatible: true, CredentialConfigured: true}}
|
||||
if err := templates["system"].ExecuteTemplate(¤tPage, "system", currentData); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(currentPage.String(), `Status</span><strong>Aktuell</strong>`) || strings.Count(currentPage.String(), `>v1.4.1<`) != 1 {
|
||||
t.Fatal("an installed current release is still rendered as a second available version")
|
||||
}
|
||||
var syncPage bytes.Buffer
|
||||
syncData := viewData{Title: "Systemupdate", Page: "system", CSRF: "csrf", User: admin, IsAdmin: true, SoftwareUpdate: appupdate.State{CurrentVersion: "v1.4.0", LatestVersion: "v2.0.0", UpdateAvailable: true, AgentAvailable: true, ComposeSyncRequired: true, CredentialConfigured: true}}
|
||||
if err := templates["system"].ExecuteTemplate(&syncPage, "system", syncData); err != nil {
|
||||
@@ -608,7 +648,7 @@ func TestDomainDetailPlacesStatusAfterTimeAndRendersTTLAndCNAMEControls(t *testi
|
||||
}
|
||||
body := rendered.String()
|
||||
statusAt := strings.Index(body, `A unverändert`)
|
||||
timeAt := strings.Index(body, `class="target-meta"><time>`)
|
||||
timeAt := strings.Index(body, `class="target-meta"><time data-local-time=`)
|
||||
if timeAt < 0 || statusAt < timeAt {
|
||||
t.Fatalf("target time must be rendered before the status pill: %s", body)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user