From f259d9fd76052dddd03fc406295b1b56494e4b6b Mon Sep 17 00:00:00 2001 From: Daniel Kjellid Date: Fri, 14 Jun 2024 14:29:23 +0200 Subject: [PATCH] Write services tests --- nest/recipes/plans/algorithm.py | 1 + nest/recipes/plans/migrations/0001_initial.py | 4 +- nest/recipes/plans/models.py | 2 +- tests/recipes/test_plans_services.py | 87 +++++++++++++++++-- 4 files changed, 85 insertions(+), 9 deletions(-) diff --git a/nest/recipes/plans/algorithm.py b/nest/recipes/plans/algorithm.py index 2ab58b88..1850e62b 100644 --- a/nest/recipes/plans/algorithm.py +++ b/nest/recipes/plans/algorithm.py @@ -51,6 +51,7 @@ def _get_products_dataframe(self) -> pl.DataFrame: quantity=item.portion_quantity, from_unit=item.portion_quantity_unit, to_unit=product.unit, + piece_weight=product.unit_quantity, ) if converted_quantity is None or product.unit_quantity is None: diff --git a/nest/recipes/plans/migrations/0001_initial.py b/nest/recipes/plans/migrations/0001_initial.py index f189218b..86ccb6de 100644 --- a/nest/recipes/plans/migrations/0001_initial.py +++ b/nest/recipes/plans/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.7 on 2024-06-12 13:23 +# Generated by Django 4.2.7 on 2024-06-14 11:58 from django.db import migrations, models import django.db.models.deletion @@ -21,7 +21,7 @@ class Migration(migrations.Migration): ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created time')), ('updated_at', models.DateTimeField(auto_now=True, verbose_name='modified time')), ('title', models.CharField(max_length=50)), - ('description', models.TextField(blank=True, max_length=100)), + ('description', models.TextField(blank=True, max_length=100, null=True)), ('slug', models.SlugField()), ('from_date', models.DateTimeField(blank=True, null=True)), ('home', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='homes.home')), diff --git a/nest/recipes/plans/models.py b/nest/recipes/plans/models.py index 7603b1e7..44fd0611 100644 --- a/nest/recipes/plans/models.py +++ b/nest/recipes/plans/models.py @@ -15,7 +15,7 @@ class RecipePlan(BaseModel): """ title = models.CharField(max_length=50) - description = models.TextField(max_length=100, blank=True) + description = models.TextField(max_length=100, blank=True, null=True) slug = models.SlugField(max_length=50) from_date = models.DateTimeField(blank=True, null=True) home = models.ForeignKey( diff --git a/tests/recipes/test_plans_services.py b/tests/recipes/test_plans_services.py index 9ef63c9a..3a34bd80 100644 --- a/tests/recipes/test_plans_services.py +++ b/tests/recipes/test_plans_services.py @@ -1,10 +1,85 @@ -def test_service_create_weekly_recipe_plan_for_home(): - assert False +from decimal import Decimal +import pytest +from django.utils import timezone +from nest.recipes.plans.algorithm import PlanDistributor +from nest.recipes.plans.models import RecipePlan, RecipePlanItem +from nest.recipes.plans.services import create_recipe_plan, _create_recipe_plan_items +from tests.factories.records import RecipeDetailRecordFactory -def test_service_create_recipe_plan(immediate_on_commit): - assert False +pytestmark = pytest.mark.django_db -def test_service__create_recipe_plan_items(): - assert False +def test_service_create_recipe_plan( + django_assert_num_queries, immediate_on_commit, mocker +): + """ + Test that the create_recipe_plan runs creates the plan instance and runs + the required methods to create a full plan. + """ + grace_period_weeks = 1 + budget = Decimal("100.00") + num_items = 1 + num_pescatarian = 1 + num_vegetarian = 1 + applicable_recipes = [] + + applicable_recipes_mock = mocker.patch( + "nest.recipes.plans.services.find_recipes_applicable_for_plan", + return_value=applicable_recipes, + ) + create_items_mock = mocker.patch( + "nest.recipes.plans.services._create_recipe_plan_items" + ) + distr_mock = mocker.patch.object(PlanDistributor, "create_plan", return_value=[]) + + initial_count = RecipePlan.objects.count() + + with django_assert_num_queries(3), immediate_on_commit: + create_recipe_plan( + title="Example title", + description=None, + budget=budget, + num_items=num_items, + num_pescatarian=num_pescatarian, + num_vegetarian=num_vegetarian, + grace_period_weeks=grace_period_weeks, + from_date=timezone.now(), + ) + + # Assert that a new plan is created. + assert RecipePlan.objects.count() == initial_count + 1 + + # Assert that mocks are called accordingly. + applicable_recipes_mock.assert_called_once_with( + grace_period_weeks=grace_period_weeks, + ) + distr_mock.assert_called_once() + create_items_mock.assert_called_once() + + +@pytest.mark.recipes( + recipe1={"title": " Recipe 1"}, + recipe2={"title": " Recipe 2"}, + recipe3={"title": " Recipe 3"}, +) +@pytest.mark.recipe_plan(title="My plan") +def test_service__create_recipe_plan_items( + django_assert_num_queries, recipes, recipe_plan +): + """ + Test that the _create_recipe_plan_items creates plan items associated to the correct + recipe plan. + """ + recipe_records = [ + RecipeDetailRecordFactory.build(id=recipe.id) for recipe in recipes.values() + ] + + initial_count = RecipePlanItem.objects.filter(recipe_plan_id=recipe_plan.id).count() + + with django_assert_num_queries(2): + _create_recipe_plan_items(plan_id=recipe_plan.id, recipes=recipe_records) + + assert RecipePlanItem.objects.filter( + recipe_plan_id=recipe_plan.id + ).count() == initial_count + len(recipe_records)