Files
openclaw/test/helpers/sqlite-statement-execution-counter.ts
Vito Cappello accc6ccaa2 improve(sqlite): reuse synchronous prepared statements (#114777)
* perf(sqlite): cache synchronous prepared statements

* fix(sqlite): refresh cached statement metadata

* fix(sqlite): bound cached statement retention

* fix(sqlite): close statement cache lifecycle gaps

* test: suppress SQLite warning in retention child

* test(sqlite): make transcript-load counter statement-cache-aware and allowlist raw test SQL

* test(sessions): type the wrapped iterate against its overloaded signature

* test: share a statement-cache-aware SQLite execution counter across prepare-count tests

* fix(pr): validate head SHA with bash regex so fork-CI runners without ripgrep pass the artifacts-init guard

---------

Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-28 03:53:16 -04:00

37 lines
1.6 KiB
TypeScript

import type { DatabaseSync, StatementSync } from "node:sqlite";
import { vi } from "vitest";
import { clearNodeSqliteKyselyCacheForDatabase } from "../../src/infra/kysely-sync.js";
/**
* Count SQLite query executions per caller-defined bucket. Prepared-statement
* caching (src/infra/kysely-sync.ts) reuses statements across calls, so
* counting `prepare` invocations undercounts; this wraps `iterate` on matching
* statements and clears the statement cache at attach so statements cached
* before the spy cannot bypass it.
*/
export function trackSqliteStatementExecutions<Key extends string>(
db: DatabaseSync,
keys: readonly Key[],
classify: (sql: string) => Key | null,
): { counts: Record<Key, number>; restore: () => void } {
clearNodeSqliteKyselyCacheForDatabase(db);
const counts = Object.fromEntries(keys.map((key) => [key, 0])) as Record<Key, number>;
const originalPrepare = db.prepare.bind(db);
const prepareSpy = vi.spyOn(db, "prepare").mockImplementation((sqlText: string) => {
const statement = originalPrepare(sqlText);
const key = classify(sqlText);
if (key !== null) {
const originalIterate = statement.iterate.bind(statement) as (
...args: unknown[]
) => ReturnType<StatementSync["iterate"]>;
// iterate is overloaded, so the wrapper forwards untyped and casts back.
statement.iterate = ((...args: unknown[]) => {
counts[key] += 1;
return originalIterate(...args);
}) as StatementSync["iterate"];
}
return statement;
});
return { counts, restore: () => prepareSpy.mockRestore() };
}