mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 22:50:51 +00:00
fix(cycle): accept a symptom logged without an intensity
Picking a symptom without tapping an intensity is how most of them are logged. The sheet sends severity null for those, the schema took a number or nothing at all, and the day log came back 422 with two issues for two symptoms. All the sheet could say was "Could not save. Try again.", and the cycle saved fine as soon as the symptoms were removed, which sent people looking in the wrong place. Nothing else disagreed: the link row has always stored an unrated symptom as a plain presence, and the writer already maps a non-number to NULL. Only the gate at the door refused it, on both the create and the patch path, and no test touched either schema. Refs #1001
This commit is contained in:
1 parent
1fe90f3fab
commit
bff083f532
2 files changed
+77
-2
No files matched your search
@@ -0,0 +1,65 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
cycleDayLogInputSchema,
|
||||
cycleDayLogPatchSchema,
|
||||
} from "@/lib/validations/cycle";
|
||||
|
||||
/**
|
||||
* A symptom picked without an intensity is the ordinary way to log one.
|
||||
*
|
||||
* The sheet holds the picker as key to intensity and sends `severity: null`
|
||||
* for every symptom the user did not rate, which is most of them. The schema
|
||||
* took a number or nothing at all, so those entries came back 422 and the
|
||||
* sheet showed "Could not save. Try again." with no way to tell why. The link
|
||||
* row has always stored an unrated symptom as a plain presence; only the
|
||||
* gate at the door disagreed, and nothing tested it.
|
||||
*/
|
||||
const BASE_INPUT = {
|
||||
date: "2026-09-17",
|
||||
loggedAt: new Date().toISOString(),
|
||||
} as const;
|
||||
|
||||
describe("cycle day-log symptoms accept an unrated selection", () => {
|
||||
for (const [name, schema] of [
|
||||
["create", cycleDayLogInputSchema],
|
||||
["patch", cycleDayLogPatchSchema],
|
||||
] as const) {
|
||||
it(`${name}: takes a symptom with no intensity at all`, () => {
|
||||
const parsed = schema.safeParse({
|
||||
...BASE_INPUT,
|
||||
symptoms: [{ key: "cramps" }],
|
||||
});
|
||||
expect(parsed.success, JSON.stringify(parsed.error?.issues)).toBe(true);
|
||||
});
|
||||
|
||||
it(`${name}: takes an explicit null intensity, which is what the sheet sends`, () => {
|
||||
const parsed = schema.safeParse({
|
||||
...BASE_INPUT,
|
||||
symptoms: [
|
||||
{ key: "cramps", severity: null },
|
||||
{ key: "back_pain", severity: null },
|
||||
],
|
||||
});
|
||||
expect(parsed.success, JSON.stringify(parsed.error?.issues)).toBe(true);
|
||||
});
|
||||
|
||||
it(`${name}: still takes a rated symptom`, () => {
|
||||
const parsed = schema.safeParse({
|
||||
...BASE_INPUT,
|
||||
symptoms: [{ key: "cramps", severity: 3 }],
|
||||
});
|
||||
expect(parsed.success).toBe(true);
|
||||
});
|
||||
|
||||
it(`${name}: still refuses an intensity outside 1 to 4`, () => {
|
||||
for (const severity of [0, 5, 2.5]) {
|
||||
const parsed = schema.safeParse({
|
||||
...BASE_INPUT,
|
||||
symptoms: [{ key: "cramps", severity }],
|
||||
});
|
||||
expect(parsed.success, `severity ${severity}`).toBe(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -131,7 +131,12 @@ export const cycleDayLogInputSchema = z.object({
|
||||
.array(
|
||||
z.object({
|
||||
key: z.string().min(1).max(80),
|
||||
severity: z.number().int().min(1).max(4).optional(),
|
||||
// NULL is the ordinary case, not an edge: picking a symptom without
|
||||
// tapping an intensity is how most entries are logged, and the link
|
||||
// row stores that as a plain presence. Rejecting it refused every
|
||||
// unrated symptom with a 422 the sheet could only show as
|
||||
// "Could not save".
|
||||
severity: z.number().int().min(1).max(4).nullable().optional(),
|
||||
}),
|
||||
)
|
||||
.max(40)
|
||||
@@ -181,7 +186,12 @@ export const cycleDayLogPatchSchema = z.object({
|
||||
.array(
|
||||
z.object({
|
||||
key: z.string().min(1).max(80),
|
||||
severity: z.number().int().min(1).max(4).optional(),
|
||||
// NULL is the ordinary case, not an edge: picking a symptom without
|
||||
// tapping an intensity is how most entries are logged, and the link
|
||||
// row stores that as a plain presence. Rejecting it refused every
|
||||
// unrated symptom with a 422 the sheet could only show as
|
||||
// "Could not save".
|
||||
severity: z.number().int().min(1).max(4).nullable().optional(),
|
||||
}),
|
||||
)
|
||||
.max(40)
|
||||
|
||||
Reference in new issue
Block a user