From 23f13d7139f932a21cec2a2d460319fb1c61f561 Mon Sep 17 00:00:00 2001 From: Till JS Date: Thu, 9 Apr 2026 15:45:35 +0200 Subject: [PATCH] test(mana/web/nutriphi): cover mealMutations.update Five new cases against fake-indexeddb covering the new update mutation: - patches description and re-encrypts (verified via ENC_PREFIX wire format check + absence of original AND new plaintext in the blob) - patches numeric nutrition fields (stays plaintext for aggregation) - partial update only touches the supplied fields (mealType change does not zero out nutrition) - bumps updatedAt - throws on missing id Total nutriphi suite is now 16/16 cases, ~50ms. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../lib/modules/nutriphi/mutations.test.ts | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/apps/mana/apps/web/src/lib/modules/nutriphi/mutations.test.ts b/apps/mana/apps/web/src/lib/modules/nutriphi/mutations.test.ts index c00a8805b..4a78618bd 100644 --- a/apps/mana/apps/web/src/lib/modules/nutriphi/mutations.test.ts +++ b/apps/mana/apps/web/src/lib/modules/nutriphi/mutations.test.ts @@ -191,6 +191,76 @@ describe('mealMutations.createFromPhoto', () => { }); }); +describe('mealMutations.update', () => { + it('patches the description and re-encrypts it', async () => { + const created = await mealMutations.create({ + mealType: 'lunch', + description: 'Originaltext', + nutrition: sampleNutrition, + }); + + const result = await mealMutations.update(created.id, { + description: 'Geänderter Text', + }); + + expect(result.description).toBe('Geänderter Text'); + // And the row in Dexie has the new description, encrypted. + const raw = await meals().get(created.id); + expect(raw?.description.startsWith(ENC_PREFIX)).toBe(true); + expect(raw?.description).not.toContain('Geänderter'); + expect(raw?.description).not.toContain('Originaltext'); + }); + + it('patches numeric nutrition fields', async () => { + const created = await mealMutations.create({ + mealType: 'lunch', + description: 'Suppe', + nutrition: sampleNutrition, + }); + + const result = await mealMutations.update(created.id, { + nutrition: { ...sampleNutrition, calories: 999 }, + }); + + expect(result.nutrition?.calories).toBe(999); + // Plaintext on disk for fast aggregation. + const raw = await meals().get(created.id); + expect(raw?.nutrition?.calories).toBe(999); + }); + + it('only touches the fields supplied in the dto', async () => { + const created = await mealMutations.create({ + mealType: 'breakfast', + description: 'Müsli', + nutrition: sampleNutrition, + }); + + await mealMutations.update(created.id, { mealType: 'snack' }); + + const raw = await meals().get(created.id); + expect(raw?.mealType).toBe('snack'); + expect(raw?.nutrition).toEqual(sampleNutrition); + }); + + it('bumps updatedAt', async () => { + const created = await mealMutations.create({ + mealType: 'lunch', + description: 'Pasta', + }); + const before = (await meals().get(created.id))!.updatedAt; + await new Promise((r) => setTimeout(r, 5)); + await mealMutations.update(created.id, { description: 'Pasta mit Pesto' }); + const after = (await meals().get(created.id))!.updatedAt; + expect(after).not.toBe(before); + }); + + it('throws if the meal does not exist', async () => { + await expect(mealMutations.update('nonexistent-id', { description: 'foo' })).rejects.toThrow( + /disappeared/ + ); + }); +}); + describe('mealMutations.delete', () => { it('soft-deletes by stamping deletedAt + updatedAt', async () => { const created = await mealMutations.create({