mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
test(mood): align bulk catalog tests with the revised factor set
The retired RATED factors leave no seeded factor with a scale narrower than the 1..5 rating envelope, so the per-tag out-of-scale "skipped" path is no longer reachable through the catalog (it stays covered by the `resolveRatedFactors` unit tests). Update the bulk integration tests to use surviving factors and assert the envelope rejection, and swap a deactivated tag for an active one in the tag-key round-trip. Refresh the stale per-tag-scale comment in the rated-factor schema. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
a3e741c23c
commit
c59290ddc6
2 files changed
+23
-23
No files matched your search
@@ -109,8 +109,9 @@ const structuredTagKeys = z.array(z.string().max(60)).max(30);
|
||||
// The Zod `rating` bound here is the OUTER envelope (1..5 covers every
|
||||
// seeded factor's scale). The REAL gate is per-tag: after resolving each
|
||||
// key to its `MoodTag`, the server rejects a rating outside the tag's own
|
||||
// `scaleMin..scaleMax` (e.g. 1..2 for `factor_conflict`). See
|
||||
// `resolveRatedFactors` in `src/lib/mood/tag-links.ts`.
|
||||
// `scaleMin..scaleMax` (every seeded factor is 1..5 today, but a factor
|
||||
// with a tighter scale is rejected here too). See `resolveRatedFactors`
|
||||
// in `src/lib/mood/tag-links.ts`.
|
||||
const ratedFactor = z.object({
|
||||
key: z.string().max(60),
|
||||
rating: z.number().int().min(1).max(5),
|
||||
|
||||
@@ -161,10 +161,10 @@ describe("POST /api/mood-entries/bulk (real Postgres)", () => {
|
||||
{
|
||||
mood: "GUT",
|
||||
moodLoggedAt: "2026-05-16T08:00:00.000Z",
|
||||
// Two valid catalog keys (one from the new hobbies category,
|
||||
// one nutrition-detail key) plus one unknown key that must be
|
||||
// dropped silently.
|
||||
tagKeys: ["movies", "fast_food", "not_a_real_tag"],
|
||||
// Two valid catalog keys (one from the hobbies category, one
|
||||
// health key) plus one unknown key that must be dropped
|
||||
// silently.
|
||||
tagKeys: ["movies", "alcohol", "not_a_real_tag"],
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -182,7 +182,7 @@ describe("POST /api/mood-entries/bulk (real Postgres)", () => {
|
||||
});
|
||||
const keys = links.map((l) => l.moodTag.key).sort();
|
||||
// The unknown key is dropped; the two catalog keys round-trip.
|
||||
expect(keys).toEqual(["fast_food", "movies"]);
|
||||
expect(keys).toEqual(["alcohol", "movies"]);
|
||||
});
|
||||
|
||||
it("persists a rated factor with its score on the link (v1.12.0)", async () => {
|
||||
@@ -195,7 +195,7 @@ describe("POST /api/mood-entries/bulk (real Postgres)", () => {
|
||||
moodLoggedAt: "2026-05-16T08:00:00.000Z",
|
||||
ratedFactors: [
|
||||
{ key: "factor_work", rating: 4 },
|
||||
{ key: "factor_conflict", rating: 2 },
|
||||
{ key: "factor_sleep_quality", rating: 2 },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -220,12 +220,18 @@ describe("POST /api/mood-entries/bulk (real Postgres)", () => {
|
||||
rating: l.rating,
|
||||
})),
|
||||
).toEqual([
|
||||
{ key: "factor_conflict", kind: "RATED", rating: 2 },
|
||||
{ key: "factor_sleep_quality", kind: "RATED", rating: 2 },
|
||||
{ key: "factor_work", kind: "RATED", rating: 4 },
|
||||
]);
|
||||
});
|
||||
|
||||
it("marks the entry skipped when a rated factor is out of scale (v1.12.0)", async () => {
|
||||
it("rejects the batch when a rated factor falls outside the 1..5 envelope (v1.12.0)", async () => {
|
||||
// Every seeded RATED factor uses the full 1..5 slider scale, so the
|
||||
// outer Zod envelope (1..5) is the binding out-of-range gate — a
|
||||
// rating above it fails parse and 422s the whole batch before any
|
||||
// row lands. The narrower per-tag-scale path (a factor with a tighter
|
||||
// `scaleMin..scaleMax`) is covered by the `resolveRatedFactors` unit
|
||||
// tests.
|
||||
const { POST } = await import("@/app/api/mood-entries/bulk/route");
|
||||
const res = await POST(
|
||||
makeRequest({
|
||||
@@ -233,26 +239,19 @@ describe("POST /api/mood-entries/bulk (real Postgres)", () => {
|
||||
{
|
||||
mood: "GUT",
|
||||
moodLoggedAt: "2026-05-16T08:00:00.000Z",
|
||||
// factor_conflict scale is 1..2; rating 5 is out of range.
|
||||
ratedFactors: [{ key: "factor_conflict", rating: 5 }],
|
||||
// factor_sleep_quality scale is 1..5; rating 9 is out of range.
|
||||
ratedFactors: [{ key: "factor_sleep_quality", rating: 9 }],
|
||||
},
|
||||
],
|
||||
}),
|
||||
);
|
||||
expect(res.status).toBe(200);
|
||||
const json = (await res.json()) as {
|
||||
data: { entries: Array<{ status: string }> };
|
||||
};
|
||||
expect(json.data.entries[0].status).toBe("skipped");
|
||||
expect(res.status).toBe(422);
|
||||
|
||||
// The mood row upserted, but no rated link landed.
|
||||
const entry = await getPrismaClient().moodEntry.findFirstOrThrow({
|
||||
// Nothing persisted — the batch was rejected at parse time.
|
||||
const entryCount = await getPrismaClient().moodEntry.count({
|
||||
where: { userId: TEST_USER_ID },
|
||||
});
|
||||
const ratedCount = await getPrismaClient().moodEntryTagLink.count({
|
||||
where: { moodEntryId: entry.id, rating: { not: null } },
|
||||
});
|
||||
expect(ratedCount).toBe(0);
|
||||
expect(entryCount).toBe(0);
|
||||
});
|
||||
|
||||
it("ignores tagKeys that resolve to no catalog rows", async () => {
|
||||
|
||||
Reference in new issue
Block a user