feat(labs): unit conversion, analyte registry, marker normalization
This commit is contained in:
55
server/test/normalize.test.ts
Normal file
55
server/test/normalize.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { resolveAnalyte } from "../src/lib/analytes";
|
||||
import { normalizeMarker } from "../src/lib/normalize";
|
||||
import { convert } from "../src/lib/units";
|
||||
|
||||
describe("units", () => {
|
||||
test("dimensional scaling: ng/mL → µg/L is 1:1", () => {
|
||||
expect(convert(30, "ng/mL", "µg/L")).toBeCloseTo(30);
|
||||
});
|
||||
test("mass→mole bridge needs molar mass", () => {
|
||||
expect(convert(100, "mg/dL", "mmol/L")).toBeNull();
|
||||
expect(convert(100, "mg/dL", "mmol/L", 180.16)).toBeCloseTo(5.551, 2); // glucose
|
||||
});
|
||||
test("mole→mass: testosterone 20 nmol/L → ng/dL", () => {
|
||||
expect(convert(20, "nmol/L", "ng/dL", 288.42)).toBeCloseTo(576.8, 0);
|
||||
});
|
||||
test("percent only converts to percent", () => {
|
||||
expect(convert(42, "%", "%")).toBe(42);
|
||||
expect(convert(42, "%", "mg/dL")).toBeNull();
|
||||
});
|
||||
test("count units: x10^9/L → thousand/uL is 1:1", () => {
|
||||
expect(convert(6.1, "x10^9/L", "thousand/uL")).toBeCloseTo(6.1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("analytes", () => {
|
||||
test("alias resolution is case/space-insensitive", () => {
|
||||
expect(resolveAnalyte("Total Testosterone")?.key).toBe("testosterone_total");
|
||||
expect(resolveAnalyte("HbA1c")?.key).toBe("hba1c");
|
||||
expect(resolveAnalyte("definitely not a marker")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("normalizeMarker", () => {
|
||||
test("known analyte converts to canonical unit and registry panel", () => {
|
||||
const n = normalizeMarker({ panel: "chemistry", name: "Glucose", value: "100", unit: "mg/dL", referenceRange: "70-99", flagged: true });
|
||||
expect(n.analyteKey).toBe("glucose");
|
||||
expect(n.panel).toBe("metabolic"); // registry panel wins for consistent grouping
|
||||
expect(n.valueCanonical).toBeCloseTo(5.551, 2);
|
||||
expect(n.canonicalUnit).toBe("mmol/L");
|
||||
expect(n.value).toBe("100"); // raw preserved
|
||||
expect(n.flagged).toBe(true);
|
||||
});
|
||||
test("unknown marker keeps raw fields, no canonical value", () => {
|
||||
const n = normalizeMarker({ name: "Exotic Marker X", value: "1.2", unit: "u/L" });
|
||||
expect(n.analyteKey).toBeNull();
|
||||
expect(n.marker).toBe("Exotic Marker X");
|
||||
expect(n.valueCanonical).toBeNull();
|
||||
});
|
||||
test("comparator values parse numerically", () => {
|
||||
const n = normalizeMarker({ name: "hs-CRP", value: "<0.3", unit: "mg/L" });
|
||||
expect(n.valueNum).toBeCloseTo(0.3);
|
||||
expect(n.value).toBe("<0.3");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user