fix(desktop): Update-Eintrag nennt den Grund einer fehlgeschlagenen Pruefung, Klick prueft erneut, Pruefung alle 4 h
Befund 22.09.2026: alpha bietet 1.2.0-beta.gc001a08 an, der Client auf
a6d1a64 zeigt aber nur den grauen Eintrag "Update installieren". Der
Nginx Proxy Manager vor alpha beantwortet die Update-Anfrage mit 401
(Basic-Auth); die Webansicht kann das Passwortfenster beantworten, der
Updater (eigener reqwest-Client) nicht. tauri-plugin-updater verschluckt
einen Nicht-2xx-Status (updater.rs Z. 529-559: nur Log, last_error
leer, Ergebnis Err(ReleaseNotFound)), und spawn_version_check fing das
mit Err(_) => {} stumm ab. Ein fehlgeschlagener Check war damit vom
Zustand "kein Update" nicht unterscheidbar, und ohne Neustart gab es
keinen Weg, erneut zu pruefen.
- Drei Endzustaende, alle anklickbar: "Auf Version/Beta-Stand ...
aktualisieren", "Kein Update verfuegbar - erneut pruefen",
"Update-Pruefung fehlgeschlagen (HTTP n | keine Verbindung) - erneut
pruefen"; waehrend der Pruefung "Suche nach Updates..." (gesperrt),
http-Server unveraendert gesperrt
- Bei ReleaseNotFound stellt der Client dieselbe Anfrage einmal selbst
(Platzhalter ersetzt, Timeout 8 s) und liest nur den Statuscode;
401/403 erklaeren den Proxy-Passwortschutz, Zugangsdaten werden
bewusst NICHT in den Client eingebaut
- Benachrichtigung je unterschiedlichem Fehlertext einmal
(LastCheckNotice), Erfolg leert die Entprellung
- Wiederhol-Thread alle 4 h (std::thread, kein neues Crate), liest die
Adresse frisch, ueberspringt bei bereits abgelegtem Update
- Klick ohne abgelegtes Update prueft erneut; der Browser-Weg bleibt nur
Rueckfall einer fehlgeschlagenen Installation
- 7 neue Tests (Labels, Diagnose-URL, Konstanten), zuvor rot (E0425)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T
|
||||
|
||||
### Behoben
|
||||
|
||||
- Desktop-App: der Update-Eintrag im Menü des Infobereich-Symbols bleibt nicht mehr stumm ausgegraut – schlägt die Update-Prüfung fehl, steht der Grund im Eintrag (z. B. „HTTP 401“, wenn ein Passwortschutz am Proxy die Anfrage abweist) und ein Klick prüft erneut; die App prüft außerdem alle vier Stunden, nicht mehr nur beim Start
|
||||
- Favoriten-Widget: kein Symbol bei Seiten mit Zertifikatsfehler oder internen Adressen – das Symbol wird jetzt trotz Zertifikatsfehler geholt, bei internen Adressen versucht es der Browser direkt
|
||||
|
||||
## 1.2.0 – 2026-09-17
|
||||
|
||||
@@ -12,13 +12,24 @@ use tauri_plugin_opener::OpenerExt;
|
||||
use tauri_plugin_store::StoreExt;
|
||||
use tauri_plugin_updater::{Update, UpdaterExt};
|
||||
|
||||
/// Standardtext des Update-Eintrags im Tray-Menü, bevor eine neuere Version
|
||||
/// gefunden wurde. Der Eintrag installiert das Update seit quick-260917-kgc
|
||||
/// direkt in der App (Download, Signaturpruefung, Installation, Neustart);
|
||||
/// den Browser oeffnet er nur noch als Rueckfall. `spawn_version_check`
|
||||
/// setzt den Eintrag nach einem Serverwechsel hierauf zurück, damit kein
|
||||
/// Hinweis des alten Servers stehen bleibt.
|
||||
const UPDATE_ITEM_DEFAULT: &str = "Update installieren";
|
||||
/// Menuetext des Update-Eintrags, solange eine Pruefung laeuft (gesperrt).
|
||||
/// `spawn_version_check` setzt den Eintrag zu Beginn hierauf, auch nach einem
|
||||
/// Serverwechsel, damit kein Hinweis des alten Servers stehen bleibt. Der
|
||||
/// Eintrag installiert ein gefundenes Update seit quick-260917-kgc direkt in
|
||||
/// der App (Download, Signaturpruefung, Installation, Neustart).
|
||||
const UPDATE_ITEM_CHECKING: &str = "Suche nach Updates…";
|
||||
|
||||
/// Menuetext, wenn die Pruefung kein Update ergab. Der Eintrag bleibt
|
||||
/// anklickbar und startet die Pruefung erneut (quick-260922-frg): der Nutzer
|
||||
/// soll nie vor einem stumm ausgegrauten Eintrag stehen. Auch der Starttext
|
||||
/// ohne gespeicherte Server-Adresse (Klick ohne Adresse tut nichts).
|
||||
const UPDATE_ITEM_NONE: &str = "Kein Update verfügbar – erneut prüfen";
|
||||
|
||||
/// Abstand der wiederholten Update-Pruefung. Die Tray-App laeuft tagelang
|
||||
/// durch; mit der Pruefung nur beim Start hat ein Client ein neues Paket auf
|
||||
/// dem Server nie gesehen (Befund 22.09.2026, quick-260922-frg). Vier
|
||||
/// Stunden reichen fuer den Beta-Kanal und belasten den Server nicht.
|
||||
const UPDATE_CHECK_INTERVAL: Duration = Duration::from_secs(4 * 3600);
|
||||
|
||||
/// Menuetext, wenn die gespeicherte Server-Adresse mit `http://` beginnt:
|
||||
/// das Updater-Plugin verweigert im Release-Bau unverschluesselte Endpunkte
|
||||
@@ -37,6 +48,15 @@ struct VersionResponse {
|
||||
/// `Update` ist Clone + Send + Sync, `app.manage` verlangt das.
|
||||
struct PendingUpdate(Mutex<Option<(Update, String)>>);
|
||||
|
||||
/// Benachrichtigungstext der zuletzt gemeldeten fehlgeschlagenen
|
||||
/// Update-Pruefung. Die Pruefung laeuft alle `UPDATE_CHECK_INTERVAL` erneut;
|
||||
/// gegen einen dauerhaft sperrenden Proxy wuerde sonst alle vier Stunden
|
||||
/// dieselbe Meldung erscheinen (T-FRG-02). Nur ein ABWEICHENDER Text wird
|
||||
/// erneut gemeldet; ein Erfolg (`Ok(..)`) leert den Wert, damit ein spaeterer
|
||||
/// Fehler wieder sichtbar wird. Eigener `app.manage`-Typ, damit
|
||||
/// `spawn_version_check` seine Signatur behaelt.
|
||||
struct LastCheckNotice(Mutex<String>);
|
||||
|
||||
/// Baut die Adresse eines API-Pfads aus der gespeicherten Server-Adresse.
|
||||
/// Die API ist vom Client nur ueber den Web-Ursprung erreichbar
|
||||
/// (Next.js-Rewrite `/api-proxy/*`, siehe 18-01) -- niemals direkt unter dem
|
||||
@@ -76,7 +96,7 @@ fn with_client_marker(url: &tauri::Url, version: &str, commit: &str, os: &str) -
|
||||
/// aeltere Middleware weiterhin als Desktop-App erkannt wird. Die Werte
|
||||
/// gehen NUR in die Navigation, niemals in den gespeicherten Server-Wert
|
||||
/// (`server_url` im Store bleibt die reine Adresse). Der Browser-Rueckfall
|
||||
/// des Tray-Eintrags "Update installieren" (`open_download_page`) bekommt
|
||||
/// einer fehlgeschlagenen Update-Installation (`open_download_page`) bekommt
|
||||
/// den Marker bewusst NICHT -- er oeffnet im System-Browser, dort muessen
|
||||
/// die Download-Links sichtbar bleiben.
|
||||
fn with_desktop_marker(url: &tauri::Url) -> tauri::Url {
|
||||
@@ -179,6 +199,78 @@ fn update_endpoint(server: &str) -> Option<tauri::Url> {
|
||||
Some(url)
|
||||
}
|
||||
|
||||
/// Baut die Update-Adresse mit ERSETZTEN Platzhaltern -- dieselbe Anfrage,
|
||||
/// die das Plugin stellt (`update_endpoint`, gleiche Kodierung ueber
|
||||
/// `query_pairs_mut`), nur mit konkreten Werten. `spawn_version_check` stellt
|
||||
/// sie nach `Err(ReleaseNotFound)` einmal selbst, um den Statuscode zu
|
||||
/// erfahren: das Plugin verschluckt einen Nicht-2xx-Status (updater.rs
|
||||
/// Z. 529-559 loggt nur, `last_error` bleibt leer, Ergebnis ist
|
||||
/// `ReleaseNotFound`). Reine Funktion, darum in den Tests mit festen Werten
|
||||
/// geprueft.
|
||||
fn diagnostic_update_url(server: &str, target: &str, arch: &str, current: &str) -> String {
|
||||
let base = server.trim_end_matches('/');
|
||||
match tauri::Url::parse(&api_url(server, "/desktop/update")) {
|
||||
Ok(mut url) => {
|
||||
url.query_pairs_mut()
|
||||
.append_pair("target", target)
|
||||
.append_pair("arch", arch)
|
||||
.append_pair("current", current)
|
||||
.append_pair("base", base);
|
||||
url.into()
|
||||
}
|
||||
// Gespeicherte Adressen sind immer parsebar (parse_server_url);
|
||||
// reine Absicherung, damit die Funktion kein Option liefern muss.
|
||||
Err(_) => api_url(server, "/desktop/update"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Liefert (Menuetext, Benachrichtigungstext) fuer eine fehlgeschlagene
|
||||
/// Update-Pruefung. `status` ist der Antwortcode des Servers, wenn er
|
||||
/// geantwortet hat (`Some`), sonst `None` (keine Verbindung, TLS, Timeout).
|
||||
/// 401/403 bekommen die Proxy-Erklaerung: Befund 22.09.2026 -- der Nginx
|
||||
/// Proxy Manager vor alpha verlangt Basic-Auth, die Webansicht beantwortet
|
||||
/// das Passwortfenster, der Updater (eigener `reqwest`-Client) kann es nicht.
|
||||
/// Zugangsdaten gehoeren nicht in ausgelieferte Clients (T-FRG-03), darum
|
||||
/// wird der Grund angezeigt, nicht umgangen. Alle Texte enden auf
|
||||
/// "– erneut prüfen", weil der Klick die Pruefung wiederholt.
|
||||
fn check_failure_labels(status: Option<u16>) -> (String, String) {
|
||||
match status {
|
||||
Some(code @ (401 | 403)) => (
|
||||
format!("Update-Prüfung fehlgeschlagen (HTTP {code}) – erneut prüfen"),
|
||||
format!(
|
||||
"Der Server hat die Update-Anfrage mit HTTP {code} abgewiesen. Meist steht ein \
|
||||
Passwortschutz oder eine Zugriffsliste am vorgeschalteten Proxy davor, die die \
|
||||
App für Updates nicht durchlaufen kann. Anmeldung und Arbeiten in der App sind \
|
||||
davon nicht betroffen."
|
||||
),
|
||||
),
|
||||
Some(code) => (
|
||||
format!("Update-Prüfung fehlgeschlagen (HTTP {code}) – erneut prüfen"),
|
||||
format!("Der Server hat auf die Update-Anfrage mit HTTP {code} geantwortet statt mit Paketdaten."),
|
||||
),
|
||||
None => (
|
||||
"Update-Prüfung fehlgeschlagen (keine Verbindung) – erneut prüfen".to_string(),
|
||||
"Der Server war für die Update-Prüfung nicht erreichbar. Die App prüft in vier \
|
||||
Stunden erneut – oder über den Menüeintrag."
|
||||
.to_string(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Stellt die Update-Anfrage ein zweites Mal mit eigenem `reqwest`-Client
|
||||
/// (Timeout 8 s, Muster `check_server`) und liest NUR den Statuscode -- der
|
||||
/// Rumpf wird nicht ausgewertet, Redirects folgen dem Standard (T-FRG-01:
|
||||
/// Ziel ist die vom Nutzer gespeicherte Adresse, kein Geheimnis in der
|
||||
/// Anfrage). `None`, wenn keine Antwort kam.
|
||||
async fn probe_update_status(url: String) -> Option<u16> {
|
||||
let client = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(8))
|
||||
.build()
|
||||
.ok()?;
|
||||
let resp = client.get(url).send().await.ok()?;
|
||||
Some(resp.status().as_u16())
|
||||
}
|
||||
|
||||
/// Leitet (Menuetext, Benachrichtigungstext) aus Client- und Server-Version
|
||||
/// ab: eine neue Basis X.Y.Z wird als Version genannt (auch bei Beta), bei
|
||||
/// gleicher Basis der Beta-Stempel.
|
||||
@@ -321,14 +413,57 @@ async fn check_for_update(
|
||||
.await
|
||||
}
|
||||
|
||||
/// Meldet eine fehlgeschlagene Update-Pruefung: Menuetext mit Grund, Eintrag
|
||||
/// wieder anklickbar (Klick = erneut pruefen), Benachrichtigung nur, wenn
|
||||
/// der Text vom zuletzt gemeldeten abweicht (`LastCheckNotice`, T-FRG-02).
|
||||
fn report_check_failure(app: &AppHandle, update_item: &MenuItem<Wry>, status: Option<u16>) {
|
||||
let (menu_text, body) = check_failure_labels(status);
|
||||
let _ = update_item.set_text(&menu_text);
|
||||
let _ = update_item.set_enabled(true);
|
||||
let changed = app
|
||||
.state::<LastCheckNotice>()
|
||||
.0
|
||||
.lock()
|
||||
.map(|mut last| {
|
||||
if *last == body {
|
||||
false
|
||||
} else {
|
||||
*last = body.clone();
|
||||
true
|
||||
}
|
||||
})
|
||||
.unwrap_or(true);
|
||||
if changed {
|
||||
let _ = app
|
||||
.notification()
|
||||
.builder()
|
||||
.title("Tessera-Update")
|
||||
.body(body)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/// Leert den zuletzt gemeldeten Fehlertext -- nach einer gelungenen Pruefung
|
||||
/// soll ein spaeterer Fehler wieder gemeldet werden.
|
||||
fn clear_check_notice(app: &AppHandle) {
|
||||
if let Ok(mut last) = app.state::<LastCheckNotice>().0.lock() {
|
||||
last.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Versionsprüfung, aus `setup` herausgezogen, damit sie nach einem
|
||||
/// Serverwechsel erneut laufen kann (async, non-blocking wie bisher). Setzt
|
||||
/// den Update-Eintrag zuerst auf den Standardtext zurück und leert den
|
||||
/// abgelegten Update-Stand -- nach einem Wechsel darf kein Hinweis des alten
|
||||
/// Servers stehen bleiben.
|
||||
/// Serverwechsel, beim Klick auf den Eintrag und alle
|
||||
/// `UPDATE_CHECK_INTERVAL` erneut laufen kann (async, non-blocking wie
|
||||
/// bisher). Setzt den Update-Eintrag zuerst auf "Suche nach Updates…"
|
||||
/// (gesperrt) und leert den abgelegten Update-Stand -- nach einem Wechsel
|
||||
/// darf kein Hinweis des alten Servers stehen bleiben. Jeder Ausgang der
|
||||
/// Pruefung endet in einem sichtbaren, anklickbaren Zustand (Update gefunden
|
||||
/// / kein Update / fehlgeschlagen mit Grund); nur der http-Fall bleibt
|
||||
/// gesperrt. Ein fehlgeschlagener Check war bisher vom Zustand "kein Update"
|
||||
/// nicht unterscheidbar (quick-260922-frg).
|
||||
fn spawn_version_check(app: AppHandle, server_url: String) {
|
||||
let update_item = app.state::<TrayItems>().update.clone();
|
||||
let _ = update_item.set_text(UPDATE_ITEM_DEFAULT);
|
||||
let _ = update_item.set_text(UPDATE_ITEM_CHECKING);
|
||||
let _ = update_item.set_enabled(false);
|
||||
if let Ok(mut pending) = app.state::<PendingUpdate>().0.lock() {
|
||||
*pending = None;
|
||||
@@ -365,8 +500,13 @@ fn spawn_version_check(app: AppHandle, server_url: String) {
|
||||
if let Ok(mut pending) = app.state::<PendingUpdate>().0.lock() {
|
||||
*pending = Some((update, menu_text));
|
||||
}
|
||||
clear_check_notice(&app);
|
||||
}
|
||||
Ok(None) => {
|
||||
let _ = update_item.set_text(UPDATE_ITEM_NONE);
|
||||
let _ = update_item.set_enabled(true);
|
||||
clear_check_notice(&app);
|
||||
}
|
||||
Ok(None) => {}
|
||||
// Der Release-Bau verweigert http-Endpunkte (config.rs
|
||||
// `validate_endpoints`), der Debug-Bau warnt nur. Keine
|
||||
// `dangerousInsecureTransportProtocol`-Freigabe (T-KGC-06): der
|
||||
@@ -374,15 +514,33 @@ fn spawn_version_check(app: AppHandle, server_url: String) {
|
||||
Err(tauri_plugin_updater::Error::InsecureTransportProtocol) => {
|
||||
let _ = update_item.set_text(UPDATE_ITEM_INSECURE);
|
||||
}
|
||||
Err(_) => {}
|
||||
// Der Server hat geantwortet, aber nicht mit 2xx/204: das Plugin
|
||||
// verschluckt den Statuscode (updater.rs Z. 529-559). Dieselbe
|
||||
// Anfrage einmal selbst stellen und nur den Status lesen -- so
|
||||
// steht im Eintrag "HTTP 401", wenn ein Passwortschutz am Proxy
|
||||
// die Anfrage abweist (Befund 22.09.2026).
|
||||
Err(tauri_plugin_updater::Error::ReleaseNotFound) => {
|
||||
let status = probe_update_status(diagnostic_update_url(
|
||||
&server_url,
|
||||
std::env::consts::OS,
|
||||
std::env::consts::ARCH,
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
))
|
||||
.await;
|
||||
report_check_failure(&app, &update_item, status);
|
||||
}
|
||||
// Keine Verbindung, TLS, Timeout (Reqwest/Network/Io) oder
|
||||
// unlesbares Manifest: keine zweite Anfrage, Status unbekannt.
|
||||
Err(_) => report_check_failure(&app, &update_item, None),
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Oeffnet die Seite Einstellungen → Desktop-App im System-Browser -- der
|
||||
/// bisherige Weg des Tray-Eintrags, jetzt Rueckfall, wenn kein Update
|
||||
/// abgelegt ist oder die Installation in der App fehlschlaegt. Bewusst ohne
|
||||
/// `desktop=1` (siehe `with_desktop_marker`).
|
||||
/// bisherige Weg des Tray-Eintrags, seit quick-260922-frg NUR noch Rueckfall,
|
||||
/// wenn die Installation in der App fehlschlaegt (ein Klick ohne abgelegtes
|
||||
/// Update prueft stattdessen erneut). Bewusst ohne `desktop=1` (siehe
|
||||
/// `with_desktop_marker`).
|
||||
fn open_download_page(app: &AppHandle) {
|
||||
if let Some(server) = stored_server_url(app) {
|
||||
let target = format!("{}/settings/general/desktop", server.trim_end_matches('/'));
|
||||
@@ -566,13 +724,16 @@ pub fn run() {
|
||||
}
|
||||
|
||||
// Tray menu: Verbunden mit ... (gesperrt) · — · Öffnen ·
|
||||
// Server-Adresse ändern... · Update installieren · — ·
|
||||
// Server-Adresse ändern... · Suche nach Updates… · — ·
|
||||
// Autostart-Haken · — · Beenden.
|
||||
// "connected" ist nicht anklickbar; "update" bleibt gesperrt, bis
|
||||
// die Versionspruefung eine neuere Version findet, und heisst dann
|
||||
// "Auf Version X.Y.Z aktualisieren" bzw. "Auf Beta-Stand ...
|
||||
// aktualisieren"; "autostart" spiegelt den tatsaechlichen
|
||||
// Systemzustand beim Start.
|
||||
// "connected" ist nicht anklickbar; "update" startet gesperrt als
|
||||
// "Suche nach Updates…", weil die Erstpruefung unten sofort
|
||||
// anlaeuft, und heisst danach "Auf Version X.Y.Z aktualisieren"
|
||||
// bzw. "Auf Beta-Stand ... aktualisieren", "Kein Update verfügbar
|
||||
// – erneut prüfen" oder "Update-Prüfung fehlgeschlagen (...) –
|
||||
// erneut prüfen" (alle anklickbar). Ohne gespeicherte Adresse
|
||||
// "Kein Update verfügbar – erneut prüfen" (Klick tut nichts);
|
||||
// "autostart" spiegelt den tatsaechlichen Systemzustand beim Start.
|
||||
let connected =
|
||||
MenuItemBuilder::with_id("connected", tray_labels(server_url.as_deref()).1)
|
||||
.enabled(false)
|
||||
@@ -580,8 +741,13 @@ pub fn run() {
|
||||
let open = MenuItemBuilder::with_id("open", "Öffnen").build(app)?;
|
||||
let change_server =
|
||||
MenuItemBuilder::with_id("change_server", "Server-Adresse ändern…").build(app)?;
|
||||
let update = MenuItemBuilder::with_id("update", UPDATE_ITEM_DEFAULT)
|
||||
.enabled(false)
|
||||
let update_text = if server_url.is_some() {
|
||||
UPDATE_ITEM_CHECKING
|
||||
} else {
|
||||
UPDATE_ITEM_NONE
|
||||
};
|
||||
let update = MenuItemBuilder::with_id("update", update_text)
|
||||
.enabled(server_url.is_none())
|
||||
.build(app)?;
|
||||
let autostart_label = if cfg!(target_os = "windows") {
|
||||
"Mit Windows starten"
|
||||
@@ -610,6 +776,7 @@ pub fn run() {
|
||||
update: update.clone(),
|
||||
});
|
||||
app.manage(PendingUpdate(Mutex::new(None)));
|
||||
app.manage(LastCheckNotice(Mutex::new(String::new())));
|
||||
|
||||
let autostart_for_menu = autostart.clone();
|
||||
|
||||
@@ -637,8 +804,12 @@ pub fn run() {
|
||||
}
|
||||
"update" => {
|
||||
// `take()` verhindert Doppelklick-Downloads, der
|
||||
// gesperrte Eintrag ebenso; ohne abgelegten Stand
|
||||
// bleibt der Browser-Weg (Einstellungen → Desktop-App).
|
||||
// gesperrte Eintrag ebenso. Ohne abgelegten Stand
|
||||
// ("Kein Update verfügbar" / "Update-Prüfung
|
||||
// fehlgeschlagen") prueft der Klick erneut -- der
|
||||
// Browser-Weg (`open_download_page`) ist nur noch
|
||||
// Rueckfall einer fehlgeschlagenen Installation.
|
||||
// Ohne gespeicherte Adresse gibt es nichts zu pruefen.
|
||||
let pending = app
|
||||
.state::<PendingUpdate>()
|
||||
.0
|
||||
@@ -649,7 +820,11 @@ pub fn run() {
|
||||
Some((update, menu_text)) => {
|
||||
spawn_update_install(app.clone(), update, menu_text)
|
||||
}
|
||||
None => open_download_page(app),
|
||||
None => {
|
||||
if let Some(url) = stored_server_url(app) {
|
||||
spawn_version_check(app.clone(), url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"autostart" => {
|
||||
@@ -696,6 +871,34 @@ pub fn run() {
|
||||
spawn_version_check(app.handle().clone(), url);
|
||||
}
|
||||
|
||||
// Wiederholte Pruefung alle UPDATE_CHECK_INTERVAL. Bewusst ein
|
||||
// schlichter Betriebssystem-Thread mit `sleep` statt
|
||||
// `tokio::time`: das Tauri-Async-Runtime braeuchte dafuer das
|
||||
// `time`-Feature bzw. ein eigenes Crate, und ein schlafender
|
||||
// Thread kostet nichts. Je Durchlauf wird die Adresse frisch
|
||||
// gelesen (Serverwechsel zaehlt). Haelt `PendingUpdate` bereits
|
||||
// ein Update, wird der Durchlauf uebersprungen: die Pruefung
|
||||
// wuerde den Stand leeren und "Neuer Beta-Stand" erneut melden.
|
||||
// Waehrend einer Installation ist der Stand per `take()` leer;
|
||||
// faellt die 4-h-Marke genau in den Download, wird nur der
|
||||
// Fortschrittstext ueberschrieben (T-FRG-04, akzeptiert).
|
||||
let handle = app.handle().clone();
|
||||
std::thread::spawn(move || loop {
|
||||
std::thread::sleep(UPDATE_CHECK_INTERVAL);
|
||||
let pending = handle
|
||||
.state::<PendingUpdate>()
|
||||
.0
|
||||
.lock()
|
||||
.map(|guard| guard.is_some())
|
||||
.unwrap_or(false);
|
||||
if pending {
|
||||
continue;
|
||||
}
|
||||
if let Some(url) = stored_server_url(&handle) {
|
||||
spawn_version_check(handle.clone(), url);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.on_window_event(|window, event| {
|
||||
@@ -1008,4 +1211,63 @@ mod tests {
|
||||
Err("Diese Adresse ist ungültig.".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_failure_labels_401_nennt_proxy_passwortschutz() {
|
||||
let (menu_text, body) = check_failure_labels(Some(401));
|
||||
assert!(menu_text.contains("HTTP 401"));
|
||||
assert!(menu_text.ends_with("– erneut prüfen"));
|
||||
assert!(body.contains("HTTP 401"));
|
||||
assert!(body.contains("Passwortschutz"));
|
||||
assert!(body.contains("Zugriffsliste"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_failure_labels_403_gleiche_erklaerung() {
|
||||
let (menu_text, body) = check_failure_labels(Some(403));
|
||||
assert!(menu_text.contains("HTTP 403"));
|
||||
assert!(body.contains("HTTP 403"));
|
||||
assert!(body.contains("Passwortschutz"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_failure_labels_anderer_status_ohne_proxy_erklaerung() {
|
||||
let (menu_text, body) = check_failure_labels(Some(502));
|
||||
assert!(menu_text.contains("HTTP 502"));
|
||||
assert!(menu_text.ends_with("– erneut prüfen"));
|
||||
assert!(!body.contains("Passwortschutz"));
|
||||
assert!(body.contains("statt mit Paketdaten"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_failure_labels_ohne_status_keine_verbindung() {
|
||||
let (menu_text, body) = check_failure_labels(None);
|
||||
assert!(menu_text.contains("keine Verbindung"));
|
||||
assert!(menu_text.ends_with("– erneut prüfen"));
|
||||
assert!(body.contains("vier Stunden"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagnostic_update_url_ersetzt_platzhalter_und_kodiert_base() {
|
||||
assert_eq!(
|
||||
diagnostic_update_url("https://alpha.example", "windows", "x86_64", "1.2.0"),
|
||||
"https://alpha.example/api-proxy/desktop/update?target=windows&arch=x86_64¤t=1.2.0&base=https%3A%2F%2Falpha.example"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagnostic_update_url_trimmt_schlussstrich() {
|
||||
assert_eq!(
|
||||
diagnostic_update_url("https://alpha.example/", "linux", "x86_64", "1.2.0"),
|
||||
"https://alpha.example/api-proxy/desktop/update?target=linux&arch=x86_64¤t=1.2.0&base=https%3A%2F%2Falpha.example"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_item_konstanten() {
|
||||
assert_eq!(UPDATE_ITEM_CHECKING, "Suche nach Updates…");
|
||||
assert_eq!(UPDATE_ITEM_NONE, "Kein Update verfügbar – erneut prüfen");
|
||||
assert_eq!(UPDATE_ITEM_INSECURE, "Update nur über https möglich");
|
||||
assert_eq!(UPDATE_CHECK_INTERVAL, Duration::from_secs(4 * 3600));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user