import assert from "node:assert/strict"; import { randomUUID } from "node:crypto"; import { spawnSync } from "node:child_process"; import { fileURLToPath } from "node:url"; import test from "node:test"; import { closeLocalPostgresDataPool, createLocalPostgresDataClient } from "../src/lib/db/local-postgres-client-core.ts"; import { startPostgresFixture, type PostgresFixture } from "./helpers/postgres-fixture.ts"; // BUG-1399: `.gte()` was missing from the self-hosted client, so the synastry // report's "reports created today" count threw before anything was reserved. // This runs the same filters against a real database. Fictional rows only. const runner = fileURLToPath(new URL("../scripts/db-migrate.mjs", import.meta.url)); const docker = spawnSync("docker", ["version", "--format", "{{.Server.Version}}"], { stdio: "ignore" }).status === 0; const SERVICE_URL_ROLE = ["service_runtime", "service-runtime-test-password"] as const; function applyMigrations(fixture: PostgresFixture): void { const result = spawnSync(process.execPath, [runner], { encoding: "utf8", env: { ...process.env, SCHEMA_DATABASE_URL: fixture.connectionUrl("schema_owner", "schema-owner-test-password"), }, }); assert.equal(result.status, 0, result.stderr || result.stdout); } test("gte and lt filter real rows, and the synastry daily count query runs", { skip: docker ? false : "docker unavailable", }, async () => { const fixture = startPostgresFixture(); const serviceUrl = fixture.connectionUrl(...SERVICE_URL_ROLE); try { applyMigrations(fixture); const owner = randomUUID(); // A probe table keeps this a query-builder test: runtime roles have no // direct DML on business tables, so grant select on the probe only. fixture.psql(`create table public.range_filter_probe (id integer primary key, seen_at timestamptz not null); insert into public.range_filter_probe values (1, '2026-10-01T00:00:00Z'), (2, '2026-10-21T08:00:00Z'); grant select on table public.range_filter_probe to service_role;`); const admin = createLocalPostgresDataClient(serviceUrl, null, "service_role"); const since = await admin.from("range_filter_probe").select("id") .gte("seen_at", "2026-10-21T00:00:00.000Z"); assert.equal(since.error, null, since.error?.message ?? "gte failed"); assert.deepEqual((since.data as Array<{ id: number }>).map((row) => row.id), [2]); const before = await admin.from("range_filter_probe").select("id") .lt("seen_at", "2026-10-21T00:00:00.000Z"); assert.equal(before.error, null, before.error?.message ?? "lt failed"); assert.deepEqual((before.data as Array<{ id: number }>).map((row) => row.id), [1]); const boundary = await admin.from("range_filter_probe").select("id") .gte("seen_at", "2026-10-21T08:00:00.000Z") .lt("seen_at", "2026-10-21T08:00:00.001Z"); assert.equal(boundary.error, null, boundary.error?.message ?? "range failed"); assert.deepEqual((boundary.data as Array<{ id: number }>).map((row) => row.id), [2]); // The exact chain createSynastryFullReport uses for today's count. const today = await admin.from("personal_reports").select("created_at") .eq("user_id", owner) .gte("created_at", "2026-10-21T00:00:00.000Z"); assert.equal(today.error, null, today.error?.message ?? "personal_reports gte failed"); assert.deepEqual(today.data, []); } finally { await closeLocalPostgresDataPool(serviceUrl); fixture.stop(); } });