From 9d687470baa0fc79cd0a87d72980b9329c32d12a Mon Sep 17 00:00:00 2001 From: FyloZ Date: Wed, 31 Mar 2021 20:04:12 -0400 Subject: [PATCH] Correction d'un bug qui retournait une erreur 404 lorsque la fiche signalitique d'un produit existait Ajout de tests pour SimdutService. --- .../service/files/FileService.kt | 15 +- .../service/files/SimdutService.kt | 9 +- .../service/files/FileServiceTest.kt | 113 ++++++++++++ .../service/files/SimdutServiceTest.kt | 161 ++++++++++++++++++ 4 files changed, 281 insertions(+), 17 deletions(-) create mode 100644 src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileServiceTest.kt create mode 100644 src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutServiceTest.kt diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileService.kt index c951872..607dd48 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileService.kt @@ -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 withFileAt(path: String, block: File.() -> T) = - with(File(path)) { block() } + File(path).block() fun getPath(fileName: String): String = "${creProperties.workingDirectory}/$fileName" diff --git a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutService.kt b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutService.kt index cb989df..8e4843f 100644 --- a/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutService.kt +++ b/src/main/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutService.kt @@ -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))) diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileServiceTest.kt new file mode 100644 index 0000000..bc1b1db --- /dev/null +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/FileServiceTest.kt @@ -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() + private val logger = mock() + 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() + val resourceStream = mock() + 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() + 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() + 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()) + } + } + + @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) } + } + } +} diff --git a/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutServiceTest.kt b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutServiceTest.kt new file mode 100644 index 0000000..9cfc0f0 --- /dev/null +++ b/src/test/kotlin/dev/fyloz/colorrecipesexplorer/service/files/SimdutServiceTest.kt @@ -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() + 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() + + 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() + + whenever(fileService.write(simdutMultipart, path)).doReturn(false) + + val exception = assertThrows { service.write(material, simdutMultipart) } + assertEquals(material, exception.material) + } + } + + // update() + + @Test + fun `update() deletes and write the SIMDUT for the given material`() { + val simdutMultipart = mock() + + // 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") + } +}