fix(quality): close backup and localization guard gaps

This commit is contained in:
Marc-André Bombeck committed 2026-07-28 12:03:02 +02:00
1 parent 477485ba47
commit a2f2a30ac2
11 files changed
+46 -16

No files matched your search

+2 -1
View File
@@ -2865,7 +2865,8 @@
"flights": "Etagen",
"falls": "Stürze",
"years": "Jahre",
"breathingEvents": "Ereignisse"
"breathingEvents": "Ereignisse",
"respiratoryRate": "Atemzüge/min"
},
"sleep": {
"title": "Schlaf",
+2 -1
View File
@@ -2865,7 +2865,8 @@
"flights": "flights",
"falls": "falls",
"years": "years",
"breathingEvents": "events"
"breathingEvents": "events",
"respiratoryRate": "breaths/min"
},
"sleep": {
"title": "Sleep",
+2 -1
View File
@@ -2865,7 +2865,8 @@
"flights": "pisos",
"falls": "caídas",
"years": "años",
"breathingEvents": "eventos"
"breathingEvents": "eventos",
"respiratoryRate": "respiraciones/min"
},
"sleep": {
"title": "Sueño",
+2 -1
View File
@@ -2865,7 +2865,8 @@
"flights": "étages",
"falls": "chutes",
"years": "ans",
"breathingEvents": "événements"
"breathingEvents": "événements",
"respiratoryRate": "respirations/min"
},
"sleep": {
"title": "Sommeil",
+2 -1
View File
@@ -2865,7 +2865,8 @@
"flights": "piani",
"falls": "cadute",
"years": "anni",
"breathingEvents": "eventi"
"breathingEvents": "eventi",
"respiratoryRate": "respiri/min"
},
"sleep": {
"title": "Sonno",
+2 -1
View File
@@ -2865,7 +2865,8 @@
"flights": "piętra",
"falls": "upadki",
"years": "lata",
"breathingEvents": "zdarzenia"
"breathingEvents": "zdarzenia",
"respiratoryRate": "oddechów/min"
},
"sleep": {
"title": "Sen",
@@ -187,12 +187,15 @@ function relationFieldNames(model: string): string[] {
const READ_OPS = /(findMany|findUnique|findFirst|findUniqueOrThrow|groupBy)/;
const WRITE_OPS = /(create|createMany|upsert|update|updateMany)/;
const READ_RELATION_OPS = /[{[t]/;
const WRITE_RELATION_OPS = /\{\s*create\b/;
/** Does any of `files` touch `model` with an operation matching `ops`? */
/** Does any of `files` touch `model` through matching delegate and relation operations? */
function touches(
files: readonly string[],
model: string,
ops: RegExp,
delegateOps: RegExp,
relationOps: RegExp,
): boolean {
const delegate = delegateName(model);
const relations = relationFieldNames(model);
@@ -202,16 +205,28 @@ function touches(
for (const match of source.matchAll(
new RegExp(`\\.${delegate}\\s*\\.\\s*(\\w+)`, "g"),
)) {
if (ops.test(match[1])) return true;
if (delegateOps.test(match[1])) return true;
}
// A nested relation key: `schedules: {`, `dayLogs: [`, `entries: true`.
return relations.some((relation) =>
new RegExp(`\\b${relation}\\s*:\\s*[{[t]`).test(source),
new RegExp(`\\b${relation}\\s*:\\s*(?:${relationOps.source})`).test(
source,
),
);
});
}
describe("every backed-up model travels both ways, or is named as debt", () => {
it("does not count a nested relation read as a restore write", () => {
expect(
touches(
["src/app/api/admin/backups/[id]/restore/route.ts"],
"User",
WRITE_OPS,
WRITE_RELATION_OPS,
),
).toBe(false);
});
it("declares writer and restore files that exist", () => {
for (const file of [...BACKUP_WRITER_FILES, ...BACKUP_RESTORE_FILES]) {
expect(() => read(file), `${file} is declared but missing`).not.toThrow();
@@ -250,7 +265,8 @@ describe("every backed-up model travels both ways, or is named as debt", () => {
it("finds a payload reader for every model claimed two-ended", () => {
const missing = TWO_ENDED_MODELS.filter(
(model) => !touches(BACKUP_WRITER_FILES, model, READ_OPS),
(model) =>
!touches(BACKUP_WRITER_FILES, model, READ_OPS, READ_RELATION_OPS),
);
expect(
missing,
@@ -261,7 +277,8 @@ describe("every backed-up model travels both ways, or is named as debt", () => {
it("finds a restore branch for every model claimed two-ended", () => {
const missing = TWO_ENDED_MODELS.filter(
(model) => !touches(BACKUP_RESTORE_FILES, model, WRITE_OPS),
(model) =>
!touches(BACKUP_RESTORE_FILES, model, WRITE_OPS, WRITE_RELATION_OPS),
);
expect(
missing,
@@ -34,7 +34,6 @@ const LANGUAGE_NEUTRAL_UNITS = new Set([
"%",
"/10",
"bpm",
"breaths/min",
"dBA",
"kcal",
"kg/m²",
+1 -2
View File
@@ -22,8 +22,7 @@ export default function InsightsAtemfrequenzPage() {
i18nPrefix="insights.respiratoryRate"
explainerMetric="respiratoryRate"
color="var(--info)"
unit="breaths/min"
yAxisUnit="breaths/min"
unitKey="insights.units.respiratoryRate"
emptyStateIcon={<Wind className="size-6" />}
emptyStateCtaType={null}
coachPrefill="I haven't logged any respiratory rate yet — what does this metric tell me about my health, and how do I improve it?"
@@ -7,6 +7,8 @@
* which is the failure mode the whole backup guard exists to make loud.
*/
import { describe, expect, it, vi } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
// The slot-count module reaches for the global Prisma client at load; this
// test only wants the constant off it.
@@ -73,6 +75,13 @@ function tx(created: Array<Record<string, unknown>>) {
}
describe("intraday profile backup", () => {
it("keeps the backup source reviewable as text", () => {
const source = readFileSync(
resolve(process.cwd(), "src/lib/export/intraday-profile-backup.ts"),
);
expect(source.includes(0)).toBe(false);
});
it("carries the same slot count the reader indexes by", () => {
// Re-declared rather than imported, so it is pinned here instead: a change
// to one without the other would put rows in the table that every read
Binary file not shown.