refactor(db): typed query helpers, shared test FormData helper
CI / check (push) Successful in 4m10s
CI / preflight (push) Skipped
CI / deploy (push) Successful in 2m4s

Replace raw db.execute tuple casts with queryRows/rowsFrom/execResult/
affectedRows helpers from lib/db, drop redundant mysql2 casts on typed
query builders, and centralize per-test fakeForm into test/fake-form.
Update db mocks in tests so helpers resolve against mocked execute.
This commit is contained in:
openhands committed 2026-09-17 21:02:57 +02:00
1 parent 2e25b39364
commit 8638e81444
116 files changed
+866 -897

No files matched your search

@@ -7,7 +7,10 @@ const state = vi.hoisted(() => ({
allowed: new Set<string>(),
execute: vi.fn(),
}));
vi.mock("@/lib/db", () => ({ db: { execute: state.execute } }));
vi.mock("@/lib/db", async () => ({
...(await import("@/test/db-helpers")).createDbHelpers(state.execute),
db: { execute: state.execute },
}));
vi.mock("@/lib/permissions", async () => ({
...(await import("@/lib/permission-slugs")),
canAccess: (_: unknown, permission: string) => state.allowed.has(permission),
@@ -1,5 +1,5 @@
import { type SQL, sql } from "drizzle-orm";
import { db } from "@/lib/db";
import { queryRows } from "@/lib/db";
import { canAccess, PERMS, type PermissionSet } from "@/lib/permissions";
export type OverviewRow = {
@@ -27,8 +27,7 @@ export async function loadUserOverview(
rank: number,
): Promise<OverviewSection[]> {
const read = async (query: SQL): Promise<OverviewRow[]> => {
const [rows] = await db.execute(query);
return rows as unknown as OverviewRow[];
return queryRows<OverviewRow>(query);
};
const sources: {
key: string;
+43 -41
View File
@@ -11,54 +11,56 @@ vi.mock("next/navigation", () => ({
throw new Error("not found");
},
}));
vi.mock("@/lib/db", () => {
vi.mock("@/lib/db", async () => {
const tables = Object.fromEntries(
["User", "Rooms", "RoomTradeLog", "UsersBadges", "UsersCurrency"].map(
(name) => [name, { name }],
),
);
const db = {
select() {
let name = "";
const rows = () =>
name === "User"
? [{ id: 42, username: "User", rank: 2, online: "0" }]
: [];
const query = {
from(table: { name: string }) {
name = table.name;
state.tables.push(name);
return query;
},
where() {
return query;
},
orderBy() {
return query;
},
limit() {
return query;
},
// biome-ignore lint/suspicious/noThenProperty: Drizzle queries are intentionally awaitable.
then(resolve: (value: unknown[]) => unknown) {
return Promise.resolve(rows()).then(resolve);
},
catch() {
return Promise.resolve(rows());
},
};
return query;
},
async execute(query: Parameters<MySqlDialect["sqlToQuery"]>[0]) {
const compiled = new MySqlDialect().sqlToQuery(query).sql;
state.sql.push(compiled);
if (state.failLogs && compiled.includes("chatlogs_room"))
throw new Error("unavailable");
return [[], []];
},
};
return {
...tables,
db: {
select() {
let name = "";
const rows = () =>
name === "User"
? [{ id: 42, username: "User", rank: 2, online: "0" }]
: [];
const query = {
from(table: { name: string }) {
name = table.name;
state.tables.push(name);
return query;
},
where() {
return query;
},
orderBy() {
return query;
},
limit() {
return query;
},
// biome-ignore lint/suspicious/noThenProperty: Drizzle queries are intentionally awaitable.
then(resolve: (value: unknown[]) => unknown) {
return Promise.resolve(rows()).then(resolve);
},
catch() {
return Promise.resolve(rows());
},
};
return query;
},
async execute(query: Parameters<MySqlDialect["sqlToQuery"]>[0]) {
const compiled = new MySqlDialect().sqlToQuery(query).sql;
state.sql.push(compiled);
if (state.failLogs && compiled.includes("chatlogs_room"))
throw new Error("unavailable");
return [[], []];
},
},
db,
...(await import("@/test/db-helpers")).createDbHelpers(db.execute),
};
});
+2 -2
View File
@@ -2,6 +2,7 @@ import { asc, count, desc, eq, or, sql } from "drizzle-orm";
import { notFound } from "next/navigation";
import {
db,
queryRows,
Rooms,
RoomTradeLog,
User,
@@ -10,8 +11,7 @@ import {
} from "@/lib/db";
async function rawRows<T>(query: ReturnType<typeof sql>): Promise<T[]> {
const [rows] = (await db.execute(query)) as unknown as [T[], unknown];
return rows ?? [];
return queryRows<T>(query);
}
export async function loadUserById(