milestone 19: hygiene sweep - dead ecs surface, single-source constants, tls classification, frontend state hazards, docker smoke network fix
CI / test (push) Successful in 1m46s
CI / test-aarch64 (push) Successful in 5m30s
CI / frontend (push) Successful in 46s
CI / cross (push) Successful in 8m12s
CI / docker (push) Successful in 3m46s

This commit is contained in:
2026-08-07 20:39:27 +02:00
parent 6f67940995
commit 6c507992e4
59 changed files with 1020 additions and 382 deletions
@@ -1,11 +1,12 @@
import { Suspense } from "react";
import { QueryClientProvider } from "@tanstack/react-query";
import { QueryClientProvider, type QueryClient } from "@tanstack/react-query";
import { fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { act } from "react";
import SettingsPage, { patchRequiresRestart } from "@/features/settings/SettingsPage";
import RestartBanner from "@/features/settings/RestartBanner";
import { dismissRestartBanner } from "@/features/settings/restartBanner";
import { createQueryClient } from "@/lib/queryClient";
import { queryKeys } from "@/lib/queries";
import type { Settings, SettingsPatch } from "@/lib/types";
function baseSettings(): Settings {
@@ -88,9 +89,10 @@ afterEach(() => {
vi.unstubAllGlobals();
});
async function renderPage() {
async function renderPage(): Promise<QueryClient> {
const queryClient = createQueryClient();
render(
<QueryClientProvider client={createQueryClient()}>
<QueryClientProvider client={queryClient}>
<RestartBanner />
<Suspense fallback={<p>loading</p>}>
<SettingsPage />
@@ -98,6 +100,7 @@ async function renderPage() {
</QueryClientProvider>,
);
await screen.findByRole("heading", { name: "Settings" });
return queryClient;
}
function saveButton(): HTMLButtonElement {
@@ -232,3 +235,39 @@ test("patchRequiresRestart ignores only a bare web.password", () => {
expect(patchRequiresRestart({ dns: { port: 5353 } })).toBe(true);
expect(patchRequiresRestart({ web: { password: "x" }, cache: { size: 1 } })).toBe(true);
});
test("a background refetch does not turn out-of-band changes into phantom patch entries", async () => {
const queryClient = await renderPage();
const dns = screen.getByRole("group", { name: "DNS" });
fireEvent.change(within(dns).getByLabelText("port"), { target: { value: "5353" } });
// Someone else changes cache.size; a background refetch brings it in. The
// derived auth_enabled line is read straight from the query data, so it
// witnesses that the refetch reached the component.
storedSettings.cache.size = 99999;
storedSettings.web.auth_enabled = false;
await act(async () => {
await queryClient.invalidateQueries({ queryKey: queryKeys.settings });
});
await waitFor(() => expect(screen.getByText(/auth_enabled: false/)).toBeTruthy());
const cache = screen.getByRole("group", { name: "Cache" });
expect((within(cache).getByLabelText("size") as HTMLInputElement).value).toBe("10000");
fireEvent.click(saveButton());
await waitFor(() => expect(putBodies).toHaveLength(1));
expect(putBodies[0]).toEqual({ dns: { port: 5353 } });
});
test("saving re-freezes the baseline, so the next diff starts from the server echo", async () => {
await renderPage();
const dns = screen.getByRole("group", { name: "DNS" });
fireEvent.change(within(dns).getByLabelText("port"), { target: { value: "5353" } });
fireEvent.click(saveButton());
await waitFor(() => expect(putBodies).toHaveLength(1));
await waitFor(() => expect(saveButton().disabled).toBe(true));
fireEvent.change(within(dns).getByLabelText("port"), { target: { value: "5454" } });
fireEvent.click(saveButton());
await waitFor(() => expect(putBodies).toHaveLength(2));
expect(putBodies[1]).toEqual({ dns: { port: 5454 } });
});