Merge branch 'bug-solving' into 'master'

Correction d'un bug qui retournait une erreur 404 lorsque la fiche signalitique d'un produit existait

Closes #54

See merge request color-recipes-explorer/backend!21
This commit is contained in:
William Nolin 2021-04-01 00:10:35 +00:00
commit ac4be0b330
4 changed files with 281 additions and 17 deletions

View File

@ -4,7 +4,6 @@ import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
import org.slf4j.Logger
import org.springframework.core.io.ResourceLoader
import org.springframework.stereotype.Service
import org.springframework.util.FileCopyUtils
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.io.IOException
@ -30,23 +29,19 @@ class FileService(
}
/** Reads the given [stream] as a [String]. */
fun readInputStreamAsString(stream: InputStream) = with(FileCopyUtils.copyToByteArray(stream)) {
fun readInputStreamAsString(stream: InputStream) = with(stream.readAllBytes()) {
String(this, StandardCharsets.UTF_8)
}
/** Reads the file at the given [path] as a [ByteArray]. */
fun readAsBytes(path: String) =
Files.readAllBytes(Paths.get(path))
withFileAt(path) { this.readBytes() }
/** Reads the file at the given [path] as a [List] of [String]. */
fun readAsStrings(path: String) =
Files.readAllLines(Paths.get(path))
/** Write the given [multipartFile] to the file at the given [path]. */
/** Writes the given [multipartFile] to the file at the given [path]. */
fun write(multipartFile: MultipartFile, path: String): Boolean =
if (multipartFile.size <= 0) true
else try {
multipartFile.transferTo(create(path).toPath())
multipartFile.transferTo(create(path))
true
} catch (ex: IOException) {
logger.error("Unable to write multipart file", ex)
@ -82,7 +77,7 @@ class FileService(
/** Runs the given [block] in the context of a file with the given [path]. */
fun <T> withFileAt(path: String, block: File.() -> T) =
with(File(path)) { block() }
File(path).block()
fun getPath(fileName: String): String =
"${creProperties.workingDirectory}/$fileName"

View File

@ -9,12 +9,11 @@ import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile
import java.io.IOException
private const val SIMDUT_DIRECTORY = "simdut"
const val SIMDUT_DIRECTORY = "simdut"
@Service
class SimdutService(
private val fileService: FileService,
@Lazy private val materialService: MaterialService,
private val logger: Logger
) {
/** Checks if the given [material] has a SIMDUT file. */
@ -24,7 +23,7 @@ class SimdutService(
/** Reads the SIMDUT file of the given [material]. */
fun read(material: Material): ByteArray {
val path = getPath(material)
if (fileService.exists(path)) return ByteArray(0)
if (!fileService.exists(path)) return ByteArray(0)
return try {
fileService.readAsBytes(path)
@ -34,10 +33,6 @@ class SimdutService(
}
}
/** Reads the SIMDUT file of the material with the given [id]. */
fun read(id: Long) =
read(materialService.getById(id))
/** Writes the given [simdut] file for the given [material] to the disk. */
fun write(material: Material, simdut: MultipartFile) {
if (!fileService.write(simdut, getPath(material)))

View File

@ -0,0 +1,113 @@
package dev.fyloz.colorrecipesexplorer.service.files
import com.nhaarman.mockitokotlin2.*
import dev.fyloz.colorrecipesexplorer.config.properties.CreProperties
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.slf4j.Logger
import org.springframework.core.io.Resource
import org.springframework.core.io.ResourceLoader
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.io.IOException
import java.io.InputStream
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class FileServiceTest {
private val resourcesLoader = mock<ResourceLoader>()
private val logger = mock<Logger>()
private val properties = CreProperties()
private val service = spy(FileService(resourcesLoader, properties, logger))
private val path = "/var/cre/file"
@AfterEach
fun afterEach() {
reset(resourcesLoader, logger, service)
}
// readResource()
@Test
fun `readResource() returns content of the resource at the given path`() {
val resource = mock<Resource>()
val resourceStream = mock<InputStream>()
val resourceContent = """
Line 1
Line 2
Line 3
""".trimIndent()
whenever(resource.inputStream).doReturn(resourceStream)
whenever(resourcesLoader.getResource("classpath:$path")).doReturn(resource)
doReturn(resourceContent).whenever(service).readInputStreamAsString(resourceStream)
val found = service.readResource(path)
assertEquals(resourceContent, found)
}
// readInputStreamAsString()
@Test
fun `readInputStreamAsString() returns a String matching the given input stream's content`() {
val stream = mock<InputStream>()
val streamContent = """
Line 1
Line 2
Line 3
""".trimIndent()
whenever(stream.readAllBytes()).doAnswer { streamContent.toByteArray() }
val found = service.readInputStreamAsString(stream)
assertEquals(streamContent, found)
}
// write()
private inline fun withMultipartFile(size: Long = 1000L, test: (MultipartFile) -> Unit) {
val multipartFile = mock<MultipartFile>()
whenever(multipartFile.size).doReturn(size)
test(multipartFile)
}
@Test
fun `write() transfers data from the given MultipartFile to the file at the given path and returns true`() {
withMultipartFile { multipartFile ->
val file = File(path)
doAnswer { file }.whenever(service).create(path)
assertTrue { service.write(multipartFile, path) }
verify(multipartFile).transferTo(file)
}
}
@Test
fun `write() returns true when given MultipartFile is empty`() {
withMultipartFile(size = 0L) { multipartFile ->
assertTrue { service.write(multipartFile, path) }
verify(multipartFile, never()).transferTo(any<File>())
}
}
@Test
fun `write() returns false when the data transfer throw an IOException`() {
withMultipartFile { multipartFile ->
val file = File(path)
doAnswer { file }.whenever(service).create(path)
whenever(multipartFile.transferTo(file)).doThrow(IOException())
assertFalse { service.write(multipartFile, path) }
}
}
}

View File

@ -0,0 +1,161 @@
package dev.fyloz.colorrecipesexplorer.service.files
import com.nhaarman.mockitokotlin2.*
import dev.fyloz.colorrecipesexplorer.exception.SimdutWriteException
import dev.fyloz.colorrecipesexplorer.model.Material
import dev.fyloz.colorrecipesexplorer.model.material
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.springframework.web.multipart.MultipartFile
import java.io.IOException
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class SimdutServiceTest {
private val fileService = mock<FileService>()
private val service = spy(SimdutService(fileService, mock()))
private val material = material(id = 0L)
@AfterEach
fun afterEach() {
reset(fileService, service)
}
@JvmName("withNullableMaterialPath")
private inline fun withMaterialPath(material: Material? = null, exists: Boolean = true, test: (String) -> Unit) =
withMaterialPath(material ?: this.material, exists, test)
private inline fun withMaterialPath(material: Material, exists: Boolean = true, test: (String) -> Unit) {
val path = "data/simdut/${material.id}"
doReturn(path).whenever(service).getPath(material)
whenever(fileService.exists(path)).doReturn(exists)
test(path)
}
// exists()
@Test
fun `exists() returns true when a SIMDUT file exists for the given material`() {
withMaterialPath {
assertTrue { service.exists(material) }
}
}
@Test
fun `exists() returns false when no SIMDUT file exists for the given material`() {
withMaterialPath(exists = false) {
assertFalse { service.exists(material) }
}
}
// read()
@Test
fun `read() returns a filled ByteArray when a SIMDUT exists for the given material`() {
withMaterialPath { path ->
val simdutContent = byteArrayOf(0xf)
whenever(fileService.readAsBytes(path)).doReturn(simdutContent)
val found = service.read(material)
assertEquals(simdutContent, found)
}
}
@Test
fun `read() returns a empty ByteArray when no SIMDUT exists for the given material`() {
withMaterialPath(exists = false) {
val found = service.read(material)
assertTrue { found.isEmpty() }
}
}
@Test
fun `read() returns a empty ByteArray when reading the SIMDUT throws an IOException`() {
withMaterialPath { path ->
whenever(fileService.readAsBytes(path)).doAnswer { throw IOException() }
val found = service.read(material)
assertTrue { found.isEmpty() }
}
}
// write()
@Test
fun `write() writes the given MultipartFile to the disk for the given material`() {
withMaterialPath { path ->
val simdutMultipart = mock<MultipartFile>()
whenever(fileService.write(simdutMultipart, path)).doReturn(true)
service.write(material, simdutMultipart)
verify(fileService).write(simdutMultipart, path)
}
}
@Test
fun `write() throws a SimdutWriteException when writing the given MultipartFile to the disk fails`() {
withMaterialPath { path ->
val simdutMultipart = mock<MultipartFile>()
whenever(fileService.write(simdutMultipart, path)).doReturn(false)
val exception = assertThrows<SimdutWriteException> { service.write(material, simdutMultipart) }
assertEquals(material, exception.material)
}
}
// update()
@Test
fun `update() deletes and write the SIMDUT for the given material`() {
val simdutMultipart = mock<MultipartFile>()
// Prevents calling the actual implementation
doAnswer { }.whenever(service).delete(material)
doAnswer { }.whenever(service).write(material, simdutMultipart)
service.update(simdutMultipart, material)
verify(service).delete(material)
verify(service).write(material, simdutMultipart)
}
// delete()
@Test
fun `delete() deletes the SIMDUT of the given material from the disk`() {
withMaterialPath { path ->
service.delete(material)
verify(fileService).delete(path)
}
}
// getPath()
@Test
fun `getPath() returns the appropriate path for the given material`() {
val simdutFileName = material.id.toString()
val workingDirectory = "data"
val expectedPath = "$workingDirectory/$SIMDUT_DIRECTORY/$simdutFileName"
whenever(fileService.getPath(any())).doAnswer { "$workingDirectory/${it.arguments[0]}" }
doAnswer { simdutFileName }.whenever(service).getSimdutFileName(material)
val found = service.getPath(material)
assertEquals(expectedPath, found)
verify(fileService).getPath("$SIMDUT_DIRECTORY/$simdutFileName")
}
}