feat(shopping): listShoppingList mit Aggregation + Skalierung
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 2m14s

This commit is contained in:
hsiegeln
2026-04-21 23:02:05 +02:00
parent 85bf197084
commit c31a9c6110
2 changed files with 98 additions and 4 deletions

View File

@@ -103,3 +103,73 @@ describe('setCartServings', () => {
expect(() => setCartServings(db, id, -3)).toThrow();
});
});
describe('listShoppingList aggregation', () => {
it('aggregates same name+unit across recipes', () => {
const db = openInMemoryForTest();
const a = insertRecipe(db, recipe({
title: 'Carbonara', servings_default: 4,
ingredients: [{ position: 1, quantity: 200, unit: 'g', name: 'Mehl', note: null, raw_text: '', section_heading: null }]
}));
const b = insertRecipe(db, recipe({
title: 'Lasagne', servings_default: 4,
ingredients: [{ position: 1, quantity: 200, unit: 'g', name: 'Mehl', note: null, raw_text: '', section_heading: null }]
}));
addRecipeToCart(db, a, null, 4);
addRecipeToCart(db, b, null, 4);
const rows = listShoppingList(db).rows;
expect(rows).toHaveLength(1);
expect(rows[0].name_key).toBe('mehl');
expect(rows[0].unit_key).toBe('g');
expect(rows[0].total_quantity).toBe(400);
expect(rows[0].from_recipes).toContain('Carbonara');
expect(rows[0].from_recipes).toContain('Lasagne');
});
it('keeps different units as separate rows', () => {
const db = openInMemoryForTest();
const id = insertRecipe(db, recipe({
servings_default: 4,
ingredients: [
{ position: 1, quantity: 100, unit: 'g', name: 'Mehl', note: null, raw_text: '', section_heading: null },
{ position: 2, quantity: 1, unit: 'Pck', name: 'Mehl', note: null, raw_text: '', section_heading: null }
]
}));
addRecipeToCart(db, id, null, 4);
const rows = listShoppingList(db).rows;
expect(rows).toHaveLength(2);
});
it('scales quantities by servings/servings_default', () => {
const db = openInMemoryForTest();
const id = insertRecipe(db, recipe({
servings_default: 4,
ingredients: [{ position: 1, quantity: 200, unit: 'g', name: 'Mehl', note: null, raw_text: '', section_heading: null }]
}));
addRecipeToCart(db, id, null, 2);
expect(listShoppingList(db).rows[0].total_quantity).toBe(100);
});
it('null quantity stays null after aggregation', () => {
const db = openInMemoryForTest();
const id = insertRecipe(db, recipe({
ingredients: [{ position: 1, quantity: null, unit: null, name: 'Salz', note: null, raw_text: '', section_heading: null }]
}));
addRecipeToCart(db, id, null);
const rows = listShoppingList(db).rows;
expect(rows[0].total_quantity).toBeNull();
expect(rows[0].unit_key).toBe('');
});
it('counts unchecked rows in uncheckedCount', () => {
const db = openInMemoryForTest();
const id = insertRecipe(db, recipe({
ingredients: [
{ position: 1, quantity: 1, unit: 'Stk', name: 'Apfel', note: null, raw_text: '', section_heading: null },
{ position: 2, quantity: 1, unit: 'Stk', name: 'Birne', note: null, raw_text: '', section_heading: null }
]
}));
addRecipeToCart(db, id, null);
expect(listShoppingList(db).uncheckedCount).toBe(2);
});
});