Merge branch 'feature/ingredient-api' into 'main'
Ingredient controller and tests See merge request cse1105/2025-2026/teams/csep-team-76!17
This commit is contained in:
commit
f9d4901b6c
13 changed files with 507 additions and 10 deletions
|
|
@ -33,10 +33,7 @@ public class Ingredient {
|
||||||
@Column(name = "carbs", nullable = false)
|
@Column(name = "carbs", nullable = false)
|
||||||
public double carbsPer100g;
|
public double carbsPer100g;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
public Ingredient() {}
|
||||||
protected Ingredient() {
|
|
||||||
// for object mapper says sebastian
|
|
||||||
}
|
|
||||||
|
|
||||||
public Ingredient(String name,
|
public Ingredient(String name,
|
||||||
double proteinPer100g,
|
double proteinPer100g,
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,5 @@ package commons.ws;
|
||||||
|
|
||||||
public class Topics {
|
public class Topics {
|
||||||
public static final String RECIPES = "/subscribe/recipe";
|
public static final String RECIPES = "/subscribe/recipe";
|
||||||
|
public static final String INGREDIENTS = "/subscribe/ingredient";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package commons.ws.messages;
|
||||||
|
|
||||||
|
import commons.Ingredient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message sent when a new ingredient is created.
|
||||||
|
*
|
||||||
|
* @see commons.ws.messages.Message.Type#INGREDIENT_CREATE
|
||||||
|
*/
|
||||||
|
public class CreateIngredientMessage implements Message {
|
||||||
|
private Ingredient ingredient;
|
||||||
|
|
||||||
|
public CreateIngredientMessage(Ingredient ingredient) {
|
||||||
|
this.ingredient = ingredient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.INGREDIENT_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the created ingredient.
|
||||||
|
*
|
||||||
|
* @return The created ingredient.
|
||||||
|
*/
|
||||||
|
public Ingredient getIngredient() {
|
||||||
|
return ingredient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
package commons.ws.messages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message sent when an ingredient is deleted.
|
||||||
|
*
|
||||||
|
* @see commons.ws.messages.Message.Type#INGREDIENT_DELETE
|
||||||
|
*/
|
||||||
|
public class DeleteIngredientMessage implements Message {
|
||||||
|
private Long ingredientId;
|
||||||
|
|
||||||
|
public DeleteIngredientMessage(Long ingredientId) {
|
||||||
|
this.ingredientId = ingredientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.INGREDIENT_DELETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the deleted ingredient's id.
|
||||||
|
*
|
||||||
|
* @return The deleted ingredient's id.
|
||||||
|
*/
|
||||||
|
public Long getIngredientId() {
|
||||||
|
return ingredientId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,28 @@ public interface Message {
|
||||||
*
|
*
|
||||||
* @see commons.ws.messages.DeleteRecipeMessage
|
* @see commons.ws.messages.DeleteRecipeMessage
|
||||||
*/
|
*/
|
||||||
RECIPE_DELETE
|
RECIPE_DELETE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message sent when a new ingredient is created.
|
||||||
|
*
|
||||||
|
* @see commons.ws.messages.CreateIngredientMessage
|
||||||
|
*/
|
||||||
|
INGREDIENT_CREATE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message sent when an existing ingredient is updated.
|
||||||
|
*
|
||||||
|
* @see commons.ws.messages.UpdateIngredientMessage
|
||||||
|
*/
|
||||||
|
INGREDIENT_UPDATE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message sent when an ingredient is deleted.
|
||||||
|
*
|
||||||
|
* @see commons.ws.messages.DeleteIngredientMessage
|
||||||
|
*/
|
||||||
|
INGREDIENT_DELETE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package commons.ws.messages;
|
||||||
|
|
||||||
|
import commons.Ingredient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message sent when an ingredient is updated.
|
||||||
|
*
|
||||||
|
* @see commons.ws.messages.Message.Type#INGREDIENT_UPDATAE
|
||||||
|
*/
|
||||||
|
public class UpdateIngredientMessage implements Message {
|
||||||
|
private Ingredient ingredient;
|
||||||
|
|
||||||
|
public UpdateIngredientMessage(Ingredient ingredient) {
|
||||||
|
this.ingredient = ingredient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Type getType() {
|
||||||
|
return Type.INGREDIENT_UPDATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the updated ingredient.
|
||||||
|
*
|
||||||
|
* @return The updated ingredient.
|
||||||
|
*/
|
||||||
|
public Ingredient getIngredient() {
|
||||||
|
return ingredient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -47,7 +47,6 @@
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
||||||
206
server/src/main/java/server/api/IngredientController.java
Normal file
206
server/src/main/java/server/api/IngredientController.java
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
package server.api;
|
||||||
|
|
||||||
|
import commons.Ingredient;
|
||||||
|
import commons.ws.Topics;
|
||||||
|
import commons.ws.messages.CreateIngredientMessage;
|
||||||
|
import commons.ws.messages.DeleteIngredientMessage;
|
||||||
|
import commons.ws.messages.UpdateIngredientMessage;
|
||||||
|
import org.springframework.data.domain.Example;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PatchMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
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.IngredientRepository;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST controller for managing ingredients.
|
||||||
|
* <p>
|
||||||
|
* Exposes the following endpoints:
|
||||||
|
* <ul>
|
||||||
|
* <li>GET /api/ingredients - Get a list of ingredients, optionally paginated.</li>
|
||||||
|
* <li>GET /api/ingredients/{id} - Get a specific ingredient by its ID.</li>
|
||||||
|
* <li>POST /api/ingredients - Create a new ingredient.</li>
|
||||||
|
* <li>PATCH /api/ingredients/{id} - Update an existing ingredient by its ID.</li>
|
||||||
|
* <li>DELETE /api/ingredients/{id} - Delete an ingredient by its ID.</li>
|
||||||
|
* </ul>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see Ingredient
|
||||||
|
* @see IngredientRepository
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api")
|
||||||
|
public class IngredientController {
|
||||||
|
private final IngredientRepository ingredientRepository;
|
||||||
|
private final SimpMessagingTemplate messagingTemplate;
|
||||||
|
|
||||||
|
public IngredientController(IngredientRepository ingredientRepository,
|
||||||
|
SimpMessagingTemplate messagingTemplate) {
|
||||||
|
this.ingredientRepository = ingredientRepository;
|
||||||
|
this.messagingTemplate = messagingTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of ingredients, optionally paginated. <br />
|
||||||
|
* Maps to <code>GET /api/ingredients(?page=&limit=)</code>
|
||||||
|
* <p>
|
||||||
|
* If no limit is specified, all ingredients are returned sorted by name ascending.
|
||||||
|
* When calling this function, consider using pagination to avoid large responses.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* When using pagination, you should provide a limit.
|
||||||
|
* The page defaults to 0 if not provided.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param page The page number to retrieve (0-indexed). Optional.
|
||||||
|
* @param limit The maximum number of ingredients to return. Optional.
|
||||||
|
* @return A ResponseEntity containing the list of ingredients.
|
||||||
|
*
|
||||||
|
* @see Ingredient
|
||||||
|
*/
|
||||||
|
@GetMapping("/ingredients")
|
||||||
|
public ResponseEntity<List<Ingredient>> getIngredients(
|
||||||
|
@RequestParam Optional<Integer> page,
|
||||||
|
@RequestParam Optional<Integer> limit
|
||||||
|
) {
|
||||||
|
List<Ingredient> ingredients = limit
|
||||||
|
.map(l -> {
|
||||||
|
return ingredientRepository.findAllByOrderByNameAsc(
|
||||||
|
PageRequest.of(page.orElse(0), l)
|
||||||
|
).toList();
|
||||||
|
})
|
||||||
|
.orElseGet(ingredientRepository::findAllByOrderByNameAsc);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(ingredients);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a specific ingredient by its ID.
|
||||||
|
* Maps to <code>GET /api/ingredients/{id}</code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Returns 200 OK with the ingredient if found,
|
||||||
|
* or 404 Not Found if the ingredient does not exist.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param id The ID of the ingredient to retrieve.
|
||||||
|
* @return The ingredient wrapped in a ResponseEntity.
|
||||||
|
*
|
||||||
|
* @see Ingredient
|
||||||
|
*/
|
||||||
|
@GetMapping("/ingredients/{id}")
|
||||||
|
public ResponseEntity<Ingredient> getIngredientById(@PathVariable Long id) {
|
||||||
|
return ingredientRepository.findById(id)
|
||||||
|
.map(ResponseEntity::ok)
|
||||||
|
.orElseGet(() -> ResponseEntity.notFound().build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing ingredient by its ID.
|
||||||
|
* Maps to <code>PATCH /api/ingredients/{id}</code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If the ingredient with the specified ID does not exist,
|
||||||
|
* returns 404 Not Found.
|
||||||
|
* <p>
|
||||||
|
* If the ingredient exists, updates it with the provided data
|
||||||
|
* and returns the updated ingredient with 200 OK.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param id The ID of the ingredient to update.
|
||||||
|
* @param updated The updated ingredient data.
|
||||||
|
* @return The updated ingredient wrapped in a ResponseEntity.
|
||||||
|
*
|
||||||
|
* @see Ingredient
|
||||||
|
*/
|
||||||
|
@PatchMapping("/ingredients/{id}")
|
||||||
|
public ResponseEntity<Ingredient> updateIngredient(
|
||||||
|
@PathVariable Long id,
|
||||||
|
@RequestBody Ingredient updated
|
||||||
|
) {
|
||||||
|
if (!ingredientRepository.existsById(id)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Refactor to use setters
|
||||||
|
updated.id = id;
|
||||||
|
Ingredient savedIngredient = ingredientRepository.save(updated);
|
||||||
|
messagingTemplate.convertAndSend(Topics.INGREDIENTS, new CreateIngredientMessage(savedIngredient));
|
||||||
|
|
||||||
|
return ResponseEntity.ok(savedIngredient);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new ingredient.
|
||||||
|
* Maps to <code>POST /api/ingredients</code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* If an ingredient with the same name already exists,
|
||||||
|
* returns 400 Bad Request.
|
||||||
|
*
|
||||||
|
* If the ingredient is created successfully,
|
||||||
|
* returns the created ingredient with 200 OK.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param ingredient The ingredient to create.
|
||||||
|
* @return The created ingredient wrapped in a ResponseEntity.
|
||||||
|
*
|
||||||
|
* @see Ingredient
|
||||||
|
*/
|
||||||
|
@PostMapping("/ingredients")
|
||||||
|
public ResponseEntity<Ingredient> createIngredient(@RequestBody Ingredient ingredient) {
|
||||||
|
if (ingredient.name == null || ingredient.name.isEmpty()) {
|
||||||
|
return ResponseEntity.badRequest().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ingredient example = new Ingredient();
|
||||||
|
example.name = ingredient.name;
|
||||||
|
|
||||||
|
if (ingredientRepository.exists(Example.of(example))) {
|
||||||
|
return ResponseEntity.badRequest().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ingredient saved = ingredientRepository.save(ingredient);
|
||||||
|
messagingTemplate.convertAndSend(Topics.INGREDIENTS, new UpdateIngredientMessage(saved));
|
||||||
|
|
||||||
|
return ResponseEntity.ok(saved);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an ingredient by its ID.
|
||||||
|
* Maps to <code>DELETE /api/ingredients/{id}</code>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Returns 404 Not Found if the ingredient does not exist.
|
||||||
|
* If the ingredient is deleted successfully, returns 200 OK with true.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param id The ID of the ingredient to delete.
|
||||||
|
* @return A ResponseEntity indicating the result of the operation.
|
||||||
|
*
|
||||||
|
* @see Ingredient
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/ingredients/{id}")
|
||||||
|
public ResponseEntity<Boolean> deleteIngredient(@PathVariable Long id) {
|
||||||
|
if (!ingredientRepository.existsById(id)) {
|
||||||
|
return ResponseEntity.notFound().build();
|
||||||
|
}
|
||||||
|
|
||||||
|
ingredientRepository.deleteById(id);
|
||||||
|
messagingTemplate.convertAndSend(Topics.INGREDIENTS, new DeleteIngredientMessage(id));
|
||||||
|
|
||||||
|
return ResponseEntity.ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -32,7 +32,8 @@ public class RecipeController {
|
||||||
private final RecipeRepository recipeRepository; // JPA repository used in this controller
|
private final RecipeRepository recipeRepository; // JPA repository used in this controller
|
||||||
private final SimpMessagingTemplate messagingTemplate;
|
private final SimpMessagingTemplate messagingTemplate;
|
||||||
|
|
||||||
public RecipeController(RecipeRepository recipeRepository, SimpMessagingTemplate messagingTemplate) {
|
public RecipeController(RecipeRepository recipeRepository,
|
||||||
|
SimpMessagingTemplate messagingTemplate) {
|
||||||
this.recipeRepository = recipeRepository;
|
this.recipeRepository = recipeRepository;
|
||||||
this.messagingTemplate = messagingTemplate;
|
this.messagingTemplate = messagingTemplate;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package server.api;
|
package server.api;
|
||||||
|
|
||||||
import commons.Recipe;
|
import commons.Recipe;
|
||||||
|
import commons.ws.Topics;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
|
|
@ -22,7 +23,7 @@ public class UpdateMessagingController {
|
||||||
* @return The updated {@link Recipe} object wrapped in a {@link org.springframework.http.ResponseEntity}.
|
* @return The updated {@link Recipe} object wrapped in a {@link org.springframework.http.ResponseEntity}.
|
||||||
*/
|
*/
|
||||||
@MessageMapping("/updates/recipe")
|
@MessageMapping("/updates/recipe")
|
||||||
@SendTo("/subscribe/recipe")
|
@SendTo(Topics.RECIPES)
|
||||||
public ResponseEntity<Recipe> broadcastRecipeUpdate(Recipe recipe) {
|
public ResponseEntity<Recipe> broadcastRecipeUpdate(Recipe recipe) {
|
||||||
return recipeController.updateRecipe(recipe.getId(), recipe);
|
return recipeController.updateRecipe(recipe.getId(), recipe);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package server;
|
package server.database;
|
||||||
|
|
||||||
import commons.Ingredient;
|
import commons.Ingredient;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface IngredientRepository extends JpaRepository<Ingredient, Long> {
|
public interface IngredientRepository extends JpaRepository<Ingredient, Long> {
|
||||||
List<Ingredient> findAllByOrderByNameAsc();
|
List<Ingredient> findAllByOrderByNameAsc();
|
||||||
|
Page<Ingredient> findAllByOrderByNameAsc(Pageable pageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package server;
|
package server.database;
|
||||||
|
|
||||||
import commons.RecipeIngredient;
|
import commons.RecipeIngredient;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
180
server/src/test/java/server/api/IngredientControllerTest.java
Normal file
180
server/src/test/java/server/api/IngredientControllerTest.java
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
package server.api;
|
||||||
|
|
||||||
|
import commons.Ingredient;
|
||||||
|
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.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatusCode;
|
||||||
|
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||||
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
|
import server.database.IngredientRepository;
|
||||||
|
import server.WebSocketConfig;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@DataJpaTest
|
||||||
|
@ActiveProfiles("mock-data-test")
|
||||||
|
@Import(WebSocketConfig.class)
|
||||||
|
public class IngredientControllerTest {
|
||||||
|
private final SimpMessagingTemplate template;
|
||||||
|
private final IngredientRepository ingredientRepository;
|
||||||
|
private IngredientController controller;
|
||||||
|
|
||||||
|
private static final double PROTEIN_BASE = 1.0;
|
||||||
|
private static final double FAT_BASE = 2.0;
|
||||||
|
private static final double CARBS_BASE = 2.0;
|
||||||
|
|
||||||
|
private static final double PROTEIN_ALT = 0.5;
|
||||||
|
private static final double FAT_ALT = 1.0;
|
||||||
|
private static final double CARBS_ALT = 1.5;
|
||||||
|
|
||||||
|
private static final int OK_STATUS = 200;
|
||||||
|
private static final int BAD_REQUEST_STATUS = 400;
|
||||||
|
private static final int NOT_FOUND_STATUS = 404;
|
||||||
|
|
||||||
|
private static final Long INVALID_ID = 2137L;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public IngredientControllerTest(IngredientRepository ingredientRepository,
|
||||||
|
SimpMessagingTemplate template) {
|
||||||
|
this.ingredientRepository = ingredientRepository;
|
||||||
|
this.template = template;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createInitialIngredients() {
|
||||||
|
ingredientRepository.deleteAll();
|
||||||
|
|
||||||
|
Stream.of("Salt", "Sugar", "Flour", "Eggs", "Milk")
|
||||||
|
.map(name -> {
|
||||||
|
return new Ingredient(name, PROTEIN_BASE, FAT_BASE, CARBS_BASE);
|
||||||
|
})
|
||||||
|
.forEach(ingredientRepository::save);
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() {
|
||||||
|
controller = new IngredientController(ingredientRepository, template);
|
||||||
|
|
||||||
|
this.createInitialIngredients();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllIngredients() {
|
||||||
|
List<Ingredient> ingredients = controller.getIngredients(Optional.empty(), Optional.empty()).getBody();
|
||||||
|
|
||||||
|
assertNotNull(ingredients);
|
||||||
|
final int expectedCount = 5;
|
||||||
|
assertEquals(expectedCount, ingredients.size());
|
||||||
|
assertEquals("Eggs", ingredients.getFirst().name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetPaginatedIngredients() {
|
||||||
|
final int limit = 2;
|
||||||
|
List<Ingredient> ingredients = controller.getIngredients(Optional.of(0), Optional.of(limit)).getBody();
|
||||||
|
|
||||||
|
assertNotNull(ingredients);
|
||||||
|
final int expectedCount = 2;
|
||||||
|
assertEquals(expectedCount, ingredients.size());
|
||||||
|
assertEquals("Eggs", ingredients.get(0).name);
|
||||||
|
assertEquals("Flour", ingredients.get(1).name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetIngredientById() {
|
||||||
|
Ingredient ingredient = ingredientRepository.findAll().getFirst();
|
||||||
|
Long id = ingredient.id;
|
||||||
|
|
||||||
|
Ingredient fetchedIngredient = controller.getIngredientById(id).getBody();
|
||||||
|
|
||||||
|
assertNotNull(fetchedIngredient);
|
||||||
|
assertEquals(ingredient.name, fetchedIngredient.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetIngredientByInvalidId() {
|
||||||
|
var response = controller.getIngredientById(INVALID_ID);
|
||||||
|
|
||||||
|
assertEquals(HttpStatusCode.valueOf(NOT_FOUND_STATUS), response.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateIngredient() {
|
||||||
|
Ingredient newIngredient = new Ingredient("Butter", PROTEIN_ALT, FAT_ALT, CARBS_ALT);
|
||||||
|
|
||||||
|
Ingredient createdIngredient = controller.createIngredient(newIngredient).getBody();
|
||||||
|
|
||||||
|
final int expectedCount = 6;
|
||||||
|
|
||||||
|
assertNotNull(createdIngredient);
|
||||||
|
assertEquals("Butter", createdIngredient.name);
|
||||||
|
assertEquals(expectedCount, ingredientRepository.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateIngredientMissingName() {
|
||||||
|
Ingredient newIngredient = new Ingredient(null, PROTEIN_ALT, FAT_ALT, CARBS_ALT);
|
||||||
|
|
||||||
|
var response = controller.createIngredient(newIngredient);
|
||||||
|
|
||||||
|
assertEquals(HttpStatusCode.valueOf(BAD_REQUEST_STATUS), response.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateIngredient() {
|
||||||
|
Ingredient ingredient = ingredientRepository.findAll()
|
||||||
|
.stream()
|
||||||
|
.filter(i -> i.name.equals("Salt"))
|
||||||
|
.findFirst()
|
||||||
|
.get(); // Should exist, no need for a check
|
||||||
|
|
||||||
|
Long id = ingredient.id;
|
||||||
|
|
||||||
|
Ingredient updatedData = new Ingredient("Sea Salt", PROTEIN_ALT, FAT_ALT, CARBS_ALT);
|
||||||
|
|
||||||
|
Ingredient updatedIngredient = controller.updateIngredient(id, updatedData).getBody();
|
||||||
|
|
||||||
|
assertNotNull(updatedIngredient);
|
||||||
|
assertEquals("Sea Salt", updatedIngredient.name);
|
||||||
|
assertEquals(PROTEIN_ALT, updatedIngredient.proteinPer100g);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateMissingIngredient() {
|
||||||
|
Ingredient updatedData = new Ingredient("Sea Salt", PROTEIN_ALT, FAT_ALT, CARBS_ALT);
|
||||||
|
|
||||||
|
var response = controller.updateIngredient(INVALID_ID, updatedData);
|
||||||
|
|
||||||
|
assertEquals(HttpStatusCode.valueOf(NOT_FOUND_STATUS), response.getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteIngredient() {
|
||||||
|
Ingredient ingredient = ingredientRepository.findAll()
|
||||||
|
.stream()
|
||||||
|
.filter(i -> i.name.equals("Sugar"))
|
||||||
|
.findFirst()
|
||||||
|
.get(); // Should exist, no need for a check
|
||||||
|
|
||||||
|
Long id = ingredient.id;
|
||||||
|
|
||||||
|
var response = controller.deleteIngredient(id);
|
||||||
|
|
||||||
|
final int expectedCount = 4;
|
||||||
|
assertEquals(HttpStatusCode.valueOf(OK_STATUS), response.getStatusCode());
|
||||||
|
assertEquals(expectedCount, ingredientRepository.count());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteMissingIngredient() {
|
||||||
|
var response = controller.deleteIngredient(INVALID_ID);
|
||||||
|
|
||||||
|
assertEquals(HttpStatusCode.valueOf(NOT_FOUND_STATUS), response.getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue