fix(labs): let a deleted number not hold a text marker's unit, and read the winner's unit after a race

Refs #1313
This commit is contained in:
Marc-André Bombeck committed 2026-11-03 23:30:21 +01:00
1 parent 55a3a9b53d
commit 0e7134a2ea
2 files changed
+43 -4

No files matched your search

+16 -4
View File
@@ -123,8 +123,11 @@ export async function readingInMarkerUnit(
* every reading the marker holds is text there is no number the unit could
* contradict, so the first numeric reading gives the marker its unit, the
* dimensionless "1" included. The update only matches a marker whose unit is
* still empty, so two readings racing for it cannot both win. `marker.unit`
* is set on the caller's object, which writes the reading under it.
* still empty, so two readings racing for it cannot both win; the loser reads
* the winner's unit and is compared against it. `marker.unit` is set on the
* caller's object, which writes the reading under it. The adoption is not in
* the reading's transaction: if the write fails after it, the marker keeps
* the unit the reading stated, which is the unit its next reading will state.
*/
async function adoptFirstNumericUnit(
client: Pick<PrismaClient, "biomarker" | "labResult">,
@@ -134,14 +137,23 @@ async function adoptFirstNumericUnit(
const stated = unit.trim();
if (marker.unit.trim() !== "" || stated === "") return false;
const numeric = await client.labResult.count({
where: { biomarkerId: marker.id, value: { not: null } },
where: { biomarkerId: marker.id, value: { not: null }, deletedAt: null },
});
if (numeric > 0) return false;
const { count } = await client.biomarker.updateMany({
where: { id: marker.id, unit: "" },
data: { unit: stated },
});
if (count === 0) return false;
if (count === 0) {
// Another reading set the unit first: carry on with the one it chose,
// which the caller then compares as usual.
const current = await client.biomarker.findUnique({
where: { id: marker.id },
select: { unit: true },
});
if (current) marker.unit = current.unit;
return false;
}
marker.unit = stated;
return true;
}
@@ -166,6 +166,33 @@ describe("a marker whose readings are all text", () => {
expect(stored.unit).toBe("");
});
it("does not count a deleted number as holding the unit", async () => {
const marker = await textMarker();
await prisma.labResult.create({
data: {
userId: USER,
biomarkerId: marker.id,
analyte: marker.name,
value: 12,
unit: "",
takenAt: new Date("2026-08-01T08:00:00.000Z"),
source: "MANUAL",
deletedAt: new Date("2026-08-02T08:00:00.000Z"),
},
});
const res = await postReading({
biomarkerId: marker.id,
value: 18,
unit: "1",
takenAt: "2026-10-01T08:00:00.000Z",
});
expect(res.status).toBe(201);
const stored = await prisma.biomarker.findUniqueOrThrow({
where: { id: marker.id },
});
expect(stored.unit).toBe("1");
});
it("leaves a marker with a unit of its own refusing a unitless number", async () => {
const marker = await prisma.biomarker.create({
data: { userId: USER, name: "Creatinine", unit: "mg/dL" },