package app.mealsmadeeasy.api.image; import app.mealsmadeeasy.api.IntegrationTestsExtension; import app.mealsmadeeasy.api.image.spec.ImageCreateInfoSpec; import app.mealsmadeeasy.api.image.spec.ImageUpdateInfoSpec; import app.mealsmadeeasy.api.user.User; import app.mealsmadeeasy.api.user.UserCreateException; import app.mealsmadeeasy.api.user.UserService; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.MinIOContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Set; import java.util.UUID; import static app.mealsmadeeasy.api.image.ContainsImagesMatcher.containsImages; import static app.mealsmadeeasy.api.user.ContainsUsersMatcher.containsUsers; 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; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThrows; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; @Testcontainers @SpringBootTest @ExtendWith(IntegrationTestsExtension.class) public class S3ImageServiceTests { @Container private static final MinIOContainer container = new MinIOContainer( DockerImageName.parse("minio/minio:latest") ); @DynamicPropertySource public static void minioProperties(DynamicPropertyRegistry registry) { registry.add("app.mealsmadeeasy.api.minio.endpoint", container::getS3URL); registry.add("app.mealsmadeeasy.api.minio.accessKey", container::getUserName); registry.add("app.mealsmadeeasy.api.minio.secretKey", container::getPassword); } private static InputStream getHal9000InputStream() { return S3ImageServiceTests.class.getResourceAsStream("HAL9000.svg"); } @Autowired private UserService userService; @Autowired private ImageService imageService; private static final String TEST_PASSWORD = "test"; private static final long HAL_LENGTH = 27881L; private User seedUser() { final String uuid = UUID.randomUUID().toString(); try { return this.userService.createUser(uuid, uuid + "@test.com", TEST_PASSWORD); } catch (UserCreateException e) { throw new RuntimeException(e); } } private static String makeUserFilename() { return UUID.randomUUID() + ".svg"; } private Image seedImage(User owner, ImageCreateInfoSpec spec) { try (final InputStream hal9000 = getHal9000InputStream()) { return this.imageService.create( owner, makeUserFilename(), hal9000, HAL_LENGTH, spec ); } catch (ImageException | IOException e) { throw new RuntimeException(e); } } private Image seedImage(User owner) { return this.seedImage(owner, new ImageCreateInfoSpec()); } private Image makePublic(Image image, User modifier) { final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec(); spec.setPublic(true); return this.imageService.update(image, modifier, spec); } @Test public void smokeScreen() {} @Test public void simpleCreate() { final User owner = this.seedUser(); final Image image = this.seedImage(owner); assertThat(image.getOwner(), isUser(owner)); assertThat(image.getCreated(), is(notNullValue())); assertThat(image.getModified(), is(nullValue())); assertThat(image.getUserFilename(), is(notNullValue())); assertThat(image.getMimeType(), is("image/svg+xml")); assertThat(image.getAlt(), is(nullValue())); assertThat(image.getCaption(), is(nullValue())); assertThat(image.isPublic(), is(false)); assertThat(image.getViewers(), is(empty())); } @Test public void properlyLoadsContent() throws IOException { final User owner = this.seedUser(); final Image image = this.seedImage(owner); final InputStream content = assertDoesNotThrow(() -> this.imageService.getImageContent(image, owner)); //noinspection DataFlowIssue final byte[] contentBytes = content.readAllBytes(); assertThat(contentBytes.length, is((int) HAL_LENGTH)); content.close(); } @Test public void loadImageWithOwnerAsViewer() throws IOException { final User owner = this.seedUser(); final Image image = this.seedImage(owner); final InputStream content = assertDoesNotThrow(() -> this.imageService.getImageContent(image, owner)); //noinspection DataFlowIssue content.close(); } @Test public void loadPublicImage() throws IOException { final User owner = this.seedUser(); final Image seedImage = this.seedImage(owner); final Image publicImage = this.makePublic(seedImage, owner); final InputStream content = assertDoesNotThrow(() -> this.imageService.getImageContent(publicImage, null)); //noinspection DataFlowIssue content.close(); } @Test public void loadImageWithViewer() throws IOException { final User owner = this.seedUser(); final User viewer = this.seedUser(); Image seedImage = this.seedImage(owner); final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec(); spec.setViewersToAdd(Set.of(viewer)); final Image imageWithViewer = this.imageService.update(seedImage, owner, spec); final InputStream content = assertDoesNotThrow(() -> this.imageService.getImageContent(imageWithViewer, viewer)); //noinspection DataFlowIssue content.close(); } @Test public void getImagesOwnedBy() { final User owner = this.seedUser(); final User otherOwner = this.seedUser(); final Image image0 = this.seedImage(owner); final Image image1 = this.seedImage(owner); final Image image2 = this.seedImage(otherOwner); final List ownedImages = this.imageService.getImagesOwnedBy(owner); assertThat(ownedImages.size(), is(2)); assertThat(ownedImages, containsImages(image0, image1)); final List otherOwnedImages = this.imageService.getImagesOwnedBy(otherOwner); assertThat(otherOwnedImages.size(), is(1)); assertThat(otherOwnedImages, containsImages(image2)); } @Test public void updateAlt() { final User owner = this.seedUser(); Image image = this.seedImage(owner); final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec(); spec.setAlt("HAL 9000"); image = this.imageService.update(image, owner, spec); assertThat(image.getAlt(), is("HAL 9000")); } @Test public void updateCaption() { final User owner = this.seedUser(); Image image = this.seedImage(owner); final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec(); spec.setCaption("HAL 9000 from 2001: A Space Odyssey"); image = this.imageService.update(image, owner, spec); assertThat(image.getCaption(), is("HAL 9000 from 2001: A Space Odyssey")); } @Test public void updateIsPublic() { final User owner = this.seedUser(); Image image = this.seedImage(owner); final ImageUpdateInfoSpec spec = new ImageUpdateInfoSpec(); spec.setPublic(true); image = this.imageService.update(image, owner, spec); assertThat(image.isPublic(), is(true)); } private Image addViewer(Image image, User owner, User viewer) { final ImageUpdateInfoSpec spec0 = new ImageUpdateInfoSpec(); spec0.setViewersToAdd(Set.of(viewer)); return this.imageService.update(image, owner, spec0); } @Test public void addViewers() { final User owner = this.seedUser(); final User viewer = this.seedUser(); Image image = this.seedImage(owner); image = this.addViewer(image, owner, viewer); assertThat(image.getViewers(), containsUsers(viewer)); } @Test public void removeViewers() { final User owner = this.seedUser(); final User viewer = this.seedUser(); Image image = this.seedImage(owner); image = this.addViewer(image, owner, viewer); assertThat(image.getViewers(), containsUsers(viewer)); final ImageUpdateInfoSpec spec1 = new ImageUpdateInfoSpec(); spec1.setViewersToRemove(Set.of(viewer)); image = this.imageService.update(image, owner, spec1); assertThat(image.getViewers(), empty()); } @Test public void clearAllViewers() { final User owner = this.seedUser(); final User viewer = this.seedUser(); Image image = this.seedImage(owner); image = this.addViewer(image, owner, viewer); assertThat(image.getViewers(), containsUsers(viewer)); final ImageUpdateInfoSpec spec1 = new ImageUpdateInfoSpec(); spec1.setClearAllViewers(true); image = this.imageService.update(image, owner, spec1); assertThat(image.getViewers(), empty()); } @Test public void deleteImage() throws Exception { final User owner = this.seedUser(); final Image image = this.seedImage(owner); this.imageService.deleteImage(image, owner); assertThrows(ImageException.class, () -> this.imageService.getById(image.getId(), owner)); } }