Files
food-tracer/server/src/tests/mealGroups.test.ts
T
Ondřej Anděl e13f7ce8e5 fix: recepty přesnuty k jídlům
feat: přidána podpora fotek
2026-10-12 10:20:12 +02:00

175 lines
8.1 KiB
TypeScript

process.env.CALORIE_PROVIDER = 'none';
import { addMeal, getDay } from "../meals";
import { fillMealGroup, getMealGroups, renameMealGroup } from "../mealGroups";
import { searchCalories } from "../calories";
import { resetMemoryStorage } from "../storage/memory";
import { MealType } from "../../../types/gen/types.gen";
const LOGIN = 'tester';
beforeEach(() => resetMemoryStorage());
const cina = { mealType: MealType.OBED, name: 'Čína', source: 'Chefie' };
describe('skupiny jídel podle názvu a zdroje', () => {
it('seskupí jídla podle názvu i zdroje a spočítá chybějící údaje', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01', price: 13900 });
await addMeal(LOGIN, { ...cina, date: '2025-03-02', name: 'cina', price: 14900 });
await addMeal(LOGIN, { ...cina, date: '2025-03-03', price: 14900, weight: 400, caloriesPer100g: 150 });
await addMeal(LOGIN, { ...cina, date: '2025-03-04', source: 'doma' });
const groups = await getMealGroups(LOGIN);
expect(groups).toHaveLength(2);
expect(groups[0]).toMatchObject({
name: 'Čína', source: 'Chefie', count: 3, lastDate: '2025-03-03',
missingCaloriesCount: 2, missingWeightCount: 2, missingNutrientsCount: 3,
values: { weight: 400, caloriesPer100g: 150 },
days: [
{ date: '2025-03-03', incomplete: true },
{ date: '2025-03-02', incomplete: true },
{ date: '2025-03-01', incomplete: true },
],
});
expect(groups[1]).toMatchObject({ source: 'doma', count: 1 });
});
it('u dnů skupiny označí ty, kde jídlu něco chybí', async () => {
await addMeal(LOGIN, {
...cina, date: '2025-03-01', weight: 400, caloriesPer100g: 150, nutrientsPer100g: { protein: 8 },
});
await addMeal(LOGIN, { ...cina, date: '2025-03-02', price: 13900 });
const [group] = await getMealGroups(LOGIN);
expect(group.days).toEqual([
{ date: '2025-03-02', incomplete: true },
{ date: '2025-03-01', incomplete: false },
]);
});
it('energii a živiny předvyplní i z jídla stejného názvu odjinud, sazbu ne', async () => {
await addMeal(LOGIN, {
...cina, date: '2025-03-01', source: 'doma', pricePer100g: 2000, caloriesPer100g: 140,
nutrientsPer100g: { protein: 8 },
});
await addMeal(LOGIN, { ...cina, date: '2025-03-02', price: 13900 });
const group = (await getMealGroups(LOGIN)).find(item => item.source === 'Chefie');
expect(group?.values).toEqual({ caloriesPer100g: 140, nutrientsPer100g: { protein: 8 } });
});
});
describe('hromadné doplnění údajů do skupiny', () => {
it('doplní jen chybějící hodnoty a kalorie dopočte z gramáže', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01', price: 13900 });
await addMeal(LOGIN, { ...cina, date: '2025-03-02', price: 14900, weight: 300 });
await addMeal(LOGIN, { ...cina, date: '2025-03-03', price: 14900, calories: 700 });
await addMeal(LOGIN, { ...cina, date: '2025-03-04', source: 'doma' });
const result = await fillMealGroup(LOGIN, 'cina', 'chefie', {
weight: 400, caloriesPer100g: 150, nutrientsPer100g: { protein: 8 },
});
expect(result).toEqual({ count: 3, dates: ['2025-03-01', '2025-03-02', '2025-03-03'] });
expect((await getDay(LOGIN, '2025-03-01')).entries[0]).toMatchObject({ weight: 400, calories: 600, price: 13900 });
expect((await getDay(LOGIN, '2025-03-02')).entries[0]).toMatchObject({ weight: 300, calories: 450 });
// Ručně zadané kalorie zůstanou, energie na 100 g by je přepočítala
const third = (await getDay(LOGIN, '2025-03-03')).entries[0];
expect(third).toMatchObject({ weight: 400, calories: 700, nutrientsPer100g: { protein: 8 } });
expect(third.caloriesPer100g).toBeUndefined();
expect((await getDay(LOGIN, '2025-03-04')).entries[0].calories).toBeUndefined();
});
it('gramáž dopočte z vlastní ceny jídla, když přibude sazba', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01', price: 13200 });
await fillMealGroup(LOGIN, 'Čína', 'Chefie', { pricePer100g: 4400, weight: 400, caloriesPer100g: 150 });
expect((await getDay(LOGIN, '2025-03-01')).entries[0]).toMatchObject({ weight: 300, calories: 450 });
});
it('vyplněné živiny nepřepíše, chybějící doplní', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01', nutrientsPer100g: { protein: 5 } });
await fillMealGroup(LOGIN, 'Čína', 'Chefie', { nutrientsPer100g: { protein: 8, fat: 4 } });
expect((await getDay(LOGIN, '2025-03-01')).entries[0].nutrientsPer100g).toEqual({ protein: 5, fat: 4 });
});
it('jídla, kterým nic nechybí, nepočítá', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01', weight: 300, caloriesPer100g: 150 });
const result = await fillMealGroup(LOGIN, 'Čína', 'Chefie', { weight: 400, caloriesPer100g: 200 });
expect(result).toEqual({ count: 0, dates: [] });
expect((await getDay(LOGIN, '2025-03-01')).entries[0].calories).toBe(450);
});
it('bez zdroje doplní jen jídla bez zdroje', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01', source: undefined });
await addMeal(LOGIN, { ...cina, date: '2025-03-02' });
const result = await fillMealGroup(LOGIN, 'Čína', undefined, { calories: 600 });
expect(result.dates).toEqual(['2025-03-01']);
expect((await getDay(LOGIN, '2025-03-02')).entries[0].calories).toBeUndefined();
});
});
describe('přejmenování skupiny jídel', () => {
it('přejmenuje a přesune do jiného zdroje všechna jídla skupiny', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01', price: 13900 });
await addMeal(LOGIN, { ...cina, date: '2025-03-02', name: 'cina' });
await addMeal(LOGIN, { ...cina, date: '2025-03-02', source: 'doma' });
const result = await renameMealGroup(LOGIN, 'Čína', 'Chefie', 'Kuře kung-pao', 'Everest');
expect(result).toEqual({ count: 2, dates: ['2025-03-01', '2025-03-02'] });
expect((await getDay(LOGIN, '2025-03-01')).entries[0])
.toMatchObject({ name: 'Kuře kung-pao', source: 'Everest', price: 13900 });
const second = (await getDay(LOGIN, '2025-03-02')).entries;
expect(second.map(entry => `${entry.name}|${entry.source}`).sort())
.toEqual(['Kuře kung-pao|Everest', 'Čína|doma']);
});
it('prázdný zdroj zdroj odebere a jídla se sloučí se skupinou bez zdroje', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01' });
await addMeal(LOGIN, { ...cina, date: '2025-03-02', source: undefined });
await renameMealGroup(LOGIN, 'Čína', 'Chefie', 'Čína', undefined);
const groups = await getMealGroups(LOGIN);
expect(groups).toHaveLength(1);
expect(groups[0].count).toBe(2);
expect((await getDay(LOGIN, '2025-03-01')).entries[0]).not.toHaveProperty('source');
});
it('jídla se shodným zápisem nepočítá', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01' });
await addMeal(LOGIN, { ...cina, date: '2025-03-02', name: 'čína' });
const result = await renameMealGroup(LOGIN, 'Čína', 'Chefie', 'Čína', 'Chefie');
expect(result).toEqual({ count: 1, dates: ['2025-03-02'] });
});
it('energii na 100 g si knihovna zapamatuje i pod novým názvem', async () => {
await addMeal(LOGIN, { ...cina, date: '2025-03-01', weight: 400, caloriesPer100g: 150 });
await renameMealGroup(LOGIN, 'Čína', 'Chefie', 'Kuře kung-pao', 'Chefie');
const { suggestions } = await searchCalories(LOGIN, 'Kuře kung-pao');
expect(suggestions[0]).toMatchObject({ name: 'Kuře kung-pao', caloriesPer100g: 150 });
});
it('odmítne prázdný nový název', async () => {
await expect(renameMealGroup(LOGIN, 'Čína', 'Chefie', ' ', 'Chefie')).rejects.toThrow(/nový název/);
});
});