Refactoring of custom matchers and implementation/testing of ImageService.getImagesOwnedBy().
This commit is contained in:
parent
24db93111f
commit
d4da12c349
9
TODO.md
Normal file
9
TODO.md
Normal file
@ -0,0 +1,9 @@
|
||||
# TODO
|
||||
|
||||
* [ ] Tighten up api
|
||||
* [ ] Follow conventions established in `S3ImageService` and then
|
||||
`RecipeServiceImpl`.
|
||||
* [ ] Tighten up things to be more Spring-like.
|
||||
* [ ] Figure out Spring(-Boot) profiles for testing, staging, production, etc.
|
||||
* [ ] Decide on how to have services running in production: containerized or
|
||||
on-system installs?
|
@ -16,8 +16,10 @@ import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import static app.mealsmadeeasy.api.matchers.Matchers.isUser;
|
||||
import static app.mealsmadeeasy.api.image.ContainsImagesMatcher.containsImages;
|
||||
import static app.mealsmadeeasy.api.user.IsUserMatcher.isUser;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
@ -59,6 +61,16 @@ public class S3ImageServiceTests {
|
||||
}
|
||||
}
|
||||
|
||||
private Image createHal9000(User owner, InputStream data) throws ImageException, IOException {
|
||||
return this.imageService.create(
|
||||
owner,
|
||||
"HAL9000.svg",
|
||||
data,
|
||||
"image/svg+xml",
|
||||
27881L
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void smokeScreen() {}
|
||||
|
||||
@ -67,13 +79,7 @@ public class S3ImageServiceTests {
|
||||
public void simpleCreate() {
|
||||
try (final InputStream hal9000 = getHal9000()) {
|
||||
final User owner = this.createTestUser("imageOwner");
|
||||
final Image image = this.imageService.create(
|
||||
owner,
|
||||
"HAL9000.svg",
|
||||
hal9000,
|
||||
"image/svg+xml",
|
||||
27881L
|
||||
);
|
||||
final Image image = this.createHal9000(owner, hal9000);
|
||||
assertThat(image.getOwner(), isUser(owner));
|
||||
assertThat(image.getCreated(), is(notNullValue()));
|
||||
assertThat(image.getModified(), is(nullValue()));
|
||||
@ -94,13 +100,7 @@ public class S3ImageServiceTests {
|
||||
public void loadImageWithOwner() {
|
||||
try (final InputStream hal9000 = getHal9000()) {
|
||||
final User owner = this.createTestUser("imageOwner");
|
||||
final Image image = this.imageService.create(
|
||||
owner,
|
||||
"HAL9000.svg",
|
||||
hal9000,
|
||||
"image/svg+xml",
|
||||
27881L
|
||||
);
|
||||
final Image image = this.createHal9000(owner, hal9000);
|
||||
try (final InputStream stored = this.imageService.getImageContentById(image.getId(), owner)) {
|
||||
final byte[] storedBytes = stored.readAllBytes();
|
||||
assertThat(storedBytes.length, is(27881));
|
||||
@ -114,13 +114,7 @@ public class S3ImageServiceTests {
|
||||
public void loadPublicImage() {
|
||||
try (final InputStream hal9000 = getHal9000()) {
|
||||
final User owner = this.createTestUser("imageOwner");
|
||||
Image image = this.imageService.create(
|
||||
owner,
|
||||
"HAL9000.svg",
|
||||
hal9000,
|
||||
"image/svg+xml",
|
||||
27881L
|
||||
);
|
||||
Image image = this.createHal9000(owner, hal9000);
|
||||
image = this.imageService.setPublic(image, owner, true);
|
||||
try (final InputStream stored = this.imageService.getImageContentById(image.getId())) {
|
||||
final byte[] storedBytes = stored.readAllBytes();
|
||||
@ -137,13 +131,7 @@ public class S3ImageServiceTests {
|
||||
try (final InputStream hal9000 = getHal9000()) {
|
||||
final User owner = this.createTestUser("imageOwner");
|
||||
final User viewer = this.createTestUser("imageViewer");
|
||||
Image image = this.imageService.create(
|
||||
owner,
|
||||
"HAL9000.svg",
|
||||
hal9000,
|
||||
"image/svg+xml",
|
||||
27881L
|
||||
);
|
||||
Image image = this.createHal9000(owner, hal9000);
|
||||
image = this.imageService.addViewer(image, owner, viewer);
|
||||
try (final InputStream stored = this.imageService.getImageContentById(image.getId(), viewer)) {
|
||||
final byte[] storedBytes = stored.readAllBytes();
|
||||
@ -154,4 +142,30 @@ public class S3ImageServiceTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void getImagesOwnedBy() {
|
||||
try (
|
||||
final InputStream hal9000_0 = getHal9000();
|
||||
final InputStream hal9000_1 = getHal9000();
|
||||
final InputStream hal9000_2 = getHal9000();
|
||||
) {
|
||||
final User owner = this.createTestUser("imageOwner");
|
||||
final User otherOwner = this.createTestUser("otherImageOwner");
|
||||
final Image image0 = this.createHal9000(owner, hal9000_0);
|
||||
final Image image1 = this.createHal9000(owner, hal9000_1);
|
||||
final Image image2 = this.createHal9000(otherOwner, hal9000_2);
|
||||
|
||||
final List<Image> ownedImages = this.imageService.getImagesOwnedBy(owner);
|
||||
assertThat(ownedImages.size(), is(2));
|
||||
assertThat(ownedImages, containsImages(image0, image1));
|
||||
|
||||
final List<Image> otherOwnedImages = this.imageService.getImagesOwnedBy(otherOwner);
|
||||
assertThat(otherOwnedImages.size(), is(1));
|
||||
assertThat(otherOwnedImages, containsImages(image2));
|
||||
} catch (IOException | ImageException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.user.IsUserMatcher;
|
||||
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
@ -12,7 +13,6 @@ import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static app.mealsmadeeasy.api.matchers.Matchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
@ -115,7 +115,7 @@ public class RecipeServiceTests {
|
||||
recipe = this.recipeService.setPublic(recipe, owner, true);
|
||||
final RecipeStar star = this.recipeService.addStar(recipe, owner);
|
||||
final Recipe byIdWithStars = this.recipeService.getByIdWithStars(recipe.getId());
|
||||
assertThat(byIdWithStars.getStars(), containsStars(star));
|
||||
assertThat(byIdWithStars.getStars(), ContainsRecipeStarsMatcher.containsStars(star));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -174,9 +174,9 @@ public class RecipeServiceTests {
|
||||
assertThat(oneStar.size(), is(2));
|
||||
assertThat(twoStars.size(), is(1));
|
||||
|
||||
assertThat(zeroStars, containsRecipes(r0, r1, r2));
|
||||
assertThat(oneStar, containsRecipes(r1, r2));
|
||||
assertThat(twoStars, containsRecipes(r2));
|
||||
assertThat(zeroStars, ContainsRecipesMatcher.containsRecipes(r0, r1, r2));
|
||||
assertThat(oneStar, ContainsRecipesMatcher.containsRecipes(r1, r2));
|
||||
assertThat(twoStars, ContainsRecipesMatcher.containsRecipes(r2));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -223,9 +223,9 @@ public class RecipeServiceTests {
|
||||
assertThat(oneStarViewable.size(), is(2));
|
||||
assertThat(twoStarsViewable.size(), is (1));
|
||||
|
||||
assertThat(zeroStarsViewable, containsRecipes(r0, r1, r2));
|
||||
assertThat(oneStarViewable, containsRecipes(r1, r2));
|
||||
assertThat(twoStarsViewable, containsRecipes(r2));
|
||||
assertThat(zeroStarsViewable, ContainsRecipesMatcher.containsRecipes(r0, r1, r2));
|
||||
assertThat(oneStarViewable, ContainsRecipesMatcher.containsRecipes(r1, r2));
|
||||
assertThat(twoStarsViewable, ContainsRecipesMatcher.containsRecipes(r2));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -241,7 +241,7 @@ public class RecipeServiceTests {
|
||||
|
||||
final List<Recipe> publicRecipes = this.recipeService.getPublicRecipes();
|
||||
assertThat(publicRecipes.size(), is(2));
|
||||
assertThat(publicRecipes, containsRecipes(r0, r1));
|
||||
assertThat(publicRecipes, ContainsRecipesMatcher.containsRecipes(r0, r1));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -254,7 +254,7 @@ public class RecipeServiceTests {
|
||||
r0 = this.recipeService.addViewer(r0, viewer);
|
||||
final List<Recipe> viewableRecipes = this.recipeService.getRecipesViewableBy(viewer);
|
||||
assertThat(viewableRecipes.size(), is(1));
|
||||
assertThat(viewableRecipes, containsRecipes(r0));
|
||||
assertThat(viewableRecipes, ContainsRecipesMatcher.containsRecipes(r0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -264,7 +264,7 @@ public class RecipeServiceTests {
|
||||
final Recipe r0 = this.createTestRecipe(owner);
|
||||
final List<Recipe> ownedRecipes = this.recipeService.getRecipesOwnedBy(owner);
|
||||
assertThat(ownedRecipes.size(), is(1));
|
||||
assertThat(ownedRecipes, containsRecipes(r0));
|
||||
assertThat(ownedRecipes, ContainsRecipesMatcher.containsRecipes(r0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -318,7 +318,7 @@ public class RecipeServiceTests {
|
||||
final User secondOwner = this.createTestUser("secondOwner");
|
||||
Recipe recipe = this.createTestRecipe(firstOwner);
|
||||
recipe = this.recipeService.updateOwner(recipe, firstOwner, secondOwner);
|
||||
assertThat(recipe.getOwner(), isUser(secondOwner));
|
||||
assertThat(recipe.getOwner(), IsUserMatcher.isUser(secondOwner));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -339,8 +339,8 @@ public class RecipeServiceTests {
|
||||
Recipe recipe = this.createTestRecipe(owner);
|
||||
recipe = this.recipeService.addViewer(recipe, starer);
|
||||
final RecipeStar star = this.recipeService.addStar(recipe, starer);
|
||||
assertThat(star.getRecipe(), isRecipe(recipe));
|
||||
assertThat(star.getOwner(), isUser(starer));
|
||||
assertThat(star.getRecipe(), IsRecipeMatcher.isRecipe(recipe));
|
||||
assertThat(star.getOwner(), IsUserMatcher.isUser(starer));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1,13 +1,18 @@
|
||||
package app.mealsmadeeasy.api.image;
|
||||
|
||||
import app.mealsmadeeasy.api.user.UserEntity;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ImageRepository extends JpaRepository<ImageEntity, Long> {
|
||||
|
||||
@Query("SELECT image FROM Image image WHERE image.id = ?1")
|
||||
@EntityGraph(attributePaths = { "viewers" })
|
||||
ImageEntity getByIdWithViewers(long id);
|
||||
|
||||
|
||||
List<ImageEntity> findAllByOwner(UserEntity owner);
|
||||
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -89,7 +90,7 @@ public class S3ImageService implements ImageService {
|
||||
|
||||
@Override
|
||||
public List<Image> getImagesOwnedBy(User user) {
|
||||
return List.of();
|
||||
return new ArrayList<>(this.imageRepository.findAllByOwner((UserEntity) user));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,17 @@
|
||||
package app.mealsmadeeasy.api.image;
|
||||
|
||||
import app.mealsmadeeasy.api.matchers.ContainsItemsMatcher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class ContainsImagesMatcher extends ContainsItemsMatcher<Image, Long> {
|
||||
|
||||
public static ContainsImagesMatcher containsImages(Image... expected) {
|
||||
return new ContainsImagesMatcher(expected);
|
||||
}
|
||||
|
||||
private ContainsImagesMatcher(Image... allExpected) {
|
||||
super(List.of(allExpected), o -> o instanceof Image, Image::getId);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package app.mealsmadeeasy.api.matchers;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class ContainsItemsMatcher<T, ID> extends BaseMatcher<Iterable<T>> {
|
||||
|
||||
private final List<T> allExpected;
|
||||
private final Predicate<Object> isT;
|
||||
private final Function<T, ID> idFunction;
|
||||
|
||||
public ContainsItemsMatcher(List<T> allExpected, Predicate<Object> isT, Function<T, ID> idFunction) {
|
||||
this.allExpected = allExpected;
|
||||
this.isT = isT;
|
||||
this.idFunction = idFunction;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public boolean matches(Object o) {
|
||||
if (o instanceof Iterable<?> iterable) {
|
||||
checkExpected:
|
||||
for (final T expected : this.allExpected) {
|
||||
for (final Object item : iterable) {
|
||||
if (
|
||||
this.isT.test(item) && Objects.equals(
|
||||
this.idFunction.apply((T) item),
|
||||
this.idFunction.apply(expected)
|
||||
)
|
||||
) {
|
||||
continue checkExpected;
|
||||
}
|
||||
}
|
||||
// did not find expected in iterable
|
||||
return false;
|
||||
}
|
||||
// found all expected in iterable
|
||||
return true;
|
||||
}
|
||||
// o is not an iterable
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Expected ").appendValue(this.allExpected);
|
||||
}
|
||||
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package app.mealsmadeeasy.api.matchers;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class ContainsRecipesMatcher extends BaseMatcher<List<Recipe>> {
|
||||
|
||||
private final Recipe[] allExpected;
|
||||
|
||||
public ContainsRecipesMatcher(Recipe[] allExpected) {
|
||||
this.allExpected = allExpected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object actual) {
|
||||
if (actual instanceof List<?> list) {
|
||||
checkExpected:
|
||||
for (final Recipe expected : allExpected) {
|
||||
for (final Object item : list) {
|
||||
if (item instanceof Recipe o && Objects.equals(o.getId(), expected.getId())) {
|
||||
continue checkExpected;
|
||||
}
|
||||
}
|
||||
// Did not find the expected in the list
|
||||
return false;
|
||||
}
|
||||
// Found all expected in list
|
||||
return true;
|
||||
}
|
||||
// actual is not a List
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Expected ").appendValue(List.of(this.allExpected));
|
||||
}
|
||||
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
package app.mealsmadeeasy.api.matchers;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class ContainsStarsMatcher extends BaseMatcher<Set<RecipeStar>> {
|
||||
|
||||
private final RecipeStar[] allExpected;
|
||||
|
||||
public ContainsStarsMatcher(RecipeStar[] allExpected) {
|
||||
this.allExpected = allExpected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object actual) {
|
||||
if (actual instanceof Set<?> set) {
|
||||
checkExpected:
|
||||
for (final RecipeStar expected : allExpected) {
|
||||
for (final Object item : set) {
|
||||
if (item instanceof RecipeStar o && Objects.equals(o.getId(), expected.getId())) {
|
||||
continue checkExpected;
|
||||
}
|
||||
}
|
||||
// Did not find the expected in the set
|
||||
return false;
|
||||
}
|
||||
// Found all expected in set
|
||||
return true;
|
||||
}
|
||||
// actual is not a Set
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Expected ").appendValue(Set.of(this.allExpected));
|
||||
}
|
||||
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package app.mealsmadeeasy.api.matchers;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
||||
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
|
||||
public final class Matchers {
|
||||
|
||||
public static ContainsRecipesMatcher containsRecipes(Recipe... expected) {
|
||||
return new ContainsRecipesMatcher(expected);
|
||||
}
|
||||
|
||||
public static ContainsStarsMatcher containsStars(RecipeStar... expected) {
|
||||
return new ContainsStarsMatcher(expected);
|
||||
}
|
||||
|
||||
public static IsRecipeMatcher isRecipe(Recipe expected) {
|
||||
return new IsRecipeMatcher(expected);
|
||||
}
|
||||
|
||||
public static IsUserMatcher isUser(User expected) {
|
||||
return new IsUserMatcher(expected);
|
||||
}
|
||||
|
||||
private Matchers() {}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.matchers.ContainsItemsMatcher;
|
||||
import app.mealsmadeeasy.api.recipe.star.RecipeStar;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ContainsRecipeStarsMatcher extends ContainsItemsMatcher<RecipeStar, Long> {
|
||||
|
||||
public static ContainsRecipeStarsMatcher containsStars(RecipeStar... expected) {
|
||||
return new ContainsRecipeStarsMatcher(expected);
|
||||
}
|
||||
|
||||
private ContainsRecipeStarsMatcher(RecipeStar[] allExpected) {
|
||||
super(List.of(allExpected), o -> o instanceof RecipeStar, RecipeStar::getId);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.matchers.ContainsItemsMatcher;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class ContainsRecipesMatcher extends ContainsItemsMatcher<Recipe, Long> {
|
||||
|
||||
public static ContainsRecipesMatcher containsRecipes(Recipe... expected) {
|
||||
return new ContainsRecipesMatcher(expected);
|
||||
}
|
||||
|
||||
private ContainsRecipesMatcher(Recipe[] allExpected) {
|
||||
super(List.of(allExpected), o -> o instanceof Recipe, Recipe::getId);
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
package app.mealsmadeeasy.api.matchers;
|
||||
package app.mealsmadeeasy.api.recipe;
|
||||
|
||||
import app.mealsmadeeasy.api.recipe.Recipe;
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
|
||||
@ -14,6 +13,10 @@ public class IsRecipeMatcher extends BaseMatcher<Recipe> {
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
public static IsRecipeMatcher isRecipe(Recipe expected) {
|
||||
return new IsRecipeMatcher(expected);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object actual) {
|
||||
return actual instanceof Recipe o && Objects.equals(this.expected.getId(), o.getId());
|
@ -1,6 +1,5 @@
|
||||
package app.mealsmadeeasy.api.matchers;
|
||||
package app.mealsmadeeasy.api.user;
|
||||
|
||||
import app.mealsmadeeasy.api.user.User;
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
|
||||
@ -14,6 +13,10 @@ public final class IsUserMatcher extends BaseMatcher<User> {
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
public static IsUserMatcher isUser(User expected) {
|
||||
return new IsUserMatcher(expected);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object actual) {
|
||||
return actual instanceof User o && Objects.equals(o.getId(), this.expected.getId());
|
Loading…
Reference in New Issue
Block a user