Merge branch 'client/server-utils-tests-mock' into 'main'

Client/server utils tests mock

See merge request cse1105/2025-2026/teams/csep-team-76!56
This commit is contained in:
Oskar Rasieński 2026-01-15 01:07:59 +01:00
commit 22a8bc4708
8 changed files with 315 additions and 17 deletions

View file

@ -11,7 +11,6 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version.junit>5.10.1</version.junit> <version.junit>5.10.1</version.junit>
<version.mockito>5.8.0</version.mockito>
<version.jersey>3.1.9</version.jersey> <version.jersey>3.1.9</version.jersey>
<version.jfx>25.0.1</version.jfx> <version.jfx>25.0.1</version.jfx>
</properties> </properties>
@ -92,9 +91,9 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mockito</groupId> <groupId>org.wiremock</groupId>
<artifactId>mockito-core</artifactId> <artifactId>wiremock</artifactId>
<version>${version.mockito}</version> <version>3.3.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>

View file

@ -149,7 +149,7 @@ public class ServerUtils {
//Get the recipe //Get the recipe
HttpRequest request = HttpRequest.newBuilder() HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(SERVER + "/recipe/" + id)) .uri(URI.create(SERVER + "/recipe/" + id))
.GET() //Needs to be changed to POST() when api is changed .GET()
.build(); .build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

View file

