chore: commit chybějících souborů
This commit is contained in:
1 parent
f1ce3ff297
commit
9e72f431a1
7 files changed
+890
No files matched your search
@@ -0,0 +1,209 @@
|
||||
process.env.CALORIE_PROVIDER = 'none';
|
||||
|
||||
import { addActivity, deleteActivity, getActivityDay, setActivityMultiplier, updateActivity } from "../activities";
|
||||
import { applyWorkoutTemplate, deleteWorkoutTemplate, saveWorkoutTemplate } from "../workouts";
|
||||
import { getActivityStats } from "../statsService";
|
||||
import { getWorkoutPlans } from "../workoutPlans";
|
||||
import { resetMemoryStorage } from "../storage/memory";
|
||||
import { applyMealTemplate, saveMealTemplate } from "../mealTemplates";
|
||||
import { addMeal, getDay } from "../meals";
|
||||
import { ActivityUnit, MealType, PlanDeviationKind, WorkoutItemKind } from "../../../types/gen/types.gen";
|
||||
|
||||
const LOGIN = 'tester';
|
||||
const DATE = '2025-03-03';
|
||||
const NEXT_DATE = '2025-03-04';
|
||||
|
||||
/** Cvik jako položka šablony. */
|
||||
const cvik = (name: string, quantity: number, rate: number) => ({
|
||||
kind: WorkoutItemKind.CVIK,
|
||||
name,
|
||||
unit: ActivityUnit.OPAKOVANI,
|
||||
quantity,
|
||||
caloriesPer100Units: rate,
|
||||
});
|
||||
|
||||
/** Založí šablonu Kliky 100 × 0,5 kcal + Dřepy 100 × 0,3 kcal, tedy 80 kcal. */
|
||||
async function createWorkout(): Promise<string> {
|
||||
const [template] = await saveWorkoutTemplate(LOGIN, undefined, 'Workout', [
|
||||
cvik('Kliky', 100, 50),
|
||||
cvik('Dřepy', 100, 30),
|
||||
]);
|
||||
return template.id;
|
||||
}
|
||||
|
||||
const stats = () => getActivityStats(LOGIN, '2025-03-01', '2025-03-31');
|
||||
|
||||
beforeEach(() => resetMemoryStorage());
|
||||
|
||||
describe('statistiky výdeje', () => {
|
||||
|
||||
it('odcvičená šablona se shoduje s plánem', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
|
||||
const result = await stats();
|
||||
expect(result.templateCalories).toBe(80);
|
||||
expect(result.plannedCalories).toBe(80);
|
||||
expect(result.extraCalories).toBe(0);
|
||||
expect(result.byTemplate).toEqual([expect.objectContaining({
|
||||
name: 'Workout', dayCount: 1, plannedCalories: 80, actualCalories: 80,
|
||||
plannedItemCount: 2, doneItemCount: 2,
|
||||
})]);
|
||||
});
|
||||
|
||||
it('smazaný cvik ze šablony se projeví jako odchylka', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
const drepy = (await getActivityDay(LOGIN, DATE)).entries.find(entry => entry.name === 'Dřepy')!;
|
||||
await deleteActivity(LOGIN, drepy.id, DATE);
|
||||
|
||||
const result = await stats();
|
||||
expect(result.templateCalories).toBe(50);
|
||||
expect(result.plannedCalories).toBe(80);
|
||||
expect(result.byTemplate[0]).toEqual(expect.objectContaining({ plannedItemCount: 2, doneItemCount: 1 }));
|
||||
});
|
||||
|
||||
it('zkrácený cvik ze šablony se projeví jako odchylka', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
const kliky = (await getActivityDay(LOGIN, DATE)).entries.find(entry => entry.name === 'Kliky')!;
|
||||
await setActivityMultiplier(LOGIN, kliky.id, DATE, 0.5);
|
||||
|
||||
const result = await stats();
|
||||
expect(result.templateCalories).toBe(55);
|
||||
expect(result.plannedCalories).toBe(80);
|
||||
});
|
||||
|
||||
it('smazání všech cviků šablony vrátí její použití, nepočítá se jako odchylka', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
for (const entry of (await getActivityDay(LOGIN, DATE)).entries) {
|
||||
await deleteActivity(LOGIN, entry.id, DATE);
|
||||
}
|
||||
|
||||
expect(await getWorkoutPlans(LOGIN, DATE)).toEqual([]);
|
||||
const result = await stats();
|
||||
expect(result.plannedCalories).toBe(0);
|
||||
expect(result.byTemplate).toEqual([]);
|
||||
});
|
||||
|
||||
it('plán se drží podoby šablony v době použití, ne její pozdější úpravy', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
await saveWorkoutTemplate(LOGIN, id, 'Workout v2', [cvik('Kliky', 200, 50)]);
|
||||
|
||||
const result = await stats();
|
||||
expect(result.plannedCalories).toBe(80);
|
||||
expect(result.byTemplate[0].name).toBe('Workout');
|
||||
});
|
||||
|
||||
it('aktivity mimo šablonu se sčítají zvlášť podle názvu', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
await addActivity(LOGIN, { date: DATE, name: 'Chůze', unit: ActivityUnit.KROKY, quantity: 5000, caloriesPer100Units: 4 });
|
||||
await addActivity(LOGIN, { date: NEXT_DATE, name: 'Chůze', unit: ActivityUnit.KROKY, quantity: 5000, caloriesPer100Units: 4 });
|
||||
|
||||
const result = await stats();
|
||||
expect(result.totalCalories).toBe(480);
|
||||
expect(result.extraCalories).toBe(400);
|
||||
expect(result.extras).toEqual([{ key: 'Chůze', calories: 400, count: 2 }]);
|
||||
expect(result.dayCount).toBe(2);
|
||||
expect(result.byDay).toEqual([
|
||||
{ period: DATE, templateCalories: 80, plannedCalories: 80, extraCalories: 200 },
|
||||
{ period: NEXT_DATE, templateCalories: 0, plannedCalories: 0, extraCalories: 200 },
|
||||
]);
|
||||
});
|
||||
|
||||
it('cvik přesunutý do jiného dne chybí v původním a v novém se bere bez odchylky', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
const kliky = (await getActivityDay(LOGIN, DATE)).entries.find(entry => entry.name === 'Kliky')!;
|
||||
await updateActivity(LOGIN, kliky.id, { ...kliky, date: NEXT_DATE });
|
||||
|
||||
const result = await stats();
|
||||
// Původní den: plán 80, zůstaly dřepy za 30; nový den: kliky 50 bez plánu
|
||||
expect(result.templateCalories).toBe(80);
|
||||
expect(result.plannedCalories).toBe(130);
|
||||
});
|
||||
|
||||
it('šablona smazaná po použití se ve statistikách drží názvu z plánu', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
await deleteWorkoutTemplate(LOGIN, id);
|
||||
|
||||
const result = await stats();
|
||||
expect(result.byTemplate[0].name).toBe('Workout');
|
||||
});
|
||||
|
||||
it('odmítne obrácený rozsah', async () => {
|
||||
await expect(getActivityStats(LOGIN, '2025-03-31', '2025-03-01')).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('denní detail výdeje', () => {
|
||||
|
||||
it('ukáže, jaká šablona se použila a který cvik z ní chybí', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
const drepy = (await getActivityDay(LOGIN, DATE)).entries.find(entry => entry.name === 'Dřepy')!;
|
||||
await deleteActivity(LOGIN, drepy.id, DATE);
|
||||
|
||||
const [day] = (await stats()).days;
|
||||
expect(day.date).toBe(DATE);
|
||||
expect(day.templates).toEqual([expect.objectContaining({
|
||||
name: 'Workout', plannedCalories: 80, actualCalories: 50, hasPlan: true,
|
||||
deviations: [{
|
||||
kind: PlanDeviationKind.ODEBRANO,
|
||||
planned: { name: 'Dřepy', unit: ActivityUnit.OPAKOVANI, quantity: 100, calories: 30 },
|
||||
}],
|
||||
})]);
|
||||
});
|
||||
|
||||
it('upravený cvik ukáže plán i skutečnost', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
const kliky = (await getActivityDay(LOGIN, DATE)).entries.find(entry => entry.name === 'Kliky')!;
|
||||
await updateActivity(LOGIN, kliky.id, { ...kliky, quantity: 40 });
|
||||
|
||||
const [usage] = (await stats()).days[0].templates;
|
||||
expect(usage.deviations).toEqual([{
|
||||
kind: PlanDeviationKind.UPRAVENO,
|
||||
planned: expect.objectContaining({ name: 'Kliky', quantity: 100, calories: 50 }),
|
||||
actual: expect.objectContaining({ name: 'Kliky', quantity: 40, calories: 20 }),
|
||||
}]);
|
||||
});
|
||||
|
||||
it('odcvičená šablona nemá žádnou odchylku', async () => {
|
||||
const id = await createWorkout();
|
||||
await applyWorkoutTemplate(LOGIN, id, DATE);
|
||||
expect((await stats()).days[0].templates[0].deviations).toEqual([]);
|
||||
});
|
||||
|
||||
it('aktivity mimo šablonu vrátí jednotlivě i se součtem', async () => {
|
||||
await addActivity(LOGIN, { date: DATE, name: 'Chůze', unit: ActivityUnit.KROKY, quantity: 5000, caloriesPer100Units: 4 });
|
||||
await addActivity(LOGIN, { date: DATE, name: 'Běh', unit: ActivityUnit.MINUTY, quantity: 20, caloriesPer100Units: 1000 });
|
||||
|
||||
const [day] = (await stats()).days;
|
||||
expect(day.templates).toEqual([]);
|
||||
expect(day.extraCalories).toBe(400);
|
||||
expect(day.extras.map(item => item.name)).toEqual(['Chůze', 'Běh']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('vazba jídla na šablonu', () => {
|
||||
|
||||
it('jídlo ze šablony si pamatuje, ze které vzniklo', async () => {
|
||||
const [template] = await saveMealTemplate(LOGIN, { name: 'Štrůdl', mealType: MealType.PULNOCNI_SVACINA });
|
||||
await applyMealTemplate(LOGIN, template.id, DATE);
|
||||
|
||||
const [meal] = (await getDay(LOGIN, DATE)).entries;
|
||||
expect(meal.templateId).toBe(template.id);
|
||||
expect(meal.mealType).toBe(MealType.PULNOCNI_SVACINA);
|
||||
});
|
||||
|
||||
it('ručně zadané jídlo vazbu nemá a klient ji podstrčit nemůže', async () => {
|
||||
await addMeal(LOGIN, { date: DATE, mealType: MealType.OBED, name: 'Guláš', templateId: 'podvrh' });
|
||||
const [meal] = (await getDay(LOGIN, DATE)).entries;
|
||||
expect(meal.templateId).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user