fix(plan): allow empty notes, use local time for past-date gate
Three related fixes from manual smoke testing: - Zod schema: drop min(1) on note field across all four schemas — note is optional in real usage, and the modal initialized it to empty. - assertNotPastDate: replace toISOString() (UTC) with local-time getters to match the rest of the codebase. Fixes off-by-one near day boundaries. - createEntry / updateEntry: also check the date_to endpoint for past dates, so a range can't extend backwards into the past even when its start is today or future. Defense in depth. Modal: drop `required` attribute and asterisk from the note field. Tests: add 5 new cases covering empty note, past date HTTP rejection, and the new to-endpoint check in updateEntry.
This commit is contained in:
@@ -305,6 +305,42 @@ describe("plan.service.createEntry", () => {
|
||||
);
|
||||
expect("error" in result).toBe(true);
|
||||
});
|
||||
|
||||
it("allows an empty note (text is optional)", async () => {
|
||||
const result = await createEntry(
|
||||
{
|
||||
user_id: adminUserId,
|
||||
date_from: "2099-07-15",
|
||||
date_to: "2099-07-15",
|
||||
category: "work",
|
||||
note: "",
|
||||
},
|
||||
adminUserId,
|
||||
false,
|
||||
);
|
||||
expect("data" in result).toBe(true);
|
||||
});
|
||||
|
||||
it("rejects a range whose date_to is in the past, even if date_from is today", async () => {
|
||||
// The range check `date_to >= date_from` fires before the past-date
|
||||
// check, so a backwards range returns 400 (correct UX). The to-endpoint
|
||||
// past-date check matters when updating an existing entry — see the
|
||||
// matching test in updateEntry below. Here we just confirm that a
|
||||
// range whose start is in the past is rejected as 403.
|
||||
const result = await createEntry(
|
||||
{
|
||||
user_id: adminUserId,
|
||||
date_from: "2000-01-01",
|
||||
date_to: "2000-01-05",
|
||||
category: "work",
|
||||
note: `${N}past-both`,
|
||||
},
|
||||
adminUserId,
|
||||
false,
|
||||
);
|
||||
expect("error" in result).toBe(true);
|
||||
if ("error" in result) expect(result.status).toBe(403);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -338,6 +374,31 @@ describe("plan.service.updateEntry", () => {
|
||||
expect((updated.oldData as any).note).toBe(`${N}original`);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects an update that pushes date_to into the past", async () => {
|
||||
// Future entry, but the user tries to extend its end backwards into
|
||||
// the past. The to-endpoint past-date check should fire.
|
||||
const created = await createEntry(
|
||||
{
|
||||
user_id: adminUserId,
|
||||
date_from: "2099-08-10",
|
||||
date_to: "2099-08-15",
|
||||
category: "work",
|
||||
note: `${N}to-shorten`,
|
||||
},
|
||||
adminUserId,
|
||||
false,
|
||||
);
|
||||
if (!("data" in created)) throw new Error("setup failed");
|
||||
const updated = await updateEntry(
|
||||
created.data.id,
|
||||
{ date_to: "2000-01-05" },
|
||||
adminUserId,
|
||||
false,
|
||||
);
|
||||
expect("error" in updated).toBe(true);
|
||||
if ("error" in updated) expect(updated.status).toBe(403);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -787,6 +848,28 @@ describe("HTTP /api/admin/plan", () => {
|
||||
expect(body.data.note).toBe(`${N}http create`);
|
||||
});
|
||||
|
||||
it("POST /entries accepts an empty note (note is optional)", async () => {
|
||||
const res = await authPost("/api/admin/plan/entries", adminToken, {
|
||||
user_id: adminUserId,
|
||||
date_from: "2097-08-05",
|
||||
date_to: "2097-08-05",
|
||||
category: "work",
|
||||
note: "",
|
||||
});
|
||||
expect(res.statusCode).toBe(201);
|
||||
});
|
||||
|
||||
it("POST /entries rejects a past date without ?force=1", async () => {
|
||||
const res = await authPost("/api/admin/plan/entries", adminToken, {
|
||||
user_id: adminUserId,
|
||||
date_from: "2000-02-01",
|
||||
date_to: "2000-02-01",
|
||||
category: "work",
|
||||
note: `${N}past http`,
|
||||
});
|
||||
expect(res.statusCode).toBe(403);
|
||||
});
|
||||
|
||||
it("POST /overrides with force=1 allows past date", async () => {
|
||||
const res = await authPost(
|
||||
"/api/admin/plan/overrides?force=1",
|
||||
|
||||
Reference in New Issue
Block a user