From d5ba8f5c9e26ec60fc8a1b57974dbb8da2b650da Mon Sep 17 00:00:00 2001
From: Lukas Karsch <lk224@hdm-stuttgart.de>
Date: Fri, 28 Apr 2023 16:42:54 +0200
Subject: [PATCH] tests for Recipe.java

---
 pom.xml                                       |   4 +-
 src/main/java/mi/hdm/recipes/Category.java    |   3 +-
 .../mi/hdm/recipes/NutritionCalculator.java   |   5 +
 .../java/mi/hdm/recipes/NutritionTable.java   |  29 ++++-
 src/main/java/mi/hdm/recipes/Recipe.java      |  57 +++++++++-
 .../hdm/recipes/NutritionCalculatorTest.java  |   2 +-
 src/test/java/mi/hdm/recipes/RecipeTest.java  | 106 ++++++++++++++++++
 .../java/mi/hdm/recipes/ValidObjectsPool.java |   8 +-
 8 files changed, 200 insertions(+), 14 deletions(-)
 create mode 100644 src/test/java/mi/hdm/recipes/RecipeTest.java

diff --git a/pom.xml b/pom.xml
index 061e241..32d414b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,7 +54,9 @@
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>3.8.1</version>
                 <configuration>
-                    <release>11</release>
+                    <release>17</release>
+                    <source>17</source>
+                    <target>17</target>
                 </configuration>
             </plugin>
 
