Merge branch 'Server-testing' into 'main'

IngredientController test + a small refactor

Closes #61 and #62

See merge request cse1105/2025-2026/teams/csep-team-76!61
This commit is contained in:
Aysegul Aydinlik 2026-01-16 15:05:12 +01:00
commit 90653ec312
3 changed files with 136 additions and 48 deletions

View file

@ -31,12 +31,14 @@ import java.util.logging.Logger;
@RequestMapping("/api")
public class RecipeController {
private static final Logger logger = Logger.getLogger(RecipeController.class.getName());
private static final Logger logger = Logger.getLogger(
RecipeController.class.getName());
private SimpMessagingTemplate messagingTemplate;
private RecipeService recipeService;
private RecipeRepository recipeRepository;
public RecipeController(RecipeService recipeService, SimpMessagingTemplate messagingTemplate) {
public RecipeController(RecipeService recipeService,
SimpMessagingTemplate messagingTemplate) {
this.recipeService = recipeService;
this.messagingTemplate = messagingTemplate;
logger.info("Initialized controller.");
@ -72,24 +74,26 @@ public class RecipeController {
) {
logger.info("GET /recipes called.");
// TODO: maybe refactor this. this is horrid and evil and nightmare
var recipes = locales
.map(loc -> {
return limit.map(lim -> {
return recipeService.findAllWithLocales(loc, lim);
})
.orElseGet(() -> {
return recipeService.findAllWithLocales(loc);
});
})
.orElseGet(
// Choose the right overload. One has a limit, other doesn't.
() -> limit.map(recipeService::findAll).orElseGet(recipeService::findAll));
List<Recipe> recipes = locales
.map(loc -> findWithLocales(loc, limit))
.orElseGet(() -> findAll(limit));
return ResponseEntity.ok(recipes);
}
private List<Recipe> findWithLocales(List<String> locales, Optional<Integer> limit) {
return limit
.map(lim -> recipeService.findAllWithLocales(locales, lim))
.orElseGet(() -> recipeService.findAllWithLocales(locales));
}
private List<Recipe> findAll(Optional<Integer> limit) {
return limit
.map(recipeService::findAll)
.orElseGet(recipeService::findAll);
}
/**
* Mapping for <code>POST /recipe/{id}</code>.
* Also creates the ingredient elements if they do not exist.

View file

@ -6,6 +6,7 @@ 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.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.test.context.ActiveProfiles;
@ -70,18 +71,24 @@ public class IngredientControllerTest {
@Test
public void testGetAllIngredients() {
List<Ingredient> ingredients = controller.getIngredients(Optional.empty(), Optional.empty()).getBody();
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);
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();
List<Ingredient> ingredients =
controller.getIngredients(Optional.of(0),
Optional.of(limit)).getBody();
assertNotNull(ingredients);
final int expectedCount = 2;
@ -105,14 +112,17 @@ public class IngredientControllerTest {
public void testGetIngredientByInvalidId() {
var response = controller.getIngredientById(INVALID_ID);
assertEquals(HttpStatusCode.valueOf(NOT_FOUND_STATUS), response.getStatusCode());
assertEquals(HttpStatusCode.valueOf(NOT_FOUND_STATUS),
response.getStatusCode());
}
@Test
public void testCreateIngredient() {
Ingredient newIngredient = new Ingredient("Butter", PROTEIN_ALT, FAT_ALT, CARBS_ALT);
Ingredient newIngredient = new Ingredient("Butter",
PROTEIN_ALT, FAT_ALT, CARBS_ALT);
Ingredient createdIngredient = controller.createIngredient(newIngredient).getBody();
Ingredient createdIngredient =
controller.createIngredient(newIngredient).getBody();
final int expectedCount = 6;
@ -123,11 +133,13 @@ public class IngredientControllerTest {
@Test
public void testCreateIngredientMissingName() {
Ingredient newIngredient = new Ingredient(null, PROTEIN_ALT, FAT_ALT, CARBS_ALT);
Ingredient newIngredient = new Ingredient(null,
PROTEIN_ALT, FAT_ALT, CARBS_ALT);
var response = controller.createIngredient(newIngredient);
assertEquals(HttpStatusCode.valueOf(BAD_REQUEST_STATUS), response.getStatusCode());
assertEquals(HttpStatusCode.valueOf(BAD_REQUEST_STATUS),
response.getStatusCode());
}
@Test
@ -140,22 +152,30 @@ public class IngredientControllerTest {
Long id = ingredient.id;
Ingredient updatedData = new Ingredient("Sea Salt", PROTEIN_ALT, FAT_ALT, CARBS_ALT);
Ingredient updatedData = new Ingredient("Sea Salt",
PROTEIN_ALT, FAT_ALT, CARBS_ALT);
Ingredient updatedIngredient = controller.updateIngredient(id, updatedData).getBody();
Ingredient updatedIngredient =
controller.updateIngredient(id, updatedData).getBody();
assertNotNull(updatedIngredient);
assertEquals("Sea Salt", updatedIngredient.name);
assertEquals(PROTEIN_ALT, updatedIngredient.proteinPer100g);
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);
Ingredient updatedData = new Ingredient(
"Sea Salt",
PROTEIN_ALT, FAT_ALT, CARBS_ALT);
var response = controller.updateIngredient(INVALID_ID, updatedData);
var response = controller.updateIngredient(INVALID_ID,
updatedData);
assertEquals(HttpStatusCode.valueOf(NOT_FOUND_STATUS), response.getStatusCode());
assertEquals(HttpStatusCode.valueOf(NOT_FOUND_STATUS),
response.getStatusCode());
}
@Test
@ -171,14 +191,56 @@ public class IngredientControllerTest {
var response = controller.deleteIngredient(id);
final int expectedCount = 4;
assertEquals(HttpStatusCode.valueOf(OK_STATUS), response.getStatusCode());
assertEquals(expectedCount, ingredientRepository.count());
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());
assertEquals(HttpStatusCode.valueOf(NOT_FOUND_STATUS),
response.getStatusCode());
}
@Test
public void testCreateIngredientNullIngredientReturnsBadRequest() {
var response = controller.createIngredient(null);
assertEquals(HttpStatusCode.valueOf(BAD_REQUEST_STATUS),
response.getStatusCode());
assertEquals(5,
ingredientRepository.count()); // unchanged
}
@Test
public void testCreateIngredientBlankNameReturnsBadRequest() {
Ingredient blankName = new Ingredient(
" ",
PROTEIN_ALT, FAT_ALT, CARBS_ALT);
var response =
controller.createIngredient(blankName);
assertEquals(HttpStatusCode.valueOf(BAD_REQUEST_STATUS),
response.getStatusCode());
assertEquals(5,
ingredientRepository.count());
}
@Test
public void testCreateIngredientDuplicateNameReturnsConflict() {
Ingredient duplicate = new Ingredient("Salt",
PROTEIN_ALT, FAT_ALT, CARBS_ALT);
var response =
controller.createIngredient(duplicate);
assertEquals(HttpStatus.CONFLICT,
response.getStatusCode());
assertEquals(5,
ingredientRepository.count()); // unchanged
}
}

View file

@ -64,7 +64,10 @@ public class RecipeControllerTest {
public void setup(TestInfo info) {
recipes = LongStream
.range(0, NUM_RECIPES)
.mapToObj(x -> new Recipe(null, "Recipe " + x, "en", List.of(), List.of()))
.mapToObj(x -> new Recipe(
null,
"Recipe " + x,
"en", List.of(), List.of()))
.toList();
controller = new RecipeController(
recipeService,
@ -94,20 +97,26 @@ public class RecipeControllerTest {
controller.createRecipe(recipes.getFirst());
// There is 1 recipe in the repository
assertEquals(1, recipeRepository.count());
assertEquals(1,
recipeRepository.count());
}
@Test
public void createManyRecipes() {
recipes.forEach(recipe -> controller.createRecipe(recipe));
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());
assertEquals(recipes.size(),
recipeRepository.count());
}
@Test
@Tag("test-from-init-data")
public void getManyRecipes() {
// The number of recipes returned is the same as the entire input list
assertEquals(recipes.size(), controller.getRecipes(Optional.empty(), Optional.empty()).getBody().size());
assertEquals(recipes.size(),
controller.getRecipes(
Optional.empty(),
Optional.empty()).getBody().size());
}
@Test
@ -115,21 +124,28 @@ public class RecipeControllerTest {
public void getSomeRecipes() {
final int LIMIT = 5;
// The number of recipes returned is the same as the entire input list
assertEquals(LIMIT, controller.getRecipes(Optional.empty(), Optional.of(LIMIT)).getBody().size());
assertEquals(LIMIT,
controller.getRecipes(
Optional.empty(),
Optional.of(LIMIT)).getBody().size());
}
@Test
@Tag("test-from-init-data")
public void getManyRecipesWithLocale() {
// The number of recipes returned is the same as the entire input list
assertEquals(recipes.size(), controller.getRecipes(Optional.of(List.of("en", "nl")), Optional.empty()).getBody().size());
assertEquals(recipes.size(),
controller.getRecipes(Optional.of(List.of("en", "nl")),
Optional.empty()).getBody().size());
}
@Test
@Tag("test-from-init-data")
public void getNoRecipesWithLocale() {
// should have NO Dutch recipes (thank god)
assertEquals(0, controller.getRecipes(Optional.of(List.of("nl")), Optional.empty()).getBody().size());
assertEquals(0,
controller.getRecipes(Optional.of(List.of("nl")),
Optional.empty()).getBody().size());
}
@Test
@ -137,7 +153,9 @@ public class RecipeControllerTest {
public void getSomeRecipesWithLocale() {
final int LIMIT = 5;
// The number of recipes returned is the same as the entire input list
assertEquals(LIMIT, controller.getRecipes(Optional.of(List.of("en", "nl")), Optional.of(LIMIT)).getBody().size());
assertEquals(LIMIT,
controller.getRecipes(Optional.of(List.of("en", "nl")),
Optional.of(LIMIT)).getBody().size());
}
@Test
@ -167,7 +185,8 @@ public class RecipeControllerTest {
final int DELETE_INDEX = 5;
// The object has been successfully deleted
assertEquals(HttpStatus.OK, controller.deleteRecipe(recipeIds.get(DELETE_INDEX)).getStatusCode());
assertEquals(HttpStatus.OK,
controller.deleteRecipe(recipeIds.get(DELETE_INDEX)).getStatusCode());
}
@Test
@ -178,13 +197,15 @@ public class RecipeControllerTest {
controller.deleteRecipe(recipeIds.get(DELETE_INDEX));
// The count of items decreased by 1 after the 5th item has been removed.
assertEquals(recipeIds.size() - 1, recipeRepository.count());
assertEquals(recipeIds.size() - 1,
recipeRepository.count());
}
@Test
public void deleteOneRecipeFail() {
final Long DELETE_INDEX = 5L;
assertEquals(HttpStatus.BAD_REQUEST, controller.deleteRecipe(DELETE_INDEX).getStatusCode());
assertEquals(HttpStatus.BAD_REQUEST,
controller.deleteRecipe(DELETE_INDEX).getStatusCode());
}
@Test
@ -195,6 +216,7 @@ public class RecipeControllerTest {
Recipe newRecipe = controller.getRecipe(recipeIds.get(UPDATE_INDEX)).getBody();
newRecipe.setName("New recipe");
controller.updateRecipe(newRecipe.getId(), newRecipe);
assertEquals("New recipe", recipeRepository.getReferenceById(recipeIds.get(UPDATE_INDEX)).getName());
assertEquals("New recipe",
recipeRepository.getReferenceById(recipeIds.get(UPDATE_INDEX)).getName());
}
}