mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-15 06:52:04 +00:00
Merge branch 'release/v1.43.0' into v143/fixui
This commit is contained in:
commit
beb7ef4ccc
93 files changed
+4369
-3956
No files matched your search
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
* Every ISO instant under `src/` goes through `isoDateTime()`.
|
||||
*
|
||||
* Since v1.43 a request instant must carry seconds, and the refusal is counted
|
||||
* (`validation.datetime.no_seconds`) so a client on a minute-precision encoder
|
||||
* shows up in the logs. Both live in `src/lib/validations/datetime.ts`. A
|
||||
* schema that calls `z.iso.datetime(` or `z.string().datetime(` directly gets
|
||||
* zod's default message and is invisible to the counter, so a direct call
|
||||
* anywhere else is refused here.
|
||||
*
|
||||
* The matcher tolerates whitespace and line breaks between `z`, `.iso` /
|
||||
* `.string()` and `.datetime(`, because prettier splits long chains, and it
|
||||
* also refuses a bare `.datetime(` chained off anything, so an alias such as
|
||||
* `const s = z.string(); s.datetime()` cannot route around it. Comments are
|
||||
* stripped first so prose about the old form does not trip it.
|
||||
*/
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { walkSourceFiles } from "./helpers/source-files";
|
||||
|
||||
const SRC = join(process.cwd(), "src");
|
||||
const HELPER = "lib/validations/datetime.ts";
|
||||
|
||||
function sourceFiles(): string[] {
|
||||
return walkSourceFiles(SRC, { floor: 3000 })
|
||||
.filter((p) => !p.startsWith("generated/"))
|
||||
.filter((p) => !p.includes("__tests__"))
|
||||
.filter((p) => !p.endsWith(".test.ts") && !p.endsWith(".test.tsx"));
|
||||
}
|
||||
|
||||
function stripComments(text: string): string {
|
||||
return text
|
||||
.replace(/\/\*[\s\S]*?\*\//g, "")
|
||||
.replace(/(^|[^:"'`])\/\/.*$/gm, "$1");
|
||||
}
|
||||
|
||||
const DIRECT_DATETIME = /\.\s*datetime\s*\(/g;
|
||||
const HELPER_CALL = /\bisoDateTime\s*\(/g;
|
||||
|
||||
function directDatetimeCalls(text: string): number {
|
||||
return (stripComments(text).match(DIRECT_DATETIME) ?? []).length;
|
||||
}
|
||||
|
||||
describe("datetime helper guard", () => {
|
||||
const files = sourceFiles();
|
||||
|
||||
it("finds no direct zod datetime call outside the helper", () => {
|
||||
const offenders = files
|
||||
.filter((rel) => rel !== HELPER)
|
||||
.flatMap((rel) => {
|
||||
const n = directDatetimeCalls(readFileSync(join(SRC, rel), "utf8"));
|
||||
return n > 0 ? [`${rel} (${n})`] : [];
|
||||
});
|
||||
expect(
|
||||
offenders,
|
||||
"use isoDateTime() from @/lib/validations/datetime instead",
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("the helper itself is the one place that calls zod", () => {
|
||||
expect(directDatetimeCalls(readFileSync(join(SRC, HELPER), "utf8"))).toBe(
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it("actually sees the call sites it guards", () => {
|
||||
// An empty sweep agrees with an empty offender list. The helper has
|
||||
// hundreds of callers; a count far below that means the walk or the
|
||||
// matcher went blind, not that the tree is clean.
|
||||
const calls = files
|
||||
.filter((rel) => rel !== HELPER)
|
||||
.reduce(
|
||||
(sum, rel) =>
|
||||
sum +
|
||||
(readFileSync(join(SRC, rel), "utf8").match(HELPER_CALL) ?? [])
|
||||
.length,
|
||||
0,
|
||||
);
|
||||
expect(calls).toBeGreaterThan(350);
|
||||
});
|
||||
|
||||
it("matches the forms prettier produces", () => {
|
||||
expect(directDatetimeCalls("z.iso.datetime({ offset: true })")).toBe(1);
|
||||
expect(directDatetimeCalls("z.iso\n .datetime({ offset: true })")).toBe(1);
|
||||
expect(directDatetimeCalls("z\n .string()\n .datetime()")).toBe(1);
|
||||
expect(directDatetimeCalls("s . datetime ()")).toBe(1);
|
||||
expect(directDatetimeCalls("// z.iso.datetime() in prose")).toBe(0);
|
||||
expect(directDatetimeCalls("/* z.string().datetime() */")).toBe(0);
|
||||
expect(directDatetimeCalls("isoDateTime()")).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -160,6 +160,10 @@ function zodObjectShape(path: string, schemaName: string): ZodObjectShape {
|
||||
let current = expression;
|
||||
while (ts.isCallExpression(current)) {
|
||||
const callee = current.expression;
|
||||
// The shared instant helper (`@/lib/validations/datetime`) is a leaf.
|
||||
if (ts.isIdentifier(callee) && callee.text === "isoDateTime") {
|
||||
return [prefix];
|
||||
}
|
||||
expect(
|
||||
ts.isPropertyAccessExpression(callee),
|
||||
`${prefix || schemaName} contains an unsupported Zod call: ${current.getText(file)}`,
|
||||
|
||||
@@ -535,6 +535,10 @@ function zodObjectShape(path: string, schemaName: string): ZodObjectShape {
|
||||
let current = expression;
|
||||
while (ts.isCallExpression(current)) {
|
||||
const callee = current.expression;
|
||||
// The shared instant helper (`@/lib/validations/datetime`) is a leaf.
|
||||
if (ts.isIdentifier(callee) && callee.text === "isoDateTime") {
|
||||
return [prefix];
|
||||
}
|
||||
expect(
|
||||
ts.isPropertyAccessExpression(callee),
|
||||
`${prefix || schemaName} contains an unsupported Zod call: ${current.getText(file)}`,
|
||||
|
||||
Reference in new issue
Block a user