From 646183174a3833db763d9a224ab0ea193ec47a88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oskar=20Rasie=C5=84ski?=
Date: Fri, 21 Nov 2025 19:32:40 +0100
Subject: [PATCH 01/14] Created basic recipe.
---
commons/src/main/java/commons/Recipe.java | 166 ++++++++++++++++++++++
1 file changed, 166 insertions(+)
create mode 100644 commons/src/main/java/commons/Recipe.java
diff --git a/commons/src/main/java/commons/Recipe.java b/commons/src/main/java/commons/Recipe.java
new file mode 100644
index 0000000..951fc85
--- /dev/null
+++ b/commons/src/main/java/commons/Recipe.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2021 Delft University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package commons;
+
+import jakarta.persistence.*;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+// TABLE named recipes
+@Entity
+@Table(name = "recipes")
+public class Recipe {
+
+ // PRIMARY Key, unique id for recipe, generated automatically.
+ @Id
+ @GeneratedValue(strategy = GenerationType.AUTO)
+ @Column(name = "id", nullable = false, unique = true)
+ private Long id;
+
+ // Recipe name
+ @Column(name = "name", nullable = false, unique = true)
+ private String name;
+
+ // Creates another table named recipe_ingredients which stores:
+ // recipe_ingredients(recipe_id -> recipes(id), ingredient).
+ // Example recipe_ingredients table:
+ // | recipe_id | ingredient |
+ // |---------------------|------------|
+ // | 0 (Chocolate Cake) | Egg |
+ // | 0 (Chocolate Cake) | Egg |
+ // | 0 (Chocolate Cake) | Flour |
+ // | 1 (Steak) | 40g salt |
+ // | 1 (Steak) | 40g pepper |
+ // | 1 (Steak) | Meat |
+ // |----------------------------------|
+ @ElementCollection
+ @CollectionTable(name = "recipe_ingredients", joinColumns = @JoinColumn(name = "recipe_id"))
+ @Column(name = "ingredient")
+ // TODO: Replace String with Embeddable Ingredient Class
+ private List ingredients = new ArrayList<>();
+
+ // Creates another table named recipe_preparation which stores:
+ // recipe_preparation(recipe_id -> recipes(id), preparation_step, step_order).
+ // Example recipe_preparation table:
+ // | recipe_id | preparation_step | step_order |
+ // |---------------------|----------------------|------------|
+ // | 0 (Chocolate Cake) | Preheat oven | 1 |
+ // | 0 (Chocolate Cake) | Mix eggs and sugar | 2 |
+ // | 0 (Chocolate Cake) | Add flour gradually | 3 |
+ // | 1 (Steak) | Season meat | 1 |
+ // | 1 (Steak) | Heat pan | 2 |
+ // |---------------------------------------------------------|
+ @ElementCollection
+ @CollectionTable(name = "recipe_preparation", joinColumns = @JoinColumn(name = "recipe_id"))
+ @Column(name = "preparation_step")
+ @OrderColumn(name = "step_order")
+ private List preparationSteps = new ArrayList<>();
+
+ @SuppressWarnings("unused")
+ private Recipe() {
+ // for object mapper
+ }
+
+ public Recipe(Long id, String name) {
+ // Not used by JPA/Spring
+ this.id = id;
+ this.name = name;
+ }
+
+ // TODO: Replace String with Embeddable Ingredient Class for ingredients
+ public Recipe(Long id, String name, List ingredients, List preparationSteps) {
+ // Not used by JPA/Spring
+ this.id = id;
+ this.name = name;
+ this.ingredients = ingredients;
+ this.preparationSteps = preparationSteps;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ // TODO: Replace String with Embeddable Ingredient Class
+ public List getIngredients() {
+ // Disallow modifying the returned list.
+ // You can still copy it with List.copyOf(...)
+ return Collections.unmodifiableList(ingredients);
+ }
+
+ // TODO: Replace String with Embeddable Ingredient Class
+ public void setIngredients(List ingredients) {
+ this.ingredients = ingredients;
+ }
+
+ public List getPreparationSteps() {
+ // Disallow modifying the returned list.
+ // You can still copy it with List.copyOf(...)
+ return Collections.unmodifiableList(preparationSteps);
+ }
+
+ public void setPreparationSteps(List preparationSteps) {
+ this.preparationSteps = preparationSteps;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ // Faster/Better equals than reflectionEquals
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Recipe recipe = (Recipe) o;
+ return Objects.equals(id, recipe.id); // Check only for ID, as its unique!
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id); // Use ID only, as its unique.
+ }
+
+ @Override
+ public String toString() {
+ return "Recipe{" +
+ "name='" + name + '\'' +
+ ", ingredientsCount=" + ingredients.size() +
+ ", preparationStepsCount=" + preparationSteps.size() +
+ "}";
+ }
+
+ @SuppressWarnings("unused")
+ public String toDetailedString() {
+ // More detailed toString for debugging.
+ return "Recipe{" +
+ "name='" + name + '\'' +
+ ", ingredients=" + ingredients +
+ ", preparationSteps=" + preparationSteps +
+ '}';
+ }
+
+}
\ No newline at end of file
From ccdccffee8e74521b7c206778db25eb24718ddda Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oskar=20Rasie=C5=84ski?=
Date: Fri, 21 Nov 2025 19:35:03 +0100
Subject: [PATCH 02/14] Added RecipeRepository
---
.../server/database/RecipeRepository.java | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 server/src/main/java/server/database/RecipeRepository.java
diff --git a/server/src/main/java/server/database/RecipeRepository.java b/server/src/main/java/server/database/RecipeRepository.java
new file mode 100644
index 0000000..60ec130
--- /dev/null
+++ b/server/src/main/java/server/database/RecipeRepository.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021 Delft University of Technology
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package server.database;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import commons.Recipe;
+
+public interface RecipeRepository extends JpaRepository {}
\ No newline at end of file
From 110c8e72d4089447a581140096e61c6b194d0306 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oskar=20Rasie=C5=84ski?=
Date: Fri, 21 Nov 2025 19:38:48 +0100
Subject: [PATCH 03/14] Removed * import
---
commons/src/main/java/commons/Recipe.java | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/commons/src/main/java/commons/Recipe.java b/commons/src/main/java/commons/Recipe.java
index 951fc85..0a8cfaa 100644
--- a/commons/src/main/java/commons/Recipe.java
+++ b/commons/src/main/java/commons/Recipe.java
@@ -15,7 +15,17 @@
*/
package commons;
-import jakarta.persistence.*;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.Table;
+import jakarta.persistence.Entity;
+import jakarta.persistence.CollectionTable;
+import jakarta.persistence.Column;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.OrderColumn;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.ElementCollection;
+
import java.util.ArrayList;
import java.util.Collections;
From 873f1911ac9b6037847424b55cd3090a99600459 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oskar=20Rasie=C5=84ski?=
Date: Fri, 21 Nov 2025 20:17:40 +0100
Subject: [PATCH 04/14] Added tests for Recipe.
---
commons/src/test/java/commons/RecipeTest.java | 128 ++++++++++++++++++
1 file changed, 128 insertions(+)
create mode 100644 commons/src/test/java/commons/RecipeTest.java
diff --git a/commons/src/test/java/commons/RecipeTest.java b/commons/src/test/java/commons/RecipeTest.java
new file mode 100644
index 0000000..e53ac58
--- /dev/null
+++ b/commons/src/test/java/commons/RecipeTest.java
@@ -0,0 +1,128 @@
+package commons;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class RecipeTest {
+
+ Recipe recipe;
+
+ @BeforeEach
+ void setupRecipe() {
+ this.recipe = new Recipe(1L, "Chocolate Cake");
+ }
+
+ @Test
+ void getId() {
+ assertEquals(1L, recipe.getId());
+ }
+
+ @Test
+ void setId() {
+ recipe.setId(2048L);
+ assertEquals(2048L, recipe.getId());
+ }
+
+ @Test
+ void getName() {
+ assertEquals("Chocolate Cake", recipe.getName());
+ }
+
+ @Test
+ void setName() {
+ recipe.setName("Steak");
+ assertEquals("Steak", recipe.getName());
+ }
+
+ @Test
+ void getIngredientsAddThrow() {
+ // TODO: Change to actual Ingredient class later
+ assertThrows(UnsupportedOperationException.class, () -> {
+ recipe.getIngredients().add("Lasagna");
+ });
+ }
+
+ @Test
+ void getIngredientsClearThrow() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ recipe.getIngredients().clear();
+ });
+ }
+
+ @Test
+ void setIngredients() {
+ // TODO: Change to actual Ingredient class later
+ List ingredients = new ArrayList<>(List.of("Chocolate", "Flour", "Egg"));
+ recipe.setIngredients(ingredients);
+
+ assertEquals(recipe.getIngredients(), ingredients);
+ assertEquals(recipe.getIngredients().size(), 3);
+ }
+
+ @Test
+ void getPreparationStepsAddThrow() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ recipe.getPreparationSteps().add("Preheat Oven");
+ });
+ }
+
+ @Test
+ void getPreparationStepsClearThrow() {
+ assertThrows(UnsupportedOperationException.class, () -> {
+ recipe.getPreparationSteps().clear();
+ });
+ }
+
+ @Test
+ void setPreparationSteps() {
+ List steps = new ArrayList<>(List.of("Preheat oven", "Mix stuff", "decorate"));
+ recipe.setPreparationSteps(steps);
+
+ assertEquals(recipe.getPreparationSteps(), steps);
+ assertEquals(recipe.getPreparationSteps().size(), 3);
+ }
+
+ @Test
+ void testEqualsSame() {
+ Recipe recipe2 = new Recipe(1L, "Chocolate Cake");
+ assertEquals(recipe, recipe2);
+ }
+
+ @Test
+ void testEqualsSameExceptId() {
+ Recipe recipe2 = new Recipe(2L, "Chocolate Cake");
+ assertNotEquals(recipe, recipe2);
+ }
+
+ @Test
+ void testEqualsOnlySameId() {
+ Recipe recipe2 = new Recipe(1L, "Some random recipe");
+ assertEquals(recipe, recipe2); // Equals, we only look at ID!!!
+ }
+
+ @Test
+ void testHashCodeSame() {
+ Recipe recipe2 = new Recipe(1L, "Chocolate Cake");
+
+ assertEquals(recipe.hashCode(), recipe2.hashCode());
+ }
+
+ @Test
+ void testHashCodeSameExceptId() {
+ Recipe recipe2 = new Recipe(2L, "Chocolate Cake");
+
+ assertNotEquals(recipe.hashCode(), recipe2.hashCode());
+ }
+
+ @Test
+ void testHashCodeOnlySameId() {
+ Recipe recipe2 = new Recipe(1L, "Some random recipe");
+
+ assertEquals(recipe.hashCode(), recipe2.hashCode()); // Same, only looks at ID!!!
+ }
+}
\ No newline at end of file
From 1273ae47fe37eb8e646b06dc7199805088647222 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oskar=20Rasie=C5=84ski?=
Date: Fri, 21 Nov 2025 20:18:31 +0100
Subject: [PATCH 05/14] Expression lambdas
---
commons/src/test/java/commons/RecipeTest.java | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/commons/src/test/java/commons/RecipeTest.java b/commons/src/test/java/commons/RecipeTest.java
index e53ac58..97afd8b 100644
--- a/commons/src/test/java/commons/RecipeTest.java
+++ b/commons/src/test/java/commons/RecipeTest.java
@@ -42,16 +42,12 @@ class RecipeTest {
@Test
void getIngredientsAddThrow() {
// TODO: Change to actual Ingredient class later
- assertThrows(UnsupportedOperationException.class, () -> {
- recipe.getIngredients().add("Lasagna");
- });
+ assertThrows(UnsupportedOperationException.class, () -> recipe.getIngredients().add("Lasagna"));
}
@Test
void getIngredientsClearThrow() {
- assertThrows(UnsupportedOperationException.class, () -> {
- recipe.getIngredients().clear();
- });
+ assertThrows(UnsupportedOperationException.class, () -> recipe.getIngredients().clear());
}
@Test
@@ -66,16 +62,12 @@ class RecipeTest {
@Test
void getPreparationStepsAddThrow() {
- assertThrows(UnsupportedOperationException.class, () -> {
- recipe.getPreparationSteps().add("Preheat Oven");
- });
+ assertThrows(UnsupportedOperationException.class, () -> recipe.getPreparationSteps().add("Preheat Oven"));
}
@Test
void getPreparationStepsClearThrow() {
- assertThrows(UnsupportedOperationException.class, () -> {
- recipe.getPreparationSteps().clear();
- });
+ assertThrows(UnsupportedOperationException.class, () -> recipe.getPreparationSteps().clear());
}
@Test
From a4ec14bfb0b1cb9e208657e506445ac2dd283d42 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oskar=20Rasie=C5=84ski?=
Date: Fri, 21 Nov 2025 20:21:11 +0100
Subject: [PATCH 06/14] Removed magic numbers from tests
---
commons/src/test/java/commons/RecipeTest.java | 25 ++++++++++---------
1 file changed, 13 insertions(+), 12 deletions(-)
diff --git a/commons/src/test/java/commons/RecipeTest.java b/commons/src/test/java/commons/RecipeTest.java
index 97afd8b..138b79c 100644
--- a/commons/src/test/java/commons/RecipeTest.java
+++ b/commons/src/test/java/commons/RecipeTest.java
@@ -11,21 +11,22 @@ import static org.junit.jupiter.api.Assertions.*;
class RecipeTest {
Recipe recipe;
+ final Long RECIPE_ID = 1L;
@BeforeEach
void setupRecipe() {
- this.recipe = new Recipe(1L, "Chocolate Cake");
+ this.recipe = new Recipe(RECIPE_ID, "Chocolate Cake");
}
@Test
void getId() {
- assertEquals(1L, recipe.getId());
+ assertEquals(RECIPE_ID, recipe.getId());
}
@Test
void setId() {
- recipe.setId(2048L);
- assertEquals(2048L, recipe.getId());
+ recipe.setId(RECIPE_ID + 1);
+ assertEquals(RECIPE_ID + 1, recipe.getId());
}
@Test
@@ -57,7 +58,7 @@ class RecipeTest {
recipe.setIngredients(ingredients);
assertEquals(recipe.getIngredients(), ingredients);
- assertEquals(recipe.getIngredients().size(), 3);
+ assertEquals(recipe.getIngredients().size(), ingredients.size());
}
@Test
@@ -76,44 +77,44 @@ class RecipeTest {
recipe.setPreparationSteps(steps);
assertEquals(recipe.getPreparationSteps(), steps);
- assertEquals(recipe.getPreparationSteps().size(), 3);
+ assertEquals(recipe.getPreparationSteps().size(), steps.size());
}
@Test
void testEqualsSame() {
- Recipe recipe2 = new Recipe(1L, "Chocolate Cake");
+ Recipe recipe2 = new Recipe(RECIPE_ID, "Chocolate Cake");
assertEquals(recipe, recipe2);
}
@Test
void testEqualsSameExceptId() {
- Recipe recipe2 = new Recipe(2L, "Chocolate Cake");
+ Recipe recipe2 = new Recipe(RECIPE_ID + 1, "Chocolate Cake");
assertNotEquals(recipe, recipe2);
}
@Test
void testEqualsOnlySameId() {
- Recipe recipe2 = new Recipe(1L, "Some random recipe");
+ Recipe recipe2 = new Recipe(RECIPE_ID, "Some random recipe");
assertEquals(recipe, recipe2); // Equals, we only look at ID!!!
}
@Test
void testHashCodeSame() {
- Recipe recipe2 = new Recipe(1L, "Chocolate Cake");
+ Recipe recipe2 = new Recipe(RECIPE_ID, "Chocolate Cake");
assertEquals(recipe.hashCode(), recipe2.hashCode());
}
@Test
void testHashCodeSameExceptId() {
- Recipe recipe2 = new Recipe(2L, "Chocolate Cake");
+ Recipe recipe2 = new Recipe(RECIPE_ID + 1, "Chocolate Cake");
assertNotEquals(recipe.hashCode(), recipe2.hashCode());
}
@Test
void testHashCodeOnlySameId() {
- Recipe recipe2 = new Recipe(1L, "Some random recipe");
+ Recipe recipe2 = new Recipe(RECIPE_ID, "Some random recipe");
assertEquals(recipe.hashCode(), recipe2.hashCode()); // Same, only looks at ID!!!
}
From 2d9af65f2872ec49692de0743224b6465fa4e405 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Oskar=20Rasie=C5=84ski?=
Date: Fri, 21 Nov 2025 20:25:12 +0100
Subject: [PATCH 07/14] fixed checkstyle issue?
---
commons/src/test/java/commons/RecipeTest.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/commons/src/test/java/commons/RecipeTest.java b/commons/src/test/java/commons/RecipeTest.java
index 138b79c..db70bf2 100644
--- a/commons/src/test/java/commons/RecipeTest.java
+++ b/commons/src/test/java/commons/RecipeTest.java
@@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.*;
class RecipeTest {
Recipe recipe;
- final Long RECIPE_ID = 1L;
+ static final Long RECIPE_ID = 1L;
@BeforeEach
void setupRecipe() {
From 4e23d913d14d144aca0b92387b666c9448d8111d Mon Sep 17 00:00:00 2001
From: Steven Liu
Date: Sat, 22 Nov 2025 00:47:43 +0100
Subject: [PATCH 08/14] feat: RecipeController with GET, POST, PUT, DELETE
TODO: WebSockets updates should be propagated after POST/PUT/DELETE operations
---
.../java/server/api/RecipeController.java | 121 ++++++++++++++++++
1 file changed, 121 insertions(+)
create mode 100644 server/src/main/java/server/api/RecipeController.java
diff --git a/server/src/main/java/server/api/RecipeController.java b/server/src/main/java/server/api/RecipeController.java
new file mode 100644
index 0000000..4a81057
--- /dev/null
+++ b/server/src/main/java/server/api/RecipeController.java
@@ -0,0 +1,121 @@
+package server.api;
+
+import commons.Recipe;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.http.ResponseEntity;
+
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import server.database.RecipeRepository;
+
+import java.util.List;
+import java.util.Optional;
+
+@RestController
+@RequestMapping("/recipe")
+public class RecipeController {
+ private final RecipeRepository recipeRepository; // JPA repository used in this controller
+
+ public RecipeController(RecipeRepository recipeRepository) {
+ this.recipeRepository = recipeRepository;
+ }
+
+ /**
+ * Mapping for GET /recipe/{id}
+ *
+ * Gets a specific recipe by its unique id.
+ *
+ * @param id id of the recipe
+ * @return The recipe if it exists in the repository; otherwise returns 404 Not Found status
+ */
+ @GetMapping("/recipe/{id}")
+ public ResponseEntity getRecipe(@PathVariable Long id) {
+ if (!recipeRepository.existsById(id)) {
+ return ResponseEntity.notFound().build();
+ }
+ return ResponseEntity.ok(recipeRepository.findById(id).get());
+ }
+
+ /**
+ * Mapping for GET /recipes(?limit=)
+ *
+ * If the limit parameter is unspecified, return all recipes in the repository.
+ * @param limit Integer limit of items you want to get
+ * @return The list of recipes
+ */
+ @GetMapping("/recipes")
+ public ResponseEntity> getRecipes(@RequestParam Optional limit) {
+ if (limit.isPresent()) {
+ return ResponseEntity.ok(
+ recipeRepository.findAll(
+ PageRequest.of(0, limit.get())
+ ).toList());
+ }
+ return ResponseEntity.ok(recipeRepository.findAll());
+ }
+
+ /**
+ * Mapping for POST /recipe/{id}
+ * @param id The recipe id to replace
+ * @param recipe The new recipe to be replaced from the original
+ * @return The changed recipe; returns 400 Bad Request if the recipe does not exist
+ */
+ @PostMapping("/recipe/{id}")
+ public ResponseEntity updateRecipe(@PathVariable Long id, @RequestBody Recipe recipe) {
+ if (!recipeRepository.existsById(id)) {
+ return ResponseEntity.badRequest().build();
+ }
+
+ // TODO: Send WS update to all subscribers with the updated recipe
+
+ return ResponseEntity.ok(recipeRepository.save(recipe));
+ }
+
+ /**
+ * Mapping for PUT /recipe/new
+ *
+ * Inserts a new recipe into the repository
+ *
+ * @param recipe The new recipe as a request body
+ * @return 200 OK with the recipe you added; or 400 Bad Request if the recipe already exists
+ */
+ @PutMapping("/recipe/new")
+ public ResponseEntity createRecipe(@RequestBody Recipe recipe) {
+ if (recipeRepository.existsById(recipe.getId())) {
+ return ResponseEntity.badRequest().build();
+ }
+
+ // TODO: Send WS update to all subscribers with the new recipe
+
+ return ResponseEntity.ok(recipeRepository.save(recipe));
+ }
+
+ /**
+ * Mapping for DELETE /recipe/{id}
+ *
+ * Deletes a recipe identified by its id.
+ *
+ * @param id The id of the recipe to be deleted.
+ * @return 200 OK with the recipe that was deleted; or 400 Bad Request if the recipe doesn't exist.
+ */
+ @DeleteMapping("/recipe/{id}")
+ public ResponseEntity deleteRecipe(@PathVariable Long id) {
+ if (!recipeRepository.existsById(id)) {
+ return ResponseEntity.badRequest().build();
+ }
+ Recipe removedRecipe = recipeRepository.findById(id).get();
+ recipeRepository.delete(removedRecipe);
+
+ // TODO: Send WS update to propagate deletion
+ return ResponseEntity.ok(removedRecipe);
+ }
+}
From 0a9251df67729eb4f9c0b76f2ce2db93bf77b0c9 Mon Sep 17 00:00:00 2001
From: Steven Liu
Date: Sat, 22 Nov 2025 01:14:29 +0100
Subject: [PATCH 09/14] fix: recipe api now under /api
---
server/src/main/java/server/api/RecipeController.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/server/src/main/java/server/api/RecipeController.java b/server/src/main/java/server/api/RecipeController.java
index 4a81057..09fbe56 100644
--- a/server/src/main/java/server/api/RecipeController.java
+++ b/server/src/main/java/server/api/RecipeController.java
@@ -21,7 +21,7 @@ import java.util.List;
import java.util.Optional;
@RestController
-@RequestMapping("/recipe")
+@RequestMapping("/api")
public class RecipeController {
private final RecipeRepository recipeRepository; // JPA repository used in this controller
From f62e836692c1cb8282ebf0c906ef5da341eadb43 Mon Sep 17 00:00:00 2001
From: Steven Liu
Date: Sat, 22 Nov 2025 01:16:38 +0100
Subject: [PATCH 10/14] fix: createRecipe checks name is unique
Uses JPA Repository.exists(Example); Changed no-arg constuctor of
Recipe to public.
---
commons/src/main/java/commons/Recipe.java | 2 +-
.../src/main/java/server/api/RecipeController.java | 14 ++++++++++++--
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/commons/src/main/java/commons/Recipe.java b/commons/src/main/java/commons/Recipe.java
index 0a8cfaa..6ee2497 100644
--- a/commons/src/main/java/commons/Recipe.java
+++ b/commons/src/main/java/commons/Recipe.java
@@ -83,7 +83,7 @@ public class Recipe {
private List preparationSteps = new ArrayList<>();
@SuppressWarnings("unused")
- private Recipe() {
+ public Recipe() {
// for object mapper
}
diff --git a/server/src/main/java/server/api/RecipeController.java b/server/src/main/java/server/api/RecipeController.java
index 09fbe56..9b1753e 100644
--- a/server/src/main/java/server/api/RecipeController.java
+++ b/server/src/main/java/server/api/RecipeController.java
@@ -2,6 +2,7 @@ package server.api;
import commons.Recipe;
+import org.springframework.data.domain.Example;
import org.springframework.data.domain.PageRequest;
import org.springframework.http.ResponseEntity;
@@ -86,11 +87,20 @@ public class RecipeController {
* Inserts a new recipe into the repository
*
* @param recipe The new recipe as a request body
- * @return 200 OK with the recipe you added; or 400 Bad Request if the recipe already exists
+ * @return 200 OK with the recipe you added; or 400 Bad Request if the recipe already exists by name
*/
@PutMapping("/recipe/new")
public ResponseEntity createRecipe(@RequestBody Recipe recipe) {
- if (recipeRepository.existsById(recipe.getId())) {
+
+ // We initialize a new example recipe with the name of input recipe
+ // This is the only attribute we are concerned about making sure it's unique
+ Recipe example = new Recipe();
+ example.setName(recipe.getName());
+
+ /* Here we use very funny JPA magic repository.exists(Example)
+ We check if any recipe in the repository has the same name as the input
+ */
+ if (recipeRepository.exists(Example.of(example))) {
return ResponseEntity.badRequest().build();
}
From 17f4b8bba4143b845d7bd37c5a4c4a8706335a16 Mon Sep 17 00:00:00 2001
From: Steven Liu
Date: Sat, 22 Nov 2025 01:24:04 +0100
Subject: [PATCH 11/14] fix: correct deletion method implementation
reference: https://stackoverflow.com/questions/75912878/could-not-write-json-failed-to-lazily-initialize-a-collection-when-returning-an
---
server/src/main/java/server/api/RecipeController.java | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/server/src/main/java/server/api/RecipeController.java b/server/src/main/java/server/api/RecipeController.java
index 9b1753e..a1d79fe 100644
--- a/server/src/main/java/server/api/RecipeController.java
+++ b/server/src/main/java/server/api/RecipeController.java
@@ -115,17 +115,16 @@ public class RecipeController {
* Deletes a recipe identified by its id.
*
* @param id The id of the recipe to be deleted.
- * @return 200 OK with the recipe that was deleted; or 400 Bad Request if the recipe doesn't exist.
+ * @return 200 OK with true; or 400 Bad Request if the recipe doesn't exist.
*/
@DeleteMapping("/recipe/{id}")
- public ResponseEntity deleteRecipe(@PathVariable Long id) {
+ public ResponseEntity deleteRecipe(@PathVariable Long id) {
if (!recipeRepository.existsById(id)) {
return ResponseEntity.badRequest().build();
}
- Recipe removedRecipe = recipeRepository.findById(id).get();
- recipeRepository.delete(removedRecipe);
+ recipeRepository.deleteById(id);
// TODO: Send WS update to propagate deletion
- return ResponseEntity.ok(removedRecipe);
+ return ResponseEntity.ok(true);
}
}
From a1fbea1585c2ee2f4eb55829c32f5f13bf9c323b Mon Sep 17 00:00:00 2001
From: Steven Liu
Date: Sat, 22 Nov 2025 02:35:00 +0100
Subject: [PATCH 12/14] test: add unit tests for RecipeController
Covers 6/6 methods in controller
Also added test configuration in application-mock-data-test.properties
---
.../application-mock-data-test.properties | 18 +++
.../java/server/api/RecipeControllerTest.java | 137 ++++++++++++++++++
2 files changed, 155 insertions(+)
create mode 100644 server/src/main/resources/application-mock-data-test.properties
create mode 100644 server/src/test/java/server/api/RecipeControllerTest.java
diff --git a/server/src/main/resources/application-mock-data-test.properties b/server/src/main/resources/application-mock-data-test.properties
new file mode 100644
index 0000000..c68d6fa
--- /dev/null
+++ b/server/src/main/resources/application-mock-data-test.properties
@@ -0,0 +1,18 @@
+spring.datasource.driverClassName=org.h2.Driver
+spring.datasource.username=sa
+spring.datasource.password=
+spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
+
+# use one of these alternatives...
+# ... purely in-memory, wiped on restart, but great for testing
+spring.datasource.url=jdbc:h2:mem:testdb
+# ... persisted on disk (in project directory)
+#spring.datasource.url=jdbc:h2:file:./h2-database
+
+# enable DB view on http://localhost:8080/h2-console
+spring.h2.console.enabled=true
+
+# strategy for table (re-)generation
+spring.jpa.hibernate.ddl-auto=update
+# show auto-generated SQL commands
+#spring.jpa.hibernate.show_sql=true
diff --git a/server/src/test/java/server/api/RecipeControllerTest.java b/server/src/test/java/server/api/RecipeControllerTest.java
new file mode 100644
index 0000000..5931b3a
--- /dev/null
+++ b/server/src/test/java/server/api/RecipeControllerTest.java
@@ -0,0 +1,137 @@
+package server.api;
+
+
+import commons.Recipe;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
+import org.springframework.http.HttpStatus;
+import org.springframework.test.context.ActiveProfiles;
+import server.database.RecipeRepository;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.LongStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+// Spring Boot unit testing magic
+// Before each test the state of the repository is reset by
+// rolling back transaction of changes from previous test
+@DataJpaTest
+
+// This test sources its application profile from
+// resources/application-mock-data-test.properties
+// This config uses an in-memory database
+@ActiveProfiles("mock-data-test")
+public class RecipeControllerTest {
+ private RecipeController controller;
+ private List recipes;
+ private RecipeRepository recipeRepository;
+ public static final int NUM_RECIPES = 10;
+
+ // Injects a test repository into the test class
+ @Autowired
+ public RecipeControllerTest(RecipeRepository recipeRepository) {
+ this.recipeRepository = recipeRepository;
+ }
+
+ @BeforeEach
+ public void setup() {
+ recipes = LongStream
+ .range(0, NUM_RECIPES)
+ .mapToObj(x -> new Recipe(null, "Recipe " + x, List.of(), List.of()))
+ .toList();
+ controller = new RecipeController(recipeRepository);
+ }
+ @Test
+ public void createOneRecipe() {
+ controller.createRecipe(recipes.getFirst());
+
+ // There is 1 recipe in the repository
+ assertEquals(1, recipeRepository.count());
+ }
+ @Test
+ public void createManyRecipes() {
+ recipes.forEach(recipe -> controller.createRecipe(recipe));
+
+ // There are the same number of recipes in the repository as the input list
+ assertEquals(recipes.size(), recipeRepository.count());
+ }
+ @Test
+ public void getManyRecipes() {
+ recipes.forEach(recipe -> controller.createRecipe(recipe));
+
+ // The number of recipes returned is the same as the entire input list
+ assertEquals(recipes.size(), controller.getRecipes(Optional.empty()).getBody().size());
+ }
+ @Test
+ public void getSomeRecipes() {
+ final int LIMIT = 5;
+ recipes.forEach(recipe -> controller.createRecipe(recipe));
+ // The number of recipes returned is the same as the entire input list
+ assertEquals(LIMIT, controller.getRecipes(Optional.of(LIMIT)).getBody().size());
+ }
+ @Test
+ public void findOneRecipeExists() {
+ final int CHECK_INDEX = 3;
+ List ids = LongStream
+ .range(0, NUM_RECIPES)
+ .map(recipe -> controller.createRecipe(recipes.get((int) recipe)).getBody().getId())
+ .boxed().toList();
+
+ // The third item in the input list is the same as the third item retrieved from the database
+ assertEquals(
+ recipes.get(CHECK_INDEX),
+ controller.getRecipe(ids.get(CHECK_INDEX)).getBody());
+ }
+ @Test
+ public void findOneRecipeNotExists() {
+ final int CHECK_INDEX = 3;
+ // There does not exist a recipe with ID=3 since there are no items in the repository.
+ assertEquals(
+ HttpStatus.NOT_FOUND,
+ controller.getRecipe((long) CHECK_INDEX).getStatusCode());
+ }
+ @Test
+ public void deleteOneRecipeGood() {
+ final int DELETE_INDEX = 5;
+ List ids = LongStream
+ .range(0, NUM_RECIPES)
+ .map(recipe -> controller.createRecipe(recipes.get((int) recipe)).getBody().getId())
+ .boxed().toList();
+
+ // The object has been successfully deleted
+ assertEquals(HttpStatus.OK, controller.deleteRecipe(ids.get(DELETE_INDEX)).getStatusCode());
+ }
+ @Test
+ public void deleteOneRecipeCountGood() {
+ final int DELETE_INDEX = 5;
+ List ids = LongStream
+ .range(0, NUM_RECIPES)
+ .map(recipe -> controller.createRecipe(recipes.get((int) recipe)).getBody().getId())
+ .boxed().toList();
+ controller.deleteRecipe(ids.get(DELETE_INDEX));
+ // The count of items decreased by 1 after the 5th item has been removed.
+ assertEquals(ids.size() - 1, recipeRepository.count());
+ }
+ @Test
+ public void deleteOneRecipeFail() {
+ final Long DELETE_INDEX = 5L;
+ assertEquals(HttpStatus.BAD_REQUEST, controller.deleteRecipe(DELETE_INDEX).getStatusCode());
+ }
+
+ @Test
+ public void updateOneRecipeHasNewData() {
+ final int UPDATE_INDEX = 5;
+ List ids = LongStream
+ .range(0, NUM_RECIPES)
+ .map(recipe -> controller.createRecipe(recipes.get((int) recipe)).getBody().getId())
+ .boxed().toList();
+ Recipe newRecipe = controller.getRecipe(ids.get(UPDATE_INDEX)).getBody();
+ newRecipe.setName("New recipe");
+ controller.updateRecipe(newRecipe.getId(), newRecipe);
+ assertEquals("New recipe", recipeRepository.getReferenceById(ids.get(UPDATE_INDEX)).getName());
+ }
+}
From fff5f2fffa575c97a4b3380dc13bc34d0d47e514 Mon Sep 17 00:00:00 2001
From: Steven Liu
Date: Sat, 22 Nov 2025 12:19:00 +0100
Subject: [PATCH 13/14] docs: api definition
---
docs/recipe-api.md | 243 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 243 insertions(+)
create mode 100644 docs/recipe-api.md
diff --git a/docs/recipe-api.md b/docs/recipe-api.md
new file mode 100644
index 0000000..e2b9b4f
--- /dev/null
+++ b/docs/recipe-api.md
@@ -0,0 +1,243 @@
+## Recipe API
+
+Base path: `/api`
+
+This API allows clients to create, read, update, and delete `Recipe` resources.
+
+---
+
+## Endpoints
+
+### Get a Recipe by ID
+
+**GET** `/api/recipe/{id}`
+
+Retrieves a specific recipe by its unique identifier.
+
+* **Path Parameters**
+
+ | Name | Type | Required | Description |
+ | ---- | ------ | -------- | ------------------------------ |
+ | `id` | `Long` | Yes | The ID of the recipe to fetch. |
+
+* **Responses**
+
+ | Status Code | Description |
+ | --------------- | ------------------------------------- |
+ | `200 OK` | Returns the recipe with the given ID. |
+ | `404 Not Found` | No recipe exists with that ID. |
+
+* **Example Request**
+
+ ```bash
+ curl -X GET "http://SERVER_ADDRESS/api/recipe/1" \
+ -H "Accept: application/json"
+ ```
+
+* **Example Response (200)**
+
+ ```json
+ {
+ "id": 1,
+ "name": "Pancakes",
+ "ingredients": ["Ingredient 1", "Ingredient 2"],
+ "preparationSteps": ["Step 1", "Step 2"]
+ }
+ ```
+
+---
+
+### List Recipes
+
+**GET** `/api/recipes`
+
+Retrieves a list of recipes. Supports optional pagination via a `limit` query parameter.
+
+* **Query Parameters**
+
+ | Name | Type | Required | Description |
+ | ------- | --------- | -------- | ---------------------------------------------------------------------------- |
+ | `limit` | `Integer` | No | Maximum number of recipes to return. If not provided, returns *all* recipes. |
+
+* **Responses**
+
+ | Status Code | Description |
+ | ----------- | --------------------------------------------- |
+ | `200 OK` | Returns a list of recipes (possibly limited). |
+
+* **Example Request (no limit)**
+
+ ```bash
+ curl -X GET "http://SERVER_ADDRESS/api/recipes" \
+ -H "Accept: application/json"
+ ```
+
+* **Example Request (with limit)**
+
+ ```bash
+ curl -X GET "http://SERVER_ADDRESS/api/recipes?limit=10" \
+ -H "Accept: application/json"
+ ```
+
+* **Example Response (200)**
+
+ ```json
+ [
+ {
+ "id": 10,
+ "name": "Recipe 0",
+ "ingredients": ["Flour", "Milk"],
+ "preparationSteps": ["Do something", "Do something else"]
+ },
+ {
+ "id": 11,
+ "name": "Recipe 1",
+ "ingredients": ["Flour", "Milk"],
+ "preparationSteps": ["Do something", "Do something else"]
+ }
+ // etc. max {limit} items
+ ]
+ ```
+
+---
+
+### Create a New Recipe
+
+**PUT** `/api/recipe/new`
+
+Creates a new recipe. The recipe name must be unique in the repository.
+
+* **Request Body**
+
+ A JSON object representing the recipe. Example:
+
+ ```json
+ {
+ "name": "Pancakes",
+ "ingredients": ["Flour", "Milk", "Eggs"],
+ "preparationSteps": ["Step 1", "Step 2"]
+ }
+ ```
+
+* **Responses**
+
+ | Status Code | Description |
+ | ----------------- | -------------------------------------------------------------------------------------------- |
+ | `200 OK` | The recipe was successfully created. Returns the created recipe (including its assigned ID). |
+ | `400 Bad Request` | A recipe with the same name already exists. |
+
+* **Example Request**
+
+ ```bash
+ curl -X PUT "https://SERVER_ADDRESS/api/recipe/new" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "name": "Pancakes",
+ "ingredients": ["Flour", "Milk", "Eggs"],
+ "preparationSteps": ["Step 1", "Step 2"]
+ }'
+ ```
+
+* **Example Response (200)**
+
+ ```json
+ {
+ "id": 125,
+ "name": "Pancakes",
+ "ingredients": ["Flour", "Milk", "Eggs"],
+ "preparationSteps": ["Step 1", "Step 2"]
+ }
+ ```
+
+---
+
+### Update an Existing Recipe
+
+**POST** `/api/recipe/{id}`
+
+Replaces or updates the recipe with the given ID.
+
+* **Path Parameters**
+
+ | Name | Type | Required | Description |
+ | ---- | ------ | -------- | ------------------------------- |
+ | `id` | `Long` | Yes | The ID of the recipe to update. |
+
+* **Request Body**
+
+ A JSON object containing the new recipe data. It is expected to include the ID (or the server-side save will override it), or at least map correctly to the stored entity.
+
+ Example:
+
+ ```json
+ {
+ "id": 123,
+ "name": "Better Pancakes",
+ "ingredients": ["Flour", "Almond milk", "Eggs"],
+ "preparationSteps": ["Step 10", "Step 11"]
+ }
+ ```
+
+* **Responses**
+
+ | Status Code | Description |
+ | ----------------- | ---------------------------------------------------------------- |
+ | `200 OK` | The recipe was successfully updated. Returns the updated recipe. |
+ | `400 Bad Request` | No recipe exists with the given ID. |
+
+ * **Example Request**
+
+ ```bash
+ curl -X POST "https://your-domain.com/api/recipe/123" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "id": 123,
+ "name": "Better Pancakes",
+ "ingredients": ["Flour", "Almond milk", "Eggs"],
+ "preparationSteps": ["Step 10", "Step 11"]
+ }'
+ ```
+
+* **Example Response (200)**
+
+ ```json
+ {
+ "id": 123,
+ "name": "Updated Pancakes",
+ "ingredients": ["Flour", "Almond milk", "Eggs"],
+ "preparationSteps": "Mix and fry differently."
+ }
+ ```
+
+---
+
+### Delete a Recipe
+
+**DELETE** `/api/recipe/{id}`
+
+Deletes the recipe with the given ID.
+
+* **Path Parameters**
+
+ | Name | Type | Required | Description |
+ | ---- | ------ | -------- | ------------------------------- |
+ | `id` | `Long` | Yes | The ID of the recipe to delete. |
+
+* **Responses**
+
+ | Status Code | Description |
+ | ----------------- | ---------------------------------------------------- |
+ | `200 OK` | The recipe was successfully deleted. Returns `true`. |
+ | `400 Bad Request` | No recipe exists with the given ID. |
+
+* **Example Request**
+
+ ```bash
+ curl -X DELETE "https://your-domain.com/api/recipe/123"
+ ```
+
+* **Example Response (200)**
+
+ ```json
+ true
+ ```
\ No newline at end of file
From 1e9bd169d06cd219e9a20bf606053ed1c6fadafd Mon Sep 17 00:00:00 2001
From: Steven Liu
Date: Sun, 23 Nov 2025 20:04:59 +0100
Subject: [PATCH 14/14] test: improve unit test clarity
---
.../java/server/api/RecipeControllerTest.java | 68 +++++++++++--------
1 file changed, 40 insertions(+), 28 deletions(-)
diff --git a/server/src/test/java/server/api/RecipeControllerTest.java b/server/src/test/java/server/api/RecipeControllerTest.java
index 5931b3a..f1a766e 100644
--- a/server/src/test/java/server/api/RecipeControllerTest.java
+++ b/server/src/test/java/server/api/RecipeControllerTest.java
@@ -3,15 +3,19 @@ package server.api;
import commons.Recipe;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.http.HttpStatus;
import org.springframework.test.context.ActiveProfiles;
import server.database.RecipeRepository;
+import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import java.util.Set;
import java.util.stream.LongStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -28,7 +32,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
public class RecipeControllerTest {
private RecipeController controller;
private List recipes;
- private RecipeRepository recipeRepository;
+ private final RecipeRepository recipeRepository;
+ private List recipeIds;
public static final int NUM_RECIPES = 10;
// Injects a test repository into the test class
@@ -38,12 +43,29 @@ public class RecipeControllerTest {
}
@BeforeEach
- public void setup() {
+ public void setup(TestInfo info) {
recipes = LongStream
.range(0, NUM_RECIPES)
.mapToObj(x -> new Recipe(null, "Recipe " + x, List.of(), List.of()))
.toList();
controller = new RecipeController(recipeRepository);
+ Set tags = info.getTags();
+ List ids = new ArrayList<>();
+
+ // Some tests want initial data to be created.
+ if (tags.contains("test-from-init-data")) {
+ ids = LongStream
+ .range(0, NUM_RECIPES)
+ .map(idx -> recipeRepository.save(recipes.get((int) idx)).getId())
+ .boxed().toList();
+ }
+
+ // Some tests need to know the stored IDs of objects
+ if (tags.contains("need-ids")) {
+ recipeIds = ids;
+ }
+
+ // If no tags specified, the repository is initialized as empty.
}
@Test
public void createOneRecipe() {
@@ -60,31 +82,27 @@ public class RecipeControllerTest {
assertEquals(recipes.size(), recipeRepository.count());
}
@Test
+ @Tag("test-from-init-data")
public void getManyRecipes() {
- recipes.forEach(recipe -> controller.createRecipe(recipe));
-
// The number of recipes returned is the same as the entire input list
assertEquals(recipes.size(), controller.getRecipes(Optional.empty()).getBody().size());
}
@Test
+ @Tag("test-from-init-data")
public void getSomeRecipes() {
final int LIMIT = 5;
- recipes.forEach(recipe -> controller.createRecipe(recipe));
// The number of recipes returned is the same as the entire input list
assertEquals(LIMIT, controller.getRecipes(Optional.of(LIMIT)).getBody().size());
}
@Test
+ @Tag("test-from-init-data")
+ @Tag("need-ids")
public void findOneRecipeExists() {
final int CHECK_INDEX = 3;
- List ids = LongStream
- .range(0, NUM_RECIPES)
- .map(recipe -> controller.createRecipe(recipes.get((int) recipe)).getBody().getId())
- .boxed().toList();
-
// The third item in the input list is the same as the third item retrieved from the database
assertEquals(
recipes.get(CHECK_INDEX),
- controller.getRecipe(ids.get(CHECK_INDEX)).getBody());
+ controller.getRecipe(recipeIds.get(CHECK_INDEX)).getBody());
}
@Test
public void findOneRecipeNotExists() {
@@ -95,26 +113,22 @@ public class RecipeControllerTest {
controller.getRecipe((long) CHECK_INDEX).getStatusCode());
}
@Test
+ @Tag("test-from-init-data")
+ @Tag("need-ids")
public void deleteOneRecipeGood() {
final int DELETE_INDEX = 5;
- List ids = LongStream
- .range(0, NUM_RECIPES)
- .map(recipe -> controller.createRecipe(recipes.get((int) recipe)).getBody().getId())
- .boxed().toList();
// The object has been successfully deleted
- assertEquals(HttpStatus.OK, controller.deleteRecipe(ids.get(DELETE_INDEX)).getStatusCode());
+ assertEquals(HttpStatus.OK, controller.deleteRecipe(recipeIds.get(DELETE_INDEX)).getStatusCode());
}
@Test
+ @Tag("test-from-init-data")
+ @Tag("need-ids")
public void deleteOneRecipeCountGood() {
final int DELETE_INDEX = 5;
- List ids = LongStream
- .range(0, NUM_RECIPES)
- .map(recipe -> controller.createRecipe(recipes.get((int) recipe)).getBody().getId())
- .boxed().toList();
- controller.deleteRecipe(ids.get(DELETE_INDEX));
+ controller.deleteRecipe(recipeIds.get(DELETE_INDEX));
// The count of items decreased by 1 after the 5th item has been removed.
- assertEquals(ids.size() - 1, recipeRepository.count());
+ assertEquals(recipeIds.size() - 1, recipeRepository.count());
}
@Test
public void deleteOneRecipeFail() {
@@ -123,15 +137,13 @@ public class RecipeControllerTest {
}
@Test
+ @Tag("test-from-init-data")
+ @Tag("need-ids")
public void updateOneRecipeHasNewData() {
final int UPDATE_INDEX = 5;
- List ids = LongStream
- .range(0, NUM_RECIPES)
- .map(recipe -> controller.createRecipe(recipes.get((int) recipe)).getBody().getId())
- .boxed().toList();
- Recipe newRecipe = controller.getRecipe(ids.get(UPDATE_INDEX)).getBody();
+ Recipe newRecipe = controller.getRecipe(recipeIds.get(UPDATE_INDEX)).getBody();
newRecipe.setName("New recipe");
controller.updateRecipe(newRecipe.getId(), newRecipe);
- assertEquals("New recipe", recipeRepository.getReferenceById(ids.get(UPDATE_INDEX)).getName());
+ assertEquals("New recipe", recipeRepository.getReferenceById(recipeIds.get(UPDATE_INDEX)).getName());
}
}