docs(comments): correct comments the cuts left stale
The registration language is read today (preferences.language, the mail language); the Modal and the players order against popovers through layerStack, not the FloatingTree; the upload's auth dependency is current_user; the module of access tests covers onboarding and access, not only the canonical email. The dev proxy's WebSocket upgrade served the removed messages socket and goes; the test compose header says a pure-logic run needs SIFTLODE_TEST_REQUIRE_DB=0; the speed measure no longer reports an unreachable database as "no share link".
This commit is contained in:
@@ -298,8 +298,9 @@ class EmailPasswordIn(BaseModel):
|
|||||||
# The language the sign-up page was in. Captured at registration because it is the ONLY moment
|
# The language the sign-up page was in. Captured at registration because it is the ONLY moment
|
||||||
# the choice exists anywhere the server can see: it lives in the browser until an account does,
|
# the choice exists anywhere the server can see: it lives in the browser until an account does,
|
||||||
# and a preference nobody recorded cannot be backfilled later (as `google_email` showed: those
|
# and a preference nobody recorded cannot be backfilled later (as `google_email` showed: those
|
||||||
# rows still read NULL until each user signs in again). Nothing reads it yet; the bilingual
|
# rows still read NULL until each user signs in again). It is stored as the account's
|
||||||
# mail templates will.
|
# `preferences["language"]`, which the UI starts in and every mail to the account is written in
|
||||||
|
# (`mailtext.lang_for_user` / `lang_for_email`).
|
||||||
lang: str | None = None
|
lang: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
# DB-backed pytest lane: an ephemeral Postgres + the test image, wired together so the
|
# DB-backed pytest lane: an ephemeral Postgres + the test image, wired together so the
|
||||||
# `db` fixture (tests/conftest.py) has a real database. The whole thing is throwaway — the pg data
|
# `db` fixture (tests/conftest.py) has a real database. The whole thing is throwaway — the pg data
|
||||||
# lives in tmpfs (RAM) and nothing is persisted. The pure-logic lane still runs WITHOUT this file
|
# lives in tmpfs (RAM) and nothing is persisted. A pure-logic run without this file is possible but
|
||||||
# (plain `docker run … python -m pytest` — DB tests skip when no Postgres is reachable).
|
# must say so: `docker run -e SIFTLODE_TEST_REQUIRE_DB=0 … python -m pytest` (without it, a missing
|
||||||
|
# Postgres fails the run instead of skipping the DB tests).
|
||||||
#
|
#
|
||||||
# docker compose -f backend/docker-compose.test.yml up --build --abort-on-container-exit \
|
# docker compose -f backend/docker-compose.test.yml up --build --abort-on-container-exit \
|
||||||
# --exit-code-from test
|
# --exit-code-from test
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
"""Review round — the invariants the canonical-email work's own review found unpinned.
|
"""The onboarding and access invariants a review of that work found unpinned.
|
||||||
|
|
||||||
Every test here corresponds to a defect a reviewer could still introduce with the whole suite green:
|
Every test here corresponds to a defect a reviewer could still introduce with the whole suite green:
|
||||||
a mail whose body never renders in any test, an activation that only works for the exact spelling, a
|
a mail whose body never renders in some language, a link lifetime described in the wrong unit, a
|
||||||
"we already wrote to them" stamp for a mail that was dropped, an env allow-list that answers a
|
registration that rewrites an existing password or kills a link already in flight, an activation
|
||||||
different question than the gate it backs, and the address pre-emption that the order-independent
|
that only works for the exact spelling, a "we already wrote to them" stamp for a mail that was
|
||||||
gates' argument covered only while the address stayed unverified.
|
dropped, an env allow-list that answers a different question than the gate it backs, a reset or
|
||||||
|
resend that reports success it did not have, and a throttle that leaks whether an account exists.
|
||||||
|
Approval's password revoke was called redundant because `password_login` refuses an unverified
|
||||||
|
account; that holds only until the address is verified, which is what the tests on pre-emption pin.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
|
|||||||
@@ -38,19 +38,17 @@ function shareTokens(): string[] {
|
|||||||
" join media_assets a on a.id = j.asset_id" +
|
" join media_assets a on a.id = j.asset_id" +
|
||||||
" where l.password_hash is null and (l.expires_at is null or l.expires_at > now())" +
|
" where l.password_hash is null and (l.expires_at is null or l.expires_at > now())" +
|
||||||
" and a.status = 'ready' order by l.id";
|
" and a.status = 'ready' order by l.id";
|
||||||
try {
|
// Not caught: a database this cannot reach (no docker, a renamed container) is a failed
|
||||||
const out = execFileSync(
|
// measurement, not "no playable share link on the local stack".
|
||||||
"docker",
|
const out = execFileSync(
|
||||||
["exec", CONTAINER, "psql", "-U", "siftlode", "-d", "siftlode", "-tAc", sql],
|
"docker",
|
||||||
{ encoding: "utf8" },
|
["exec", CONTAINER, "psql", "-U", "siftlode", "-d", "siftlode", "-tAc", sql],
|
||||||
);
|
{ encoding: "utf8" },
|
||||||
return out
|
);
|
||||||
.split("\n")
|
return out
|
||||||
.map((t) => t.trim())
|
.split("\n")
|
||||||
.filter(Boolean);
|
.map((t) => t.trim())
|
||||||
} catch {
|
.filter(Boolean);
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test("speed baseline", async ({ browser, page: page0 }) => {
|
test("speed baseline", async ({ browser, page: page0 }) => {
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ import type { useAnchoredPopover } from "./anchoredPopoverHooks";
|
|||||||
// - autoUpdate → follows the trigger on scroll/resize (the old rAF loop), coalesced by the library
|
// - autoUpdate → follows the trigger on scroll/resize (the old rAF loop), coalesced by the library
|
||||||
// Positioning is portalled through FloatingPortal (escapes ancestor stacking contexts — same job the
|
// Positioning is portalled through FloatingPortal (escapes ancestor stacking contexts — same job the
|
||||||
// house <Overlay> did for TagsCell). Every popover is a node in the app's <FloatingTree> so Escape /
|
// house <Overlay> did for TagsCell). Every popover is a node in the app's <FloatingTree> so Escape /
|
||||||
// outside-press dispatch is topmost-only across nested layers (the Modal and players included).
|
// outside-press dispatch is topmost-only across nested popovers; against the Modal and the players,
|
||||||
|
// which are not nodes, it is lib/layerStack that decides who is topmost.
|
||||||
//
|
//
|
||||||
// This layer adds the keyboard/ARIA model the roles promise, mirroring `ViewSwitcher` (the house's
|
// This layer adds the keyboard/ARIA model the roles promise, mirroring `ViewSwitcher` (the house's
|
||||||
// textbook menu) but via Floating UI's idioms rather than a second hand-rolled roving engine:
|
// textbook menu) but via Floating UI's idioms rather than a second hand-rolled roving engine:
|
||||||
|
|||||||
@@ -434,7 +434,7 @@ describe("the debug session a tab records into", () => {
|
|||||||
|
|
||||||
// P1 — the upload's wire shape is a security boundary, not a detail. A multipart part is a DECLARED
|
// P1 — the upload's wire shape is a security boundary, not a detail. A multipart part is a DECLARED
|
||||||
// body, which the server reads and spools BEFORE its auth dependency runs; the raw body is what lets
|
// body, which the server reads and spools BEFORE its auth dependency runs; the raw body is what lets
|
||||||
// `require_human` resolve first. A revert to FormData here would silently re-open that, and the
|
// `current_user` resolve first. A revert to FormData here would silently re-open that, and the
|
||||||
// backend's own test only sees the shape the client actually sends.
|
// backend's own test only sees the shape the client actually sends.
|
||||||
describe("uploadDownload's wire shape", () => {
|
describe("uploadDownload's wire shape", () => {
|
||||||
it("sends the File as the raw body, with the names as percent-encoded headers", () => {
|
it("sends the File as the raw body, with the names as percent-encoded headers", () => {
|
||||||
|
|||||||
@@ -198,7 +198,7 @@ export const downloadsApi = {
|
|||||||
// show upload progress; any file type is accepted (media gets the full function set, a generic
|
// show upload progress; any file type is accepted (media gets the full function set, a generic
|
||||||
// file is download-only). Returns the already-`done` job the ingest creates.
|
// file is download-only). Returns the already-`done` job the ingest creates.
|
||||||
// The body IS the file, and the two names ride as headers. Not a style choice: a multipart part is
|
// The body IS the file, and the two names ride as headers. Not a style choice: a multipart part is
|
||||||
// a DECLARED body, which FastAPI reads and spools BEFORE the route's `require_human` dependency
|
// a DECLARED body, which FastAPI reads and spools BEFORE the route's `current_user` dependency
|
||||||
// runs — so with the old shape an unauthenticated caller could make the server write the whole
|
// runs — so with the old shape an unauthenticated caller could make the server write the whole
|
||||||
// per-file allowance to temp (P1). Headers are latin-1 on the wire, hence `encodeURIComponent`;
|
// per-file allowance to temp (P1). Headers are latin-1 on the wire, hence `encodeURIComponent`;
|
||||||
// the server percent-decodes both. Content-Type is left to the browser (the File's own type).
|
// the server percent-decodes both. Content-Type is left to the browser (the File's own type).
|
||||||
|
|||||||
@@ -77,7 +77,8 @@ const root =
|
|||||||
) : (
|
) : (
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
{/* One tree for the whole app: every anchored popover registers as a node so Escape and
|
{/* One tree for the whole app: every anchored popover registers as a node so Escape and
|
||||||
outside-press act only on the topmost layer (the Modal and the players join it too). */}
|
outside-press act only on the topmost popover. The Modal and the players are not nodes:
|
||||||
|
they order against the popovers through lib/layerStack. */}
|
||||||
<FloatingTree>
|
<FloatingTree>
|
||||||
<ConfirmProvider>
|
<ConfirmProvider>
|
||||||
{/* Nothing below renders until the tab knows which account it is: every provider
|
{/* Nothing below renders until the tab knows which account it is: every provider
|
||||||
|
|||||||
@@ -12,10 +12,7 @@ const target = "http://127.0.0.1:8080";
|
|||||||
// server returns to :5173 instead of the fixed :8080 callback. (Both are registered in the console.)
|
// server returns to :5173 instead of the fixed :8080 callback. (Both are registered in the console.)
|
||||||
const proxyOpts = { target, changeOrigin: false };
|
const proxyOpts = { target, changeOrigin: false };
|
||||||
const proxy = {
|
const proxy = {
|
||||||
// ws: the in-app messages socket (/api/messages/ws) is a WebSocket. Without it Vite hands that upgrade
|
"/api": proxyOpts,
|
||||||
// to nobody, so the socket never connects on :5173 — and the first reset on it (a closed tab, a dropped
|
|
||||||
// connection) throws an unhandled ECONNRESET that takes the whole dev server down.
|
|
||||||
"/api": { ...proxyOpts, ws: true },
|
|
||||||
"/auth": proxyOpts,
|
"/auth": proxyOpts,
|
||||||
"/healthz": proxyOpts,
|
"/healthz": proxyOpts,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user