import { QueryClient, QueryClientProvider } from "@tanstack/react-query" import { fireEvent, render, screen, waitFor, within } from "@testing-library/react" import { api, post } from "@/lib/api" import type { EinstellungenStand, InstanzMitStand } from "@/lib/typen" import { EinstellungenSeite } from "./Einstellungen" vi.mock("@/lib/api", async (original) => ({ ...(await original()), api: vi.fn(), post: vi.fn(), })) const BOX: EinstellungenStand = { schaltbar: true, update_fenster: { tag: 7, uhrzeit: "04:30", tag_name: "Sonntag", neustart_fenster: "Sonntag 04:00–06:59", timer_aktiv: true, naechster_lauf: "2026-09-27T04:36:00+02:00", wirksam: true, }, radar: { an: true, zustand: "an", naechster_lauf: "2026-09-25T00:30:00+02:00", wirksam: true }, } function instanz(rolle: "box" | "homelab", commit: string): InstanzMitStand { return { rolle, instanz: rolle === "box" ? "Box-Wart" : "Homelab", partner: { eingerichtet: true, name: rolle === "box" ? "Homelab" : "Box-Wart" }, stand: { quelle: "deploy", commit, betreff: "doku: Phase 4", commit_datum: "2026-09-24T20:46:03+02:00", deploy_zeit: "2026-09-24T21:10:00+02:00", }, } } function antworten(partnerCommit = "40d2840") { const daten: Record = { "/api/maintenance/geheimnisse": { hf_token_gesetzt: false, hf_token_aus_env: false, schreibbar: true }, "/api/einstellungen": BOX, "/api/instanz": instanz("box", "40d2840"), "/api/partner/instanz": instanz("homelab", partnerCommit), "/api/partner": { eingerichtet: true, name: "Homelab", erreichbar: true }, "/api/homelab/einstellungen": { arcane: { url: "http://192.168.178.28:3552", schluessel: "fehlt", echt: false, echt_quelle: "standard" }, karenz_h: 48, ausfuehrer: { verbunden: true, zuletzt: 1_790_000_000, nur_lesen: false, sicherung_speicher: "local" }, }, } vi.mocked(api).mockImplementation((async (pfad: string) => { if (pfad in daten) return daten[pfad] throw new Error(`unerwartete Abfrage ${pfad}`) }) as typeof api) } function zeigen() { const qc = new QueryClient({ defaultOptions: { queries: { retry: false } } }) render( , ) } describe("Einstellungen", () => { it("schaltet das Radar erst nach der Rückfrage aus", async () => { antworten() vi.mocked(post).mockResolvedValue({ ok: true, text: "Das Radar ist aus und schläft." }) zeigen() fireEvent.click(await screen.findByRole("button", { name: "Ausschalten" })) const dialog = await screen.findByRole("dialog", { name: "Modell-Radar ausschalten?" }) expect(dialog).toHaveTextContent("Auch ein Deploy schaltet es nicht wieder ein.") expect(post).not.toHaveBeenCalled() fireEvent.click(within(dialog).getByRole("button", { name: "Ausschalten" })) await waitFor(() => expect(post).toHaveBeenCalledWith("/api/einstellungen/radar", { an: false })) }) it("nennt vor dem Speichern des Zeitfensters das neue Neustart-Fenster", async () => { antworten() vi.mocked(post).mockResolvedValue({ ok: true, text: "Gespeichert." }) zeigen() const bereich = await screen.findByRole("region", { name: "Update-Zeitfenster" }) const speichern = await within(bereich).findByRole("button", { name: "Speichern" }) expect(speichern).toBeDisabled() // nichts geändert, der Timer passt fireEvent.change(within(bereich).getByRole("combobox", { name: "Wochentag" }), { target: { value: "6" } }) fireEvent.change(within(bereich).getByRole("combobox", { name: "Uhrzeit" }), { target: { value: "23:30" } }) fireEvent.click(speichern) const dialog = await screen.findByRole("dialog", { name: "Update-Zeitfenster ändern?" }) expect(dialog).toHaveTextContent("jeden Samstag um 23:30 Uhr") expect(dialog).toHaveTextContent("Samstag 23:00 – Sonntag 01:59") expect(post).not.toHaveBeenCalled() fireEvent.click(within(dialog).getByRole("button", { name: "Speichern" })) await waitFor(() => expect(post).toHaveBeenCalledWith("/api/einstellungen/update-fenster", { tag: 6, uhrzeit: "23:30" })) }) it("zeigt beim Telegram-Test des Homelab-Teils den Grund aus dem Melde-Log", async () => { antworten() vi.mocked(post).mockResolvedValue({ ok: false, gesendet: false, weg: null, grund: "Zweitweg: Telegram lehnt ab, HTTP-Fehler", detail: "Die Testmeldung ging nicht raus.", instanz: "Homelab", zeit: "2026-09-24T21:14:00+02:00", }) zeigen() fireEvent.click(await screen.findByRole("button", { name: "Testmeldung von Homelab senden" })) await waitFor(() => expect(post).toHaveBeenCalledWith("/api/homelab/einstellungen/telegram-test")) expect(await screen.findByText("Nicht gesendet: Zweitweg: Telegram lehnt ab, HTTP-Fehler")).toBeInTheDocument() }) it("zeigt beide Software-Stände und warnt, wenn sie abweichen", async () => { antworten("37133fd") zeigen() expect(await screen.findByText(/laufen mit verschiedenen Ständen/)).toBeInTheDocument() expect(screen.getByText("40d2840")).toBeInTheDocument() expect(screen.getByText("37133fd")).toBeInTheDocument() expect(screen.getAllByText(/Live seit 24\.09\.2026, 21:10/)).toHaveLength(2) }) it("schweigt bei gleichem Stand, auch wenn ein Hash länger ist", async () => { antworten("40d2840a") zeigen() expect(await screen.findByText("40d2840a")).toBeInTheDocument() expect(screen.queryByText(/laufen mit verschiedenen Ständen/)).not.toBeInTheDocument() }) })