diff --git a/src/main/java/mi/hdm/recipes/Category.java b/src/main/java/mi/hdm/recipes/Category.java
index 95b40ff..1932640 100644
--- a/src/main/java/mi/hdm/recipes/Category.java
+++ b/src/main/java/mi/hdm/recipes/Category.java
@@ -30,8 +30,7 @@ public class Category {
 
     @Override
     public boolean equals(Object o){
-        if (o instanceof Category) {
-            Category c = (Category) o;
+        if (o instanceof Category c) {
             return this.name.equals(c.getName()) || this.colourCode == c.getColourCode();
         }
         return false;
diff --git a/src/main/java/mi/hdm/recipes/NutritionCalculator.java b/src/main/java/mi/hdm/recipes/NutritionCalculator.java
index 20ccdec..6233f3b 100644
--- a/src/main/java/mi/hdm/recipes/NutritionCalculator.java
+++ b/src/main/java/mi/hdm/recipes/NutritionCalculator.java
@@ -11,6 +11,10 @@ public class NutritionCalculator {
      * @return the nutrition table for this recipe based on its ingredients. All nutrition values are added up.
      */
     public static NutritionTable calculateNutritionTable(Map<RecipeComponent, Integer> ingredients) {
+        if (ingredients == null || ingredients.isEmpty()) {
+            return NutritionTable.empty();
+        }
+
         double totalCals = 0,
                 totalCarbs = 0,
                 totalFats = 0,
@@ -32,6 +36,7 @@ public class NutritionCalculator {
         return new NutritionTable(totalCals, totalCarbs, totalFats, totalProteins, totalFibers, totalSalt);
     }
 
+    //TODO: implement calcuating nutrition table from meal plan
     public static NutritionTable calculateNutritionTable(MealPlan mealPlan) {
         return null;
     }
diff --git a/src/main/java/mi/hdm/recipes/NutritionTable.java b/src/main/java/mi/hdm/recipes/NutritionTable.java
index 1ff9019..36ea565 100644
--- a/src/main/java/mi/hdm/recipes/NutritionTable.java
+++ b/src/main/java/mi/hdm/recipes/NutritionTable.java
@@ -26,6 +26,17 @@ public class NutritionTable {
         table.put(Nutrition.SALT, salt);
     }
 
+    private NutritionTable() {
+        this.table = new HashMap<>();
+
+        table.put(Nutrition.CALORIES, 0.0);
+        table.put(Nutrition.CARBS, 0.0);
+        table.put(Nutrition.FAT, 0.0);
+        table.put(Nutrition.PROTEINS, 0.0);
+        table.put(Nutrition.FIBERS, 0.0);
+        table.put(Nutrition.SALT, 0.0);
+    }
+
     public Map<Nutrition, Double> getTable() {
         return table;
     }
@@ -34,12 +45,18 @@ public class NutritionTable {
         return this;
     }
 
+    //TODO: use big decimal instead of double to avoid this trouble?
+
+    /**
+     * Overridden equals method to account for rounding errors when calculating nutrition scores.
+     *
+     * @param o Object that is being compared
+     * @return true if the given object is a NutritionTable and the difference between all of the values inside of the nutrition table are within a certain limit delta = 0.001
+     */
     @Override
     public boolean equals(Object o) {
-        if (o instanceof NutritionTable) {
-            NutritionTable n = (NutritionTable) o;
-
-            double delta = 0.03;
+        if (o instanceof NutritionTable n) {
+            double delta = 0.001;
             return n.getTable().get(Nutrition.CALORIES) - table.get(Nutrition.CALORIES) < delta
                     && n.getTable().get(Nutrition.CARBS) - table.get(Nutrition.CARBS) < delta
                     && n.getTable().get(Nutrition.FAT) - table.get(Nutrition.FAT) < delta
@@ -49,4 +66,8 @@ public class NutritionTable {
         }
         return false;
     }
+
+    public static NutritionTable empty() {
+        return new NutritionTable();
+    }
 }
diff --git a/src/main/java/mi/hdm/recipes/Recipe.java b/src/main/java/mi/hdm/recipes/Recipe.java
index bd8eeaa..dce8c0b 100644
--- a/src/main/java/mi/hdm/recipes/Recipe.java
+++ b/src/main/java/mi/hdm/recipes/Recipe.java
@@ -3,11 +3,12 @@ package mi.hdm.recipes;
 import mi.hdm.exceptions.InvalidRecipeException;
 
 import java.time.LocalDateTime;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
 public class Recipe implements RecipeComponent {
-    private Map<RecipeComponent, Integer> ingredients;
+    private final Map<RecipeComponent, Integer> ingredients;
     private String name;
     private String description;
     private List<String> preparation;
@@ -16,22 +17,70 @@ public class Recipe implements RecipeComponent {
     private NutritionTable nutritionTable;
     private final LocalDateTime creationTime;
 
+    /**
+     * This constructor will create a new recipe and calculate the nutrition values for the recipe by its ingredients.
+     *
+     * @param name                Name for this recipe
+     * @param ingredients         Map of ingredients used in this recipe. The value for each component should be the amount that is needed of this ingredient (in its respective unit=
+     * @param description         Description for this recipe
+     * @param preparation         List of preparation steps for this recipe
+     * @param categories          Categories that this recipe belongs to
+     * @param preparationTimeMins Time that it takes to prepare this recipe in minutes
+     */
     public Recipe(
-            Map<RecipeComponent, Integer> ingredients,
             String name,
+            Map<RecipeComponent, Integer> ingredients,
             String description,
             List<String> preparation,
             List<Category> categories,
             Integer preparationTimeMins) {
+
+        //Das ruft den anderen Konstruktor dieser Klasse auf (siehe unten)
+        this(name, ingredients, description, preparation, categories, preparationTimeMins, NutritionCalculator.calculateNutritionTable(ingredients));
+    }
+
+    /**
+     * This constructor will create a new recipe and set the nutrition table provided by the user instead of calculating the nutrition values from the ingredients.
+     *
+     * @param name                Name for this recipe
+     * @param ingredients         Map of ingredients used in this recipe. The value for each component should be the amount that is needed of this ingredient (in its respective unit=
+     * @param description         Description for this recipe
+     * @param preparation         List of preparation steps for this recipe
+     * @param categories          Categories that this recipe belongs to
+     * @param preparationTimeMins Time that it takes to prepare this recipe in minutes
+     * @param nutritionTable      The nutrition table that will be set for this recipe
+     */
+    public Recipe(
+            String name,
+            Map<RecipeComponent, Integer> ingredients,
+            String description,
+            List<String> preparation,
+            List<Category> categories,
+            Integer preparationTimeMins,
+            NutritionTable nutritionTable) {
+
+        if (ingredients == null || ingredients.size() == 0) {
+            throw new InvalidRecipeException("Ingredient list can not be null or empty");
+        }
+        if (name == null || name.isEmpty()) {
+            throw new InvalidRecipeException("Name can not be null or empty");
+        }
+        if (preparation == null || preparation.isEmpty()) {
+            throw new InvalidRecipeException("Preparation can not be null or empty");
+        }
+        if (categories == null) {
+            categories = new ArrayList<>();
+        }
+
         this.ingredients = ingredients;
         this.name = name;
         this.description = description;
         this.preparation = preparation;
         this.categories = categories;
         this.preparationTimeMins = preparationTimeMins;
-        nutritionTable = NutritionCalculator.calculateNutritionTable(ingredients);
+        this.nutritionTable = nutritionTable;
 
-        creationTime = LocalDateTime.now();
+        this.creationTime = LocalDateTime.now();
     }
 
     public void setName(String name) {
diff --git a/src/test/java/mi/hdm/recipes/NutritionCalculatorTest.java b/src/test/java/mi/hdm/recipes/NutritionCalculatorTest.java
index cb5a28a..215a62b 100644
--- a/src/test/java/mi/hdm/recipes/NutritionCalculatorTest.java
+++ b/src/test/java/mi/hdm/recipes/NutritionCalculatorTest.java
@@ -9,7 +9,7 @@ import java.util.List;
 import java.util.Map;
 
 public class NutritionCalculatorTest {
-    List<Ingredient> ingredients = ValidObjectsPool.getValidIngredientList();
+    List<RecipeComponent> ingredients = ValidObjectsPool.getValidIngredientList();
     Map<RecipeComponent, Integer> recipeMap;
 
     @BeforeEach
diff --git a/src/test/java/mi/hdm/recipes/RecipeTest.java b/src/test/java/mi/hdm/recipes/RecipeTest.java
new file mode 100644
index 0000000..4caf909
--- /dev/null
+++ b/src/test/java/mi/hdm/recipes/RecipeTest.java
@@ -0,0 +1,106 @@
+package mi.hdm.recipes;
+
+import mi.hdm.exceptions.InvalidRecipeException;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class RecipeTest {
+    private final static List<RecipeComponent> ingredients = ValidObjectsPool.getValidIngredientList();
+    private final static Map<RecipeComponent, Integer> recipeMap = new HashMap<>();
+
+    @BeforeAll
+    public static void setUpAll() {
+        ingredients.forEach(ingredient -> recipeMap.put(ingredient, 100));
+    }
+
+    @Test
+    @DisplayName("Test invalid name: Recipes with an invalid name (name is null or empty) should not be created")
+    void shouldNotCreateRecipeWithInvalidName() {
+        assertThrows(InvalidRecipeException.class, () -> new Recipe(null, recipeMap, "Description!", List.of("step 1"), null, 100));
+        assertThrows(InvalidRecipeException.class, () -> new Recipe("", recipeMap, "Description!", List.of("step 1"), null, 100));
+    }
+
+    @Test
+    @DisplayName("Test invalid ingredients: Recipes with an invalid ingredient map (map is null or empty) should not be created")
+    void shouldNotCreateRecipeWithInvalidIngredientMap() {
+        assertThrows(InvalidRecipeException.class, () -> new Recipe("Valid name", null, "Description!", List.of("step 1"), null, 100));
+        assertThrows(InvalidRecipeException.class, () -> new Recipe("Valid name", Map.of(), "Description!", List.of("step 1"), null, 100));
+    }
+
+    @Test
+    @DisplayName("Test invalid preparation: Recipes with an invalid preparation list (list is null or empty) should not be created")
+    void shouldNotCreateRecipeWithInvalidPrep() {
+        assertThrows(InvalidRecipeException.class, () -> new Recipe("Valid name", recipeMap, "Description!", null, null, 100));
+        assertThrows(InvalidRecipeException.class, () -> new Recipe("Valid name", recipeMap, "Description!", List.of(), null, 100));
+    }
+
+    @Test
+    void getNutritionTableWhenSetManually() {
+        //given
+        NutritionTable nutritionTable = ValidObjectsPool.getValidNutritionTableOne();
+
+        //when
+        Recipe underTest = new Recipe("Valid name", recipeMap, "Valid description", List.of("Step 1"), null, 20, nutritionTable);
+
+        //then
+        assertEquals(nutritionTable, underTest.getNutritionTable());
+    }
+
+    @Test
+    void getNutritionTableWhenCalculated() {
+        //given
+        NutritionTable nutritionTable = ValidObjectsPool.getValidNutritionTableOne();
+        Ingredient ingredientOne = new Ingredient(Measurement.GRAM, "Zucker", nutritionTable);
+
+        //when
+        Recipe underTest = new Recipe("Valid name", Map.of(ingredientOne, 100), "Valid description", List.of("Step 1"), null, 20);
+
+        //then
+        assertEquals(nutritionTable, underTest.getNutritionTable());
+    }
+
+    @Test
+    void canAddCategory() {
+        //given
+        Category category = new Category("Category", 0xFF0000);
+        NutritionTable nutritionTable = ValidObjectsPool.getValidNutritionTableOne();
+        Ingredient ingredientOne = new Ingredient(Measurement.GRAM, "Zucker", nutritionTable);
+        Recipe underTest = new Recipe("Valid name", Map.of(ingredientOne, 100), "Valid description", List.of("Step 1"), new ArrayList<>(), 20);
+
+        //when
+        underTest.addCategory(category);
+
+        //then
+        assertEquals(
+                List.of(category),
+                underTest.getCategories()
+        );
+    }
+
+    @Test
+    void canAddCategoryWhenNull() {
+        //given
+        Category category = new Category("Category", 0xFF0000);
+        NutritionTable nutritionTable = ValidObjectsPool.getValidNutritionTableOne();
+        Ingredient ingredientOne = new Ingredient(Measurement.GRAM, "Zucker", nutritionTable);
+        Recipe underTest = new Recipe("Valid name", Map.of(ingredientOne, 100), "Valid description", List.of("Step 1"), null, 20);
+
+        //when
+        underTest.addCategory(category);
+
+        //then
+        assertEquals(
+                List.of(category),
+                underTest.getCategories()
+        );
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/mi/hdm/recipes/ValidObjectsPool.java b/src/test/java/mi/hdm/recipes/ValidObjectsPool.java
index bec6bfe..c9a1bd0 100644
--- a/src/test/java/mi/hdm/recipes/ValidObjectsPool.java
+++ b/src/test/java/mi/hdm/recipes/ValidObjectsPool.java
@@ -26,14 +26,18 @@ public class ValidObjectsPool {
         return ingredientTwo;
     }
 
-    public static List<Ingredient> getValidIngredientList() {
+    public static List<RecipeComponent> getValidIngredientList() {
         return List.of(ingredientOne, ingredientTwo);
     }
 
-    public static Category getValidCategory() {
+    public static Category getValidCategoryOne() {
         return categoryOne;
     }
 
+    public static Category getValidCategoryTwo() {
+        return categoryTwo;
+    }
+
     public static List<Category> getValidCategoryList() {
         return List.of(categoryOne, categoryTwo);
     }
-- 
GitLab