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:
commit
ac4be0b330
|
@ -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"
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue