diff --git a/backend/alembic.ini b/backend/alembic.ini index adce95b3..018ab441 100644 --- a/backend/alembic.ini +++ b/backend/alembic.ini @@ -1,6 +1,8 @@ [alembic] script_location = alembic prepend_sys_path = . +# Split the path list the way the OS does (alembic's legacy space/comma/colon split is deprecated). +path_separator = os [loggers] keys = root,sqlalchemy,alembic diff --git a/backend/requirements-dev.txt b/backend/requirements-dev.txt index bc9c558d..6f25a4da 100644 --- a/backend/requirements-dev.txt +++ b/backend/requirements-dev.txt @@ -1,7 +1,9 @@ # Test + lint tooling — NEVER in the prod image. Installed only by backend/Dockerfile.test (the # throwaway test image), which the prod build (root Dockerfile) never references. Pinned so the # suite is reproducible: pytest for the pure-logic tests, and ruff at the SAME version the gate's -# backend lint lane currently runs on the host (see the run_lint note in siftlode.sh). httpx is -# already an app dependency (requirements.txt), so its TestClient needs nothing extra here. +# backend lint lane runs on the host (the gate fails when the host's ruff is not this one). httpx2: +# Starlette 1.x's TestClient is built on it (`import httpx2 as httpx`) and only falls back to the +# app's own httpx with a deprecation warning on every run. pytest==8.4.2 ruff==0.15.21 +httpx2==2.13.0 diff --git a/frontend/src/components/Home.dom.test.tsx b/frontend/src/components/Home.dom.test.tsx index bd2bce1f..50017494 100644 --- a/frontend/src/components/Home.dom.test.tsx +++ b/frontend/src/components/Home.dom.test.tsx @@ -49,6 +49,7 @@ vi.mock("../lib/api", () => ({ setState: () => Promise.resolve(), watchLaterAdd: () => Promise.resolve(), watchLaterRemove: () => Promise.resolve(), + downloadIndex: () => Promise.resolve({}), // the cards' download buttons read it }, DISCOVER_BUDGETS: ["short", "medium", "long", "any"], discoverCardKey: (c: { source: string; id: string }) => `${c.source}:${c.id}`, diff --git a/frontend/src/components/Home.pin.dom.test.tsx b/frontend/src/components/Home.pin.dom.test.tsx index e3fc616a..9b122160 100644 --- a/frontend/src/components/Home.pin.dom.test.tsx +++ b/frontend/src/components/Home.pin.dom.test.tsx @@ -53,6 +53,7 @@ vi.mock("../lib/api", () => ({ setState: () => Promise.resolve(), watchLaterAdd: () => Promise.resolve(), watchLaterRemove: () => Promise.resolve(), + downloadIndex: () => Promise.resolve({}), // the cards' download buttons read it comfortPins: (...a: unknown[]) => comfortPins(...a), addComfortPin: (...a: unknown[]) => addComfortPin(...a), removeComfortPin: (...a: unknown[]) => removeComfortPin(...a), diff --git a/frontend/src/components/ui/Modal.fullscreen.dom.test.tsx b/frontend/src/components/ui/Modal.fullscreen.dom.test.tsx index 816fd154..ac1bb3fe 100644 --- a/frontend/src/components/ui/Modal.fullscreen.dom.test.tsx +++ b/frontend/src/components/ui/Modal.fullscreen.dom.test.tsx @@ -31,7 +31,7 @@ describe("a dialog open while fullscreen is left", () => { it("keeps what the viewer had typed in it, and the focus inside it", async () => { const stage = document.createElement("div"); document.body.appendChild(stage); - await setFullscreen(stage); + setFullscreen(stage); render( {}}> @@ -42,7 +42,7 @@ describe("a dialog open while fullscreen is left", () => { field.focus(); expect(stage.contains(field)).toBe(true); - await setFullscreen(null); // the browser's own Esc leaves fullscreen; the dialog stays open + setFullscreen(null); // the browser's own Esc leaves fullscreen; the dialog stays open const after = screen.getByLabelText("name") as HTMLInputElement; expect(after.value).toBe("my cut"); @@ -78,7 +78,7 @@ describe("a dialog with an autoFocus child", () => { const field = screen.getByLabelText("query"); expect(document.activeElement).toBe(field); - await setFullscreen(stage); // the player goes fullscreen under the open dialog + setFullscreen(stage); // the player goes fullscreen under the open dialog expect(stage.contains(field)).toBe(true); expect(document.activeElement).toBe(field); diff --git a/frontend/src/components/ui/QueryState.dom.test.tsx b/frontend/src/components/ui/QueryState.dom.test.tsx index 089e147b..0b18581c 100644 --- a/frontend/src/components/ui/QueryState.dom.test.tsx +++ b/frontend/src/components/ui/QueryState.dom.test.tsx @@ -8,6 +8,9 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { render, cleanup } from "@testing-library/react"; import { LoadingState } from "./QueryState"; +// The label is not under test; without an i18n instance react-i18next warns on every render. +vi.mock("react-i18next", () => ({ useTranslation: () => ({ t: (key: string) => key }) })); + let now = 0; beforeEach(() => { vi.spyOn(performance, "now").mockImplementation(() => now);