Cleanup after initial plan
This commit is contained in:
parent
d2a7e652b3
commit
245520fb50
53 changed files with 6733 additions and 621 deletions
|
|
@ -161,5 +161,112 @@ describe(SuggestionEngineService.name, () => {
|
|||
// Variety calculation: 7 days ago / 14 days = 0.5
|
||||
expect(suggestions[0]!.scores.variety).toBeCloseTo(0.5, 1);
|
||||
});
|
||||
|
||||
it('triggers reasoning branches for partial coverage and urgent items', async () => {
|
||||
const recipeC = {
|
||||
_id: 'recipeC',
|
||||
name: 'Recipe C',
|
||||
ingredients: [
|
||||
{ productId: 'prod1', quantity: 10, isOptional: false },
|
||||
{ productId: 'prod2', quantity: 10, isOptional: false },
|
||||
],
|
||||
};
|
||||
|
||||
mockRecipesRepo.findByHousehold.mockResolvedValue({ data: [recipeC] });
|
||||
|
||||
// 1. Coverage: (10/10 + 5/10)/2 = 0.75 (hits >0.5)
|
||||
// 2. Urgency: both set to urgent = 1.0 (hits >0.7)
|
||||
mockPantryRepo.findActiveByHousehold.mockResolvedValue([
|
||||
{ productId: 'prod1', quantity: 10, freshnessEstimate: { urgency: 'urgent' } },
|
||||
{ productId: 'prod2', quantity: 5, freshnessEstimate: { urgency: 'urgent' } },
|
||||
]);
|
||||
mockNutritionRepo.findByUser.mockResolvedValue(null);
|
||||
mockMealPlanRepo.findByHousehold.mockResolvedValue({ data: [] });
|
||||
|
||||
const suggestions = await service.getSuggestions('hh1', 'user1');
|
||||
expect(suggestions[0]!.scores.coverage).toBe(0.75);
|
||||
expect(suggestions[0]!.scores.urgency).toBe(1);
|
||||
expect(suggestions[0]!.reasoning).toContain('Uses several ingredients already stocked in your pantry.');
|
||||
expect(suggestions[0]!.reasoning).toContain('High priority: Saves expiring pantry items from going to waste!');
|
||||
});
|
||||
|
||||
it('triggers reasoning for moderately soon-to-expire items', async () => {
|
||||
const recipeD = {
|
||||
_id: 'recipeD',
|
||||
name: 'Recipe D',
|
||||
ingredients: [{ productId: 'prod1', quantity: 5, isOptional: false }],
|
||||
};
|
||||
|
||||
mockRecipesRepo.findByHousehold.mockResolvedValue({ data: [recipeD] });
|
||||
|
||||
// Urgency soon/expiringSoon has weight 0.7 (hits >0.4 branch)
|
||||
mockPantryRepo.findActiveByHousehold.mockResolvedValue([
|
||||
{ productId: 'prod1', quantity: 5, freshnessEstimate: { urgency: 'expiringSoon' } },
|
||||
]);
|
||||
mockNutritionRepo.findByUser.mockResolvedValue(null);
|
||||
mockMealPlanRepo.findByHousehold.mockResolvedValue({ data: [] });
|
||||
|
||||
const suggestions = await service.getSuggestions('hh1', 'user1');
|
||||
expect(suggestions[0]!.scores.urgency).toBe(0.7);
|
||||
expect(suggestions[0]!.reasoning).toContain('Helps use up items that should be consumed soon.');
|
||||
});
|
||||
|
||||
it('aggregates duplicate pantry items and handles normal/default urgencies', async () => {
|
||||
const recipeE = {
|
||||
_id: 'recipeE',
|
||||
name: 'Recipe E',
|
||||
ingredients: [
|
||||
{ productId: 'prod1', quantity: 5, isOptional: false },
|
||||
],
|
||||
};
|
||||
|
||||
mockRecipesRepo.findByHousehold.mockResolvedValue({ data: [recipeE] });
|
||||
|
||||
mockPantryRepo.findActiveByHousehold.mockResolvedValue([
|
||||
{ productId: 'prod1', quantity: 2, freshnessEstimate: { daysRemaining: 5, urgency: 'normal' } },
|
||||
{ productId: 'prod1', quantity: 3, freshnessEstimate: { daysRemaining: 10, urgency: 'unknown-type' } },
|
||||
]);
|
||||
mockNutritionRepo.findByUser.mockResolvedValue(null);
|
||||
mockMealPlanRepo.findByHousehold.mockResolvedValue({ data: [] });
|
||||
|
||||
const suggestions = await service.getSuggestions('hh1', 'user1');
|
||||
expect(suggestions[0]!.scores.coverage).toBe(1);
|
||||
expect(suggestions[0]!.scores.urgency).toBe(0.3);
|
||||
});
|
||||
|
||||
it('covers boundary logic for nameless recipes, custom meals, default targets and private weights', async () => {
|
||||
// 1. Nameless recipe and recipe without ingredients
|
||||
const rawRecipe = { _id: 'recipeMissingProps' };
|
||||
mockRecipesRepo.findByHousehold.mockResolvedValue({ data: [rawRecipe] });
|
||||
|
||||
// 2. Active target with partial/falsy info
|
||||
mockNutritionRepo.findByUser.mockResolvedValue({ dailyCalories: 0, proteinG: 0 });
|
||||
|
||||
// 3. Last eaten containing a custom meal without recipeId (should continue)
|
||||
mockMealPlanRepo.findByHousehold.mockResolvedValue({
|
||||
data: [
|
||||
{
|
||||
days: [
|
||||
{
|
||||
date: '2026-05-19',
|
||||
meals: [
|
||||
{ customName: 'Snack' }, // no recipeId!
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
mockPantryRepo.findActiveByHousehold.mockResolvedValue([]);
|
||||
|
||||
const suggestions = await service.getSuggestions('hh1', 'user1');
|
||||
|
||||
expect(suggestions).toHaveLength(1);
|
||||
|
||||
// 4. Direct call to getUrgencyWeight default branch
|
||||
const defaultWeight = (service as any).getUrgencyWeight('mystery-status');
|
||||
expect(defaultWeight).toBe(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue