/** * Was passiert, wenn Home Assistant neu startet. * * Gemeldet vom Eigentümer (2026-08-31): während eines Neustarts zeigte die App * „502 Error“, und die Meldung blieb stehen, nachdem Home Assistant längst * wieder lief — erst ein Beenden der App half. Zwei Ursachen, beide hier * abgesichert: * * 1. 502 galt als harter Ladefehler statt als „Server kommt gerade hoch“. * 2. Nach dem Wiederverbinden wurde genau einmal nachgeladen. Schlug das * fehl (die Websocket-Verbindung steht regelmäßig ein paar Sekunden vor * der REST-Schnittstelle), kam nie wieder ein „verbunden“ — und damit nie * wieder ein Versuch. */ import { render, screen, waitFor } from "@testing-library/react" import { describe, expect, it, vi } from "vitest" import { ApiFehler, type DataMetricApi } from "../api" import { DatenAnbieter, useDaten } from "./DatenKontext" import { beispielApi } from "../tests/beispieldaten" function Anzeige() { const { bereit, ladefehler } = useDaten() return (
{bereit ? "ja" : "nein"} {ladefehler ?? "-"}
) } /** Eine API, deren `profilLesen` sich auf Kommando wie ein gerade startender Home Assistant verhält: die nächsten n Aufrufe mit 502 abweisen, danach wieder normal antworten. Die Fehlschläge werden bewusst gezählt und nicht an der Aufrufnummer festgemacht: der Horcher meldet beim Abonnieren sofort "verbunden", die Zählung liefe sonst schon beim Aufbau los. */ function apiMitNeustart() { const echt = beispielApi() let verbindungHorcher: ((z: string) => void) | null = null let offeneFehlschlaege = 0 const profilLesen = vi.fn(async () => { if (offeneFehlschlaege > 0) { offeneFehlschlaege -= 1 throw new ApiFehler("Bad Gateway", 502) } return echt.profilLesen() }) const api = { ...echt, profilLesen, live: { ...echt.live, aufVerbindung: (h: (z: string) => void) => { verbindungHorcher = h h("verbunden") return () => {} }, }, } as unknown as DataMetricApi return { api, profilLesen, /** Home Assistant startet neu: die nächsten n Ladeversuche laufen ins 502, dann meldet sich die Websocket-Verbindung zurück. */ neustartMelden: (fehlschlaege: number) => { offeneFehlschlaege = fehlschlaege verbindungHorcher?.("verbunden") }, } } describe("Neustart von Home Assistant", () => { it("meldet 502 nicht als Ladefehler, solange Daten da sind", async () => { const { api, neustartMelden } = apiMitNeustart() render( , ) await waitFor(() => expect(screen.getByTestId("bereit").textContent).toBe("ja")) neustartMelden(1) // Der fehlgeschlagene Versuch darf keine Fehlerzeile hinterlassen. await waitFor(() => expect(screen.getByTestId("fehler").textContent).toBe("-")) expect(screen.getByTestId("bereit").textContent).toBe("ja") }) it("versucht es nach einem fehlgeschlagenen Nachladen erneut", async () => { vi.useFakeTimers({ shouldAdvanceTime: true }) try { const { api, profilLesen, neustartMelden } = apiMitNeustart() render( , ) await waitFor(() => expect(screen.getByTestId("bereit").textContent).toBe("ja")) const vorher = profilLesen.mock.calls.length neustartMelden(2) await waitFor(() => expect(profilLesen.mock.calls.length).toBe(vorher + 1)) // Ohne Wiederholung bliebe es bei diesem einen Versuch — und die App // auf dem Stand von vor dem Neustart, bis jemand sie beendet. await vi.advanceTimersByTimeAsync(3000) await waitFor(() => expect(profilLesen.mock.calls.length).toBe(vorher + 2)) await vi.advanceTimersByTimeAsync(5000) await waitFor(() => expect(profilLesen.mock.calls.length).toBe(vorher + 3)) expect(screen.getByTestId("fehler").textContent).toBe("-") } finally { vi.useRealTimers() } }) }) describe("ApiFehler.istVoruebergehend", () => { it("gilt für kein Netz und für einen startenden Server", () => { expect(new ApiFehler("offline", null).istVoruebergehend).toBe(true) expect(new ApiFehler("bad gateway", 502).istVoruebergehend).toBe(true) expect(new ApiFehler("unavailable", 503).istVoruebergehend).toBe(true) expect(new ApiFehler("timeout", 504).istVoruebergehend).toBe(true) }) it("gilt nicht für einen echten Serverfehler", () => { // 500 bessert sich durch Warten nicht — das muss sichtbar bleiben. expect(new ApiFehler("kaputt", 500).istVoruebergehend).toBe(false) expect(new ApiFehler("nicht da", 404).istVoruebergehend).toBe(false) }) })