@ -0,0 +1,287 @@
package client;
import client.utils.ServerUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import commons.FormalIngredient;
import commons.Ingredient;
import commons.Recipe;
import commons.RecipeIngredient;
import commons.VagueIngredient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static org.junit.jupiter.api.Assertions.*;
@WireMockTest(httpPort = 8080)
class ServerUtilsMockTest {
private ServerUtils serverUtils;
private ObjectMapper objectMapper;
static final List<Ingredient> ingredients = List.of(
new Ingredient("Bread", 1, 2, 3),
new Ingredient("Cheese", 2, 2, 2),
new Ingredient("Ham", 3, 3, 3)
);
static final List<RecipeIngredient> testIngredients = List.of(
new VagueIngredient(ingredients.get(0), "2 pieces of"),
new VagueIngredient(ingredients.get(1), "1 slice of"),
new VagueIngredient(ingredients.get(2), "1 slice of")
);
static final List<String> testPrepSteps = List.of("1. do smth", "2. do smth else");
@BeforeEach
void setup() {
objectMapper = new ObjectMapper();
serverUtils = new ServerUtils();
}
/**
* Returns a deep copy of recipe, but with null as id.
* @param recipe recipe to be stripped of id.
* @return new recipe without id.
*/
Recipe stripId(Recipe recipe) {
Recipe newRecipe = new Recipe();
newRecipe.setId(null);
newRecipe.setName(recipe.getName());
newRecipe.setIngredients(recipe.getIngredients());
newRecipe.setPreparationSteps(recipe.getPreparationSteps());
return newRecipe;
}
@Test
void addRecipeTest() throws IOException, InterruptedException {
Recipe input = new Recipe(
1L,
"Eggs on toast",
"en",
testIngredients,
testPrepSteps);
String inputJson = objectMapper.writeValueAsString(input);
// Setup fake endpoint that returns empty list for getRecipes.
// urlPathEqualTo, because it ignores query params.
stubFor(get(urlPathEqualTo("/api/recipes")).willReturn(okJson("[]")));
// Setup fake endpoint that returns the input recipe when creating new recipe.
stubFor(put(urlEqualTo("/api/recipe/new")).willReturn(okJson(inputJson)));
// The addRecipe should strip the recipe of id when sending to server.
Recipe verifyRecipe = stripId(input);
String verifyJson = objectMapper.writeValueAsString(verifyRecipe);
Recipe result = serverUtils.addRecipe(input);
// Confirm a put request has been made with the verifyRecipe
verify(putRequestedFor(urlEqualTo("/api/recipe/new"))
.withRequestBody(equalToJson(verifyJson)));
assertNotNull(result);
assertEquals(1L, result.getId());
assertEquals("Eggs on toast", result.getName());
assertEquals(testIngredients, result.getIngredients());
assertEquals(testPrepSteps, result.getPreparationSteps());
}
@Test
void addRecipeDuplicateTest() throws IOException, InterruptedException {
Recipe pre = new Recipe(
1L,
"Steak",
"en",
testIngredients,
testPrepSteps);
// We start with name Steak already being taken.
List<Recipe> recipes = List.of(pre);
String recipesJson = objectMapper.writeValueAsString(recipes);
// Setup fake endpoint that returns list with 1 recipe in it.
// urlPathEqualTo, because it ignores query params.
stubFor(get(urlPathEqualTo("/api/recipes")).willReturn(okJson(recipesJson)));
// Our input to add.
Recipe inputRecipe = new Recipe(
1337L,
pre.getName(),
"en",
pre.getIngredients(),
pre.getPreparationSteps());
// addRecipe should generate this.
Recipe generatedRecipe = new Recipe(
null,
pre.getName()+"(1)",
"en",
pre.getIngredients(),
pre.getPreparationSteps());
String verifyJson = objectMapper.writeValueAsString(generatedRecipe);
// Server will return this.
Recipe serverReturnRecipe = new Recipe(
pre.getId()+1,
pre.getName()+"(1)",
"en",
pre.getIngredients(),
pre.getPreparationSteps());
String serverReturnJson = objectMapper.writeValueAsString(serverReturnRecipe);
stubFor(put(urlEqualTo("/api/recipe/new")).willReturn(okJson(serverReturnJson)));
// Ignore the return value as it's tested in addRecipeTest.
serverUtils.addRecipe(inputRecipe);
// Confirm a put request has been made with the verifyRecipe
verify(putRequestedFor(urlEqualTo("/api/recipe/new"))
.withRequestBody(equalToJson(verifyJson)));
}
@Test
void addRecipeDoubleDuplicateTest() throws IOException, InterruptedException {
Recipe pre = new Recipe(
1L,
"Steak",
"en",
testIngredients,
testPrepSteps);
// We start with name Steak already being taken.
List<Recipe> recipes = new ArrayList<>(List.of(pre));
String recipesJson = objectMapper.writeValueAsString(recipes);
// Setup fake endpoint that returns list with 1 recipe in it.
// urlPathEqualTo, because it ignores query params.
stubFor(get(urlPathEqualTo("/api/recipes")).willReturn(okJson(recipesJson)));
// Our input to add.
Recipe inputRecipe = new Recipe(
1337L,
pre.getName(),
"en",
pre.getIngredients(),
pre.getPreparationSteps());
// addRecipe should generate this.
Recipe generatedRecipe = new Recipe(
null,
pre.getName()+"(2)",
"en",
pre.getIngredients(),
pre.getPreparationSteps());
String verifyJson = objectMapper.writeValueAsString(generatedRecipe);
// Doesn't matter what we return here, verifyJson is just convenient as it's a recipe.
stubFor(put(urlEqualTo("/api/recipe/new")).willReturn(okJson(verifyJson)));
// Ignore the return value as it's tested in addRecipeTest.
serverUtils.addRecipe(inputRecipe);
// update recipes
recipes.add(inputRecipe);
recipesJson = objectMapper.writeValueAsString(recipes);
inputRecipe.setName(pre.getName()); // Reset name back to Steak without the (1)
// Setup fake endpoint that returns list with 2 recipes in it.
// urlPathEqualTo, because it ignores query params.
stubFor(get(urlPathEqualTo("/api/recipes")).willReturn(okJson(recipesJson)));
serverUtils.addRecipe(inputRecipe);
// Confirm a put request has been made with the verifyRecipe
verify(putRequestedFor(urlEqualTo("/api/recipe/new"))
.withRequestBody(equalToJson(verifyJson)));
}
@Test
void addRecipeIngredientIdNullTest() throws IOException, InterruptedException {
// This test checks if RecipeIngredient and recipe ids are null.
Ingredient i1 = new Ingredient(1L, "salt",1,2,3);
Ingredient i2 = new Ingredient(2L, "pepper",4,5,6);
// input
List<RecipeIngredient> ingredients = List.of(
new FormalIngredient(1L, i1, 40, "g"),
new FormalIngredient(2L, i2, 50, "g")
);
// generated
List<RecipeIngredient> ingredientsStripped = List.of(
new FormalIngredient(null, i1, 40, "g"),
new FormalIngredient(null, i2, 50, "g")
);
Recipe inputRecipe = new Recipe(
1L,
"Steak",
"en",
ingredients,
testPrepSteps);
String inputJson = objectMapper.writeValueAsString(inputRecipe);
Recipe generatedRecipe = new Recipe(
null,
"Steak",
"en",
ingredientsStripped,
testPrepSteps);
String verifyJson = objectMapper.writeValueAsString(generatedRecipe);
// Setup fake endpoint that returns empty list for getRecipes.
// urlPathEqualTo, because it ignores query params.
stubFor(get(urlPathEqualTo("/api/recipes")).willReturn(okJson("[]")));
// Setup fake endpoint that returns the input recipe when creating new recipe.
stubFor(put(urlEqualTo("/api/recipe/new")).willReturn(okJson(inputJson)));
serverUtils.addRecipe(inputRecipe);
// Confirm a put request has been made with the verifyRecipe
verify(putRequestedFor(urlEqualTo("/api/recipe/new"))
.withRequestBody(equalToJson(verifyJson)));
}
@Test
void findIdNotFoundTest() throws IOException, InterruptedException {
// Setup fake endpoint that returns empty list for getRecipes.
// urlPathEqualTo, because it ignores query params.
stubFor(get(urlPathEqualTo("/api/recipes")).willReturn(okJson("[]")));
assertThrows(IOException.class, () -> serverUtils.findId(1L));
}
@Test
void findIdFoundTest() throws IOException, InterruptedException {
Recipe recipe = new Recipe(
1L,
"Steak",
"en",
testIngredients,
testPrepSteps);
String recipeJson = objectMapper.writeValueAsString(recipe);
// Setup fake endpoint that returns the recipe by id.
// urlPathEqualTo, because it ignores query params.
stubFor(get(urlPathEqualTo("/api/recipe/"+recipe.getId())).willReturn(okJson(recipeJson)));
Recipe found = serverUtils.findId(recipe.getId());
assertEquals(recipe.getId(), found.getId());
assertEquals(recipe.getName(), found.getName());
assertEquals(recipe.getIngredients(), found.getIngredients());
assertEquals(recipe.getPreparationSteps(), found.getPreparationSteps());
}
}

View file

@ -11,7 +11,6 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<version.junit>5.10.1</version.junit> <version.junit>5.10.1</version.junit>
<version.mockito>5.8.0</version.mockito>
</properties> </properties>
<dependencies> <dependencies>
@ -39,12 +38,6 @@
<version>${version.junit}</version> <version>${version.junit}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${version.mockito}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>

View file

@ -31,6 +31,13 @@ public class FormalIngredient extends RecipeIngredient implements Scalable<Forma
this.unitSuffix = unitSuffix; this.unitSuffix = unitSuffix;
} }
public FormalIngredient(Long id, Ingredient ingredient, double amount, String unitSuffix) {
// For testing
super(id, ingredient);
this.amount = amount;
this.unitSuffix = unitSuffix;
}
public FormalIngredient(Ingredient ingredient, double amount, String unitSuffix) { public FormalIngredient(Ingredient ingredient, double amount, String unitSuffix) {
super(ingredient); super(ingredient);
this.amount = amount; this.amount = amount;

View file

@ -54,9 +54,14 @@ public abstract class RecipeIngredient {
// for ORM // for ORM
} }
public RecipeIngredient( public RecipeIngredient(Ingredient ingredient) {
Ingredient ingredient) { // Store it in the field
//store it in the field this.ingredient = ingredient;
}
public RecipeIngredient(Long id, Ingredient ingredient) {
// For testing
this.id = id;
this.ingredient = ingredient; this.ingredient = ingredient;
} }

View file

@ -24,6 +24,11 @@ public class VagueIngredient extends RecipeIngredient {
this.description = description; this.description = description;
} }
public VagueIngredient(Long id, Ingredient ingredient, String description) {
super(id, ingredient);
this.description = description;
}
public VagueIngredient(Ingredient ingredient, String description) { public VagueIngredient(Ingredient ingredient, String description) {
super(ingredient); super(ingredient);
this.description = description; this.description = description;

View file

@ -9,7 +9,6 @@ import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
public class RecipeIngredientTest { public class RecipeIngredientTest {
private Map<String, FormalIngredient> ingredientUnitMap; private Map<String, FormalIngredient> ingredientUnitMap;
private Map<String, VagueIngredient> ingredientDescriptorMap; private Map<String, VagueIngredient> ingredientDescriptorMap;
@ -17,10 +16,12 @@ public class RecipeIngredientTest {
Ingredient ingredient = new Ingredient("Bread", 1, 2, 3); Ingredient ingredient = new Ingredient("Bread", 1, 2, 3);
return new FormalIngredient(ingredient, 1.0, unit); return new FormalIngredient(ingredient, 1.0, unit);
} }
private VagueIngredient getVague(String descriptor) { private VagueIngredient getVague(String descriptor) {
Ingredient ingredient = new Ingredient("Bread", 1, 2, 3); Ingredient ingredient = new Ingredient("Bread", 1, 2, 3);
return new VagueIngredient(ingredient, descriptor); return new VagueIngredient(ingredient, descriptor);
} }
@BeforeEach @BeforeEach
void setup() { void setup() {
ingredientUnitMap = new HashMap<>(); ingredientUnitMap = new HashMap<>();
@ -37,6 +38,7 @@ public class RecipeIngredientTest {
FormalIngredient fi = new FormalIngredient(ingredient, 1.0, "g"); FormalIngredient fi = new FormalIngredient(ingredient, 1.0, "g");
assertEquals(ingredient, fi.getIngredient()); assertEquals(ingredient, fi.getIngredient());
} }
@Test @Test
void testInstantiateVagueIngredient() { void testInstantiateVagueIngredient() {
Ingredient ingredient = new Ingredient("Bread", 1, 2, 3); Ingredient ingredient = new Ingredient("Bread", 1, 2, 3);