diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/HorseEndpoint.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/HorseEndpoint.java index d3d8404..ce2931b 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/HorseEndpoint.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/HorseEndpoint.java @@ -12,8 +12,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import org.springframework.web.server.ResponseStatusException; +import java.io.IOException; import java.lang.invoke.MethodHandles; @RestController @@ -50,7 +52,7 @@ public class HorseEndpoint { return horseMapper.entityToDto(horseService.addHorse(horseEntity)); } catch (ValidationException e) { LOGGER.error(e.getMessage()); - throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Error during adding new horse: " + e.getMessage(), e); } catch (DataAccessException e) { LOGGER.error(e.getMessage()); @@ -58,4 +60,21 @@ public class HorseEndpoint { "Something went wrong during the communication with the database", e); } } + + @PostMapping(value = "/upload") + @ResponseStatus(HttpStatus.CREATED) + public void addImage(@RequestParam("file") MultipartFile image) { + LOGGER.info("POST " + BASE_URL + "/upload"); + try { + horseService.saveImage(image); + } catch(ValidationException e) { + LOGGER.error(e.getMessage()); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, + "Unsupported file type provided. Supported file types are jpg and png."); + } catch (IOException e) { + LOGGER.error(e.getMessage()); + throw new ResponseStatusException(HttpStatus.UNPROCESSABLE_ENTITY, + "Something went wrong while uploading the file"); + } + } } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/dto/HorseDto.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/dto/HorseDto.java index 892feb3..5a39018 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/dto/HorseDto.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/dto/HorseDto.java @@ -14,26 +14,29 @@ public class HorseDto extends BaseDto { private short score; private ERace race; private Date birthday; + private String imagePath; private Long owner; public HorseDto() {} - public HorseDto(String name, String description, short score, Date birthday, ERace race, Long owner) { + public HorseDto(String name, String description, short score, Date birthday, ERace race, String imagePath, Long owner) { this.name = name; this.description = description; this.score = score; this.birthday = birthday; this.race = race; + this.imagePath = imagePath; this.owner = owner; } - public HorseDto(Long id, String name, String description, short score, Date birthday, ERace race, LocalDateTime created, LocalDateTime updated, Long owner) { + public HorseDto(Long id, String name, String description, short score, Date birthday, ERace race, String imagePath, LocalDateTime created, LocalDateTime updated, Long owner) { super(id, created, updated); this.name = name; this.description = description; this.score = score; this.birthday = birthday; this.race = race; + this.imagePath = imagePath; this.owner = owner; } @@ -77,6 +80,14 @@ public class HorseDto extends BaseDto { this.race = race; } + public String getImagePath() { + return imagePath; + } + + public void setImagePath(String imagePath) { + this.imagePath = imagePath; + } + public Long getOwner() { return owner; } @@ -96,6 +107,7 @@ public class HorseDto extends BaseDto { Objects.equals(score, h.score) && Objects.equals(birthday, h.birthday) && Objects.equals(race, h.race) && + Objects.equals(imagePath, h.imagePath) && Objects.equals(owner, h.owner); } @@ -113,6 +125,7 @@ public class HorseDto extends BaseDto { ", score='" + score + '\'' + ", birthday='" + df.format(birthday) + '\'' + ", race='" + race + '\'' + + ", imagePath='" + imagePath + '\'' + ", owner_id='" + owner + '\''; } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/mapper/HorseMapper.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/mapper/HorseMapper.java index f4470ac..2b15116 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/mapper/HorseMapper.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/endpoint/mapper/HorseMapper.java @@ -8,10 +8,10 @@ import org.springframework.stereotype.Component; @Component public class HorseMapper { public HorseDto entityToDto(Horse horse) { - return new HorseDto(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getRace(), horse.getCreatedAt(), horse.getUpdatedAt(), horse.getOwner()); + return new HorseDto(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getRace(), horse.getImagePath(), horse.getCreatedAt(), horse.getUpdatedAt(), horse.getOwner()); } public Horse dtoToEntity(HorseDto horse) { - return new Horse(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getRace(), horse.getOwner()); + return new Horse(horse.getId(), horse.getName(), horse.getDescription(), horse.getScore(), horse.getBirthday(), horse.getRace(), horse.getImagePath(), horse.getOwner()); } } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/entity/Horse.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/entity/Horse.java index 25868d3..c05113a 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/entity/Horse.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/entity/Horse.java @@ -13,36 +13,40 @@ public class Horse extends BaseEntity { private short score; private ERace race; private Date birthday; + private String imagePath; private Long owner; public Horse() {} - public Horse(String name, String description, short score, Date birthday, ERace race,Long owner) { + public Horse(String name, String description, short score, Date birthday, ERace race, String imagePath, Long owner) { this.name = name; this.description = description; this.score = score; this.birthday = birthday; this.race = race; + this.imagePath = imagePath; this.owner = owner; } - public Horse(Long id, String name, String description, short score, Date birthday, ERace race, Long owner) { + public Horse(Long id, String name, String description, short score, Date birthday, ERace race, String imagePath, Long owner) { super(id); this.name = name; this.description = description; this.score = score; this.birthday = birthday; this.race = race; + this.imagePath = imagePath; this.owner = owner; } - public Horse(Long id, String name, String description, short score, Date birthday, ERace race, Long owner, LocalDateTime created, LocalDateTime updated) { + public Horse(Long id, String name, String description, short score, Date birthday, ERace race, String imagePath, Long owner, LocalDateTime created, LocalDateTime updated) { super(id, created, updated); this.name = name; this.description = description; this.score = score; this.birthday = birthday; this.race = race; + this.imagePath = imagePath; this.owner = owner; } @@ -86,6 +90,14 @@ public class Horse extends BaseEntity { this.race = race; } + public String getImagePath() { + return imagePath; + } + + public void setImagePath(String imagePath) { + this.imagePath = imagePath; + } + public Long getOwner() { return owner; } @@ -105,6 +117,7 @@ public class Horse extends BaseEntity { Objects.equals(score, h.score) && Objects.equals(birthday, h.birthday) && Objects.equals(race, h.race) && + Objects.equals(imagePath, h.imagePath) && Objects.equals(owner, h.owner); } @@ -122,6 +135,7 @@ public class Horse extends BaseEntity { ", score='" + score + '\'' + ", birthday='" + df.format(birthday) + '\'' + ", race='" + race + '\'' + + ", imagePath='" + imagePath + '\'' + ", owner='" + owner + '\''; } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/FileDao.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/FileDao.java new file mode 100644 index 0000000..d32bbc1 --- /dev/null +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/FileDao.java @@ -0,0 +1,13 @@ +package at.ac.tuwien.sepm.assignment.individual.persistence; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; + +public interface FileDao { + /** + * Used for saving files on the local file system + * @param file file to save + */ + void save(MultipartFile file) throws IOException; +} diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseFileDao.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseFileDao.java new file mode 100644 index 0000000..dae30de --- /dev/null +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseFileDao.java @@ -0,0 +1,52 @@ +package at.ac.tuwien.sepm.assignment.individual.persistence.impl; + +import at.ac.tuwien.sepm.assignment.individual.persistence.FileDao; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Repository; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.nio.file.FileSystemException; +import java.nio.file.Files; + +@Repository +public class HorseFileDao implements FileDao { + @Value("${spring.upload.path}") + private String FILE_BASE_PATH; + private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + public HorseFileDao() {}; + + @Override + public void save(MultipartFile file) throws IOException { + if(file == null || file.getOriginalFilename() == null || file.isEmpty()) + throw new IOException("Cannot save an empty file"); + + if(!file.getContentType().equals("image/jpeg") && !file.getContentType().equals("image/png")) + throw new IOException("Unsupported file type"); + + String fileName = file.getOriginalFilename(); + LOGGER.trace("Writing file to " + FILE_BASE_PATH + fileName); + + try { + // Create directory if it doesn't exist + File uploadDir = new File(FILE_BASE_PATH); + if(!uploadDir.exists()) + uploadDir.mkdir(); + + // Save the file + if(uploadDir.exists()) { + File target = new File(FILE_BASE_PATH + fileName); + Files.write(target.toPath(), file.getBytes()); + } else { + throw new FileSystemException("Base upload directory " + FILE_BASE_PATH + " does not exist"); + } + } catch (Exception e) { + LOGGER.error(e.getMessage()); + throw new IOException("Saving the file failed"); + } + } +} diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseJdbcDao.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseJdbcDao.java index 6df00f3..b37ae2a 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseJdbcDao.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/persistence/impl/HorseJdbcDao.java @@ -50,11 +50,11 @@ public class HorseJdbcDao implements HorseDao { @Override public Horse addHorse(Horse horse) throws DataAccessException { LOGGER.trace("Add horse {}", horse.toString()); - String sql = "INSERT INTO " + TABLE_NAME + "(name, description, score, birthday, race, owner_id, created_at, updated_at) VALUES(?, ?, ?, ?, ?, ?, ?, ?)"; + String sql = "INSERT INTO " + TABLE_NAME + "(name, description, score, birthday, race, image_path, owner_id, created_at, updated_at) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)"; try { // Check if the constraints are violated - if(horse.getName() == null || horse.getScore() == 0 || horse.getBirthday() == null || horse.getRace() == null) + if(horse.getName() == null || horse.getScore() == 0 || horse.getBirthday() == null || horse.getRace() == null || horse.getImagePath() == null) throw new DataIntegrityViolationException("Required parameters for horse missing"); LocalDateTime currentTime = LocalDateTime.now(); @@ -74,13 +74,15 @@ public class HorseJdbcDao implements HorseDao { ps.setDate(4, horse.getBirthday()); ps.setString(5, horse.getRace().toString()); // Convert to string to be able to save in DB - if(horse.getOwner() == null || horse.getOwner() == 0) - ps.setNull(6, Types.NULL); - else - ps.setObject(6, horse.getOwner()); + ps.setString(6, horse.getImagePath()); - ps.setObject(7, horse.getCreatedAt()); - ps.setObject(8, horse.getUpdatedAt()); + if(horse.getOwner() == null || horse.getOwner() == 0) + ps.setNull(7, Types.NULL); + else + ps.setObject(7, horse.getOwner()); + + ps.setObject(8, horse.getCreatedAt()); + ps.setObject(9, horse.getUpdatedAt()); return ps; }, keyHolder); @@ -98,6 +100,7 @@ public class HorseJdbcDao implements HorseDao { } } + private Horse mapRow(ResultSet resultSet, int i) throws SQLException { final Horse horse = new Horse(); horse.setId(resultSet.getLong("id")); @@ -107,6 +110,7 @@ public class HorseJdbcDao implements HorseDao { horse.setBirthday(resultSet.getDate("birthday")); horse.setRace(ERace.valueOf(resultSet.getString("race"))); // Convert to Enum for usage in objects horse.setOwner(resultSet.getLong("owner_id")); + horse.setImagePath(resultSet.getString("image_path")); horse.setCreatedAt(resultSet.getTimestamp("created_at").toLocalDateTime()); horse.setUpdatedAt(resultSet.getTimestamp("updated_at").toLocalDateTime()); return horse; diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/HorseService.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/HorseService.java index 524a7d5..208753a 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/HorseService.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/HorseService.java @@ -2,6 +2,9 @@ package at.ac.tuwien.sepm.assignment.individual.service; import at.ac.tuwien.sepm.assignment.individual.entity.Horse; import at.ac.tuwien.sepm.assignment.individual.exception.NotFoundException; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; public interface HorseService { /** @@ -19,4 +22,10 @@ public interface HorseService { * @throws NotFoundException will be thrown if the horse could not be found in the system. */ Horse addHorse(Horse horse); + + /** + * @param img image to upload + * @throws IOException will be thrown if something goes wrong with saving the file + */ + void saveImage(MultipartFile img) throws IOException; } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/impl/SimpleHorseService.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/impl/SimpleHorseService.java index 6bd057b..96bd78b 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/impl/SimpleHorseService.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/service/impl/SimpleHorseService.java @@ -1,6 +1,7 @@ package at.ac.tuwien.sepm.assignment.individual.service.impl; import at.ac.tuwien.sepm.assignment.individual.entity.Horse; +import at.ac.tuwien.sepm.assignment.individual.persistence.FileDao; import at.ac.tuwien.sepm.assignment.individual.persistence.HorseDao; import at.ac.tuwien.sepm.assignment.individual.service.HorseService; import at.ac.tuwien.sepm.assignment.individual.util.ValidationException; @@ -10,32 +11,42 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.lang.invoke.MethodHandles; -import java.text.SimpleDateFormat; @Service public class SimpleHorseService implements HorseService { private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - private final HorseDao horseDao; + private final HorseDao horseJdbcDao; + private final FileDao horseFileDao; private final Validator validator; @Autowired - public SimpleHorseService(HorseDao horseDao, Validator validator) { - this.horseDao = horseDao; + public SimpleHorseService(HorseDao horseJdbcDao, FileDao horseFileDao, Validator validator) { + this.horseJdbcDao = horseJdbcDao; + this.horseFileDao = horseFileDao; this.validator = validator; } @Override public Horse findOneById(Long id) { LOGGER.trace("findOneById({})", id); - return horseDao.findOneById(id); + return horseJdbcDao.findOneById(id); } @Override public Horse addHorse(Horse horse) throws ValidationException, DataAccessException { this.validator.validateNewHorse(horse); LOGGER.trace("addHorse({})", horse.toString()); - return horseDao.addHorse(horse); + return horseJdbcDao.addHorse(horse); + } + + @Override + public void saveImage(MultipartFile img) throws ValidationException, IOException { + this.validator.validateHorseImage(img); + LOGGER.trace("saveImage({})", img.getName()); + horseFileDao.save(img); } } diff --git a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/util/Validator.java b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/util/Validator.java index 523c960..45e1432 100644 --- a/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/util/Validator.java +++ b/backend/src/main/java/at/ac/tuwien/sepm/assignment/individual/util/Validator.java @@ -3,6 +3,7 @@ package at.ac.tuwien.sepm.assignment.individual.util; import at.ac.tuwien.sepm.assignment.individual.entity.Horse; import at.ac.tuwien.sepm.assignment.individual.entity.Owner; import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; @Component public class Validator { @@ -22,5 +23,18 @@ public class Validator { if(horse.getScore() > 5 || horse.getScore() < 1) { throw new ValidationException("Score value " + horse.getScore() + " not allowed. The score must be an integer between 1 and 5"); } + if(!horse.getImagePath().endsWith(".png") && !horse.getImagePath().endsWith(".jpg") && !horse.getImagePath().endsWith(".jpeg")) { + throw new ValidationException("Unsupported file type supplied. Supported file types are jpg and png"); + } } + + public void validateHorseImage(MultipartFile image) throws ValidationException { + if(image == null || image.getContentType() == null || image.isEmpty()) { + throw new ValidationException("No image supplied"); + } + if(!image.getContentType().equals("image/png") && !image.getContentType().equals("image/jpeg")) { + throw new ValidationException("Unsupported file type supplied: " + image.getContentType()); + } + } + } diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index ea8dc3e..1081fcb 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -15,5 +15,12 @@ spring: h2: console: enabled: true + upload: + path: /tmp/uploads/ + servlet: + multipart: + enabled: true + max-file-size: 10MB + max-request-size: 15MB server: port: 8080 diff --git a/backend/src/main/resources/sql/createSchema.sql b/backend/src/main/resources/sql/createSchema.sql index 4b1d5b2..727d61a 100644 --- a/backend/src/main/resources/sql/createSchema.sql +++ b/backend/src/main/resources/sql/createSchema.sql @@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS horse score TINYINT NOT NULL CHECK(score >= 1 AND score <= 5), birthday DATE NOT NULL, race RACE NOT NULL, + image_path VARCHAR(255) NOT NULL, created_at DATETIME NOT NULL, updated_at DATETIME NOT NULL ); diff --git a/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/integration/HorseEndpointTest.java b/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/integration/HorseEndpointTest.java index 973b5a7..ddcea3c 100644 --- a/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/integration/HorseEndpointTest.java +++ b/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/integration/HorseEndpointTest.java @@ -6,20 +6,25 @@ import at.ac.tuwien.sepm.assignment.individual.endpoint.dto.HorseDto; import at.ac.tuwien.sepm.assignment.individual.enums.ERace; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.*; import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.event.annotation.AfterTestMethod; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; +import java.io.File; import java.sql.Date; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") public class HorseEndpointTest { + @Value("${spring.upload.path}") + private String FILE_BASE_PATH; private static final RestTemplate REST_TEMPLATE = new RestTemplate(); private static final String BASE_URL = "http://localhost:"; private static final String HORSE_URL = "/horses"; @@ -31,7 +36,7 @@ public class HorseEndpointTest { @DisplayName("Adding a new horse with the correct parameters will return HTTP 201 and the new HorseDto") public void addingNewHorse_correctParameters_shouldReturnStatus201AndHorse() { String birthday = "2020-01-01"; - HorseDto newHorse = new HorseDto("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, null); + HorseDto newHorse = new HorseDto("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, "files/test.png", null); HttpEntity request = new HttpEntity<>(newHorse); ResponseEntity response = REST_TEMPLATE @@ -43,6 +48,32 @@ public class HorseEndpointTest { assertEquals(newHorse.getDescription(), response.getBody().getDescription()); assertEquals(newHorse.getScore(), response.getBody().getScore()); assertEquals(newHorse.getBirthday().toString(), response.getBody().getBirthday().toString()); + assertEquals(newHorse.getImagePath(), response.getBody().getImagePath()); assertEquals(newHorse.getOwner(), response.getBody().getOwner()); } + + @Test + @DisplayName("Uploading an image in the correct format will return HTTP 201") + public void addingNewImage_correctFormat_shouldReturnStatus201() { + try { + // More information: + // https://www.baeldung.com/spring-rest-template-multipart-upload + // https://github.com/spring-guides/gs-uploading-files/blob/master/complete/src/test/java/com/example/uploadingfiles/FileUploadIntegrationTests.java + + // Get the file + ClassPathResource image = new ClassPathResource("horse.jpg", getClass()); + + // Set the body + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("file", image); + + // Create the HTTP Entity + HttpEntity> request = new HttpEntity<>(body); + + ResponseEntity response = REST_TEMPLATE.postForEntity(BASE_URL + port + HORSE_URL + "/upload", request, String.class); + assertEquals(HttpStatus.CREATED, response.getStatusCode()); + } finally { + new File(FILE_BASE_PATH + "horse.jpg").delete(); + } + } } diff --git a/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/persistence/HorseDaoTestBase.java b/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/persistence/HorseDaoTestBase.java index 27e99f5..2b66366 100644 --- a/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/persistence/HorseDaoTestBase.java +++ b/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/persistence/HorseDaoTestBase.java @@ -21,7 +21,7 @@ public abstract class HorseDaoTestBase { @DisplayName("Adding a new horse with the correct parameters should return the horse") public void addingNewHorse_correctParameters_shouldReturnHorse() { String birthday = "2020-01-01"; - Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, null); + Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, "files/test.png", null); Horse savedHorse = horseDao.addHorse(newHorse); assertEquals(newHorse, savedHorse); } @@ -30,7 +30,7 @@ public abstract class HorseDaoTestBase { @DisplayName("Adding a new horse with the incorrect parameters should throw DataAccessException") public void addingNewHorse_incorrectParameters_shouldThrowDataAccess() { String birthday = "2020-01-01"; - Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 80, Date.valueOf(birthday), null, null); + Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 80, Date.valueOf(birthday), null, null, null); assertThrows(DataAccessException.class, () -> horseDao.addHorse(newHorse)); } diff --git a/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/persistence/HorseFileDaoTest.java b/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/persistence/HorseFileDaoTest.java new file mode 100644 index 0000000..f07b9eb --- /dev/null +++ b/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/persistence/HorseFileDaoTest.java @@ -0,0 +1,50 @@ +package at.ac.tuwien.sepm.assignment.individual.unit.persistence; + +import static org.junit.jupiter.api.Assertions.*; + +import at.ac.tuwien.sepm.assignment.individual.persistence.impl.HorseFileDao; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ActiveProfiles; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +@SpringBootTest +@ActiveProfiles("test") +public class HorseFileDaoTest { + @Value("${spring.upload.path}") + private String FILE_BASE_PATH; + @Autowired + HorseFileDao horseFileDao; + + @Test + @DisplayName("Creating a file with the correct type should result in a saved file") + public void savingFile_correctType_shouldWriteFile() throws FileNotFoundException, IOException { + File image = new File("src/test/resources/at/ac/tuwien/sepm/assignment/individual/integration/horse.jpg"); + horseFileDao.save(new MockMultipartFile("file", image.getName(), MediaType.IMAGE_JPEG_VALUE, new FileInputStream(image))); + File savedImage = new File(FILE_BASE_PATH + "horse.jpg"); + assertTrue(savedImage.exists()); + assertTrue(savedImage.isFile()); + } + + @Test + @DisplayName("Creating a file with the correct type should result in a saved file") + public void savingFile_incorrectType_shouldThrowIOException() throws FileNotFoundException, IOException { + File image = new File("src/test/resources/at/ac/tuwien/sepm/assignment/individual/integration/horse.jpg"); + assertThrows(IOException.class, () -> horseFileDao.save(new MockMultipartFile("file", image.getName(), MediaType.TEXT_HTML_VALUE, new FileInputStream(image)))); + } + + @AfterEach + public void cleanup() { + new File(FILE_BASE_PATH + "horse.jpg").delete(); + } +} diff --git a/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/service/HorseServiceTest.java b/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/service/HorseServiceTest.java index a229d05..11409de 100644 --- a/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/service/HorseServiceTest.java +++ b/backend/src/test/java/at/ac/tuwien/sepm/assignment/individual/unit/service/HorseServiceTest.java @@ -29,7 +29,7 @@ public class HorseServiceTest { @DisplayName("Adding a new horse with the correct parameters will return the new horse") public void addingNewHorse_correctParameters_shouldReturnHorse() { String birthday = "2020-01-01"; - Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, null); + Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 4, Date.valueOf(birthday), ERace.APPALOOSA, "files/test.png", null); Horse savedHorse = horseService.addHorse(newHorse); assertEquals(newHorse, savedHorse); } @@ -38,7 +38,7 @@ public class HorseServiceTest { @DisplayName("Adding a new horse with the incorrect parameters will throw a ValidationException") public void addingNewHorse_incorrectParameters_shouldThrowValidation() { String birthday = "2020-01-01"; - Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 80, Date.valueOf(birthday), null, null); + Horse newHorse = new Horse("Zephyr", "Nice horse", (short) 80, Date.valueOf(birthday), null, null, null); assertThrows(ValidationException.class, () -> horseService.addHorse(newHorse)); } } diff --git a/backend/src/test/resources/at/ac/tuwien/sepm/assignment/individual/integration/horse.jpg b/backend/src/test/resources/at/ac/tuwien/sepm/assignment/individual/integration/horse.jpg new file mode 100644 index 0000000..32110ac Binary files /dev/null and b/backend/src/test/resources/at/ac/tuwien/sepm/assignment/individual/integration/horse.jpg differ