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) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-09 15:45:35 +02:00
parent 4fd6a5cc77
commit 23f13d7139

View file

@ -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({