Compare commits

..

No commits in common. "e6b1ba3b451de958bd5e36c8629358c9cade6036" and "bb069512b41b3d2854dee292ba852f431b81d868" have entirely different histories.

12 changed files with 136 additions and 322 deletions

View File

@ -102,7 +102,7 @@ tasks.withType<JavaCompile>() {
}
tasks.withType<KotlinCompile>().all {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
jvmTarget = JavaVersion.VERSION_11.toString()
freeCompilerArgs = listOf(
"-Xopt-in=kotlin.contracts.ExperimentalContracts",
"-Xinline-classes"

View File

@ -51,7 +51,7 @@ class MaterialTypeInitializer(
// Remove old system types
oldSystemTypes.forEach {
logger.info("Material type '${it.name}' is not a system type anymore")
materialTypeService.updateSystemType(it.copy(systemType = false))
materialTypeService.update(materialType(it, newSystemType = false))
}
}
}

View File

@ -7,7 +7,7 @@ import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Service
interface MaterialTypeService :
ExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository> {
ExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository> {
/** Checks if a material type with the given [prefix] exists. */
fun existsByPrefix(prefix: String): Boolean
@ -19,17 +19,14 @@ interface MaterialTypeService :
/** Gets all material types who are not a system type. */
fun getAllNonSystemType(): Collection<MaterialType>
/** Allows to update the given system [materialType], should not be exposed to users. */
fun updateSystemType(materialType: MaterialType): MaterialType
}
@Service
@Profile("!emergency")
class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val materialService: MaterialService) :
AbstractExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository>(
repository
), MaterialTypeService {
AbstractExternalNamedModelService<MaterialType, MaterialTypeSaveDto, MaterialTypeUpdateDto, MaterialType, MaterialTypeRepository>(
repository
), MaterialTypeService {
override fun idNotFoundException(id: Long) = materialTypeIdNotFoundException(id)
override fun idAlreadyExistsException(id: Long) = materialIdAlreadyExistsException(id)
override fun nameNotFoundException(name: String) = materialTypeNameNotFoundException(name)
@ -39,7 +36,7 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma
override fun existsByPrefix(prefix: String): Boolean = repository.existsByPrefix(prefix)
override fun isUsedByMaterial(materialType: MaterialType): Boolean =
materialService.existsByMaterialType(materialType)
materialService.existsByMaterialType(materialType)
override fun getAllSystemTypes(): Collection<MaterialType> = repository.findAllBySystemTypeIs(true)
override fun getAllNonSystemType(): Collection<MaterialType> = repository.findAllBySystemTypeIs(false)
@ -55,22 +52,16 @@ class MaterialTypeServiceImpl(repository: MaterialTypeRepository, private val ma
return update(with(entity) {
MaterialType(
id = id,
name = if (isNotNullAndNotBlank(name)) name else persistedMaterialType.name,
prefix = if (isNotNullAndNotBlank(prefix)) prefix else persistedMaterialType.prefix,
systemType = false
id = id,
name = if (isNotNullAndNotBlank(name)) name else persistedMaterialType.name,
prefix = if (isNotNullAndNotBlank(prefix)) prefix else persistedMaterialType.prefix,
systemType = false
)
})
}
override fun updateSystemType(materialType: MaterialType) =
update(materialType, true)
override fun update(entity: MaterialType) =
update(entity, false)
private fun update(entity: MaterialType, allowSystemTypes: Boolean): MaterialType {
if (!allowSystemTypes && repository.existsByIdAndSystemTypeIsTrue(entity.id!!)) {
override fun update(entity: MaterialType): MaterialType {
if (repository.existsByIdAndSystemTypeIsTrue(entity.id!!)) {
throw cannotUpdateSystemMaterialTypeException(entity)
}

View File

@ -1,6 +1,5 @@
package dev.fyloz.colorrecipesexplorer.service
import dev.fyloz.colorrecipesexplorer.config.annotations.RequireDatabase
import dev.fyloz.colorrecipesexplorer.model.*
import dev.fyloz.colorrecipesexplorer.model.account.Group
import dev.fyloz.colorrecipesexplorer.model.validation.or
@ -10,8 +9,10 @@ import dev.fyloz.colorrecipesexplorer.service.files.WriteableFileService
import dev.fyloz.colorrecipesexplorer.service.users.GroupService
import dev.fyloz.colorrecipesexplorer.utils.setAll
import org.springframework.context.annotation.Lazy
import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Service
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.time.LocalDate
import java.time.Period
import javax.transaction.Transactional
@ -44,7 +45,7 @@ interface RecipeService :
}
@Service
@RequireDatabase
@Profile("!emergency")
class RecipeServiceImpl(
recipeRepository: RecipeRepository,
val companyService: CompanyService,
@ -212,20 +213,29 @@ interface RecipeImageService {
/** Deletes the image with the given [name] for the given [recipe]. */
fun delete(recipe: Recipe, name: String)
/** Gets the directory containing all images of the given [Recipe]. */
fun Recipe.getDirectory(): File
}
const val RECIPE_IMAGE_ID_DELIMITER = "_"
const val RECIPE_IMAGE_EXTENSION = ".jpg"
@Service
@RequireDatabase
@Profile("!emergency")
class RecipeImageServiceImpl(
val fileService: WriteableFileService
) : RecipeImageService {
override fun getAllImages(recipe: Recipe) =
fileService.listDirectoryFiles(recipe.imagesDirectoryPath)
override fun getAllImages(recipe: Recipe): Set<String> {
val recipeDirectory = recipe.getDirectory()
if (!recipeDirectory.exists() || !recipeDirectory.isDirectory) {
return setOf()
}
return recipeDirectory.listFiles()!! // Should never be null because we check if recipeDirectory exists and is a directory before
.filterNotNull()
.map { it.name }
.toSet()
}
override fun download(image: MultipartFile, recipe: Recipe): String {
/** Gets the next id available for a new image for the given [recipe]. */
@ -242,15 +252,17 @@ class RecipeImageServiceImpl(
} + 1L
}
return getImageFileName(recipe, getNextAvailableId()).also {
with(getImagePath(recipe, it)) {
fileService.writeToDirectory(image, this, recipe.imagesDirectoryPath, true)
}
return getImageFileName(recipe, getNextAvailableId()).apply {
fileService.write(image, getImagePath(recipe, this), true)
}
}
override fun delete(recipe: Recipe, name: String) =
fileService.deleteFromDirectory(getImagePath(recipe, name), recipe.imagesDirectoryPath)
fileService.delete(getImagePath(recipe, name))
override fun Recipe.getDirectory(): File = File(with(fileService) {
this@getDirectory.imagesDirectoryPath.fullPath().path
})
private fun getImageFileName(recipe: Recipe, id: Long) =
"${recipe.name}$RECIPE_IMAGE_ID_DELIMITER$id"

View File

@ -1,150 +0,0 @@
package dev.fyloz.colorrecipesexplorer.service.files
import dev.fyloz.colorrecipesexplorer.JavaFile
import dev.fyloz.colorrecipesexplorer.utils.File
import dev.fyloz.colorrecipesexplorer.utils.FilePath
import mu.KotlinLogging
object FileCache {
private val logger = KotlinLogging.logger {}
private val cache = hashMapOf<String, CachedFileSystemItem>()
operator fun contains(filePath: FilePath) =
filePath.path in cache
operator fun get(filePath: FilePath) =
cache[filePath.path]
fun getDirectory(filePath: FilePath) =
if (directoryExists(filePath)) {
this[filePath] as CachedDirectory
} else {
null
}
fun getFile(filePath: FilePath) =
if (fileExists(filePath)) {
this[filePath] as CachedFile
} else {
null
}
private operator fun set(filePath: FilePath, item: CachedFileSystemItem) {
cache[filePath.path] = item
}
fun exists(filePath: FilePath) =
filePath in this && cache[filePath.path]!!.exists
fun directoryExists(filePath: FilePath) =
exists(filePath) && this[filePath] is CachedDirectory
fun fileExists(filePath: FilePath) =
exists(filePath) && this[filePath] is CachedFile
fun setExists(filePath: FilePath, exists: Boolean = true) {
if (filePath !in this) {
load(filePath)
}
this[filePath] = this[filePath]!!.clone(exists)
logger.debug("Updated FileCache state: ${filePath.path} exists -> $exists")
}
fun setDoesNotExists(filePath: FilePath) =
setExists(filePath, false)
fun load(filePath: FilePath) =
with(JavaFile(filePath.path).toFileSystemItem()) {
cache[filePath.path] = this
logger.debug("Loaded file at ${filePath.path} into FileCache")
}
fun addContent(filePath: FilePath, childFilePath: FilePath) {
val directory = prepareDirectory(filePath) ?: return
val updatedContent = setOf(
*directory.content.toTypedArray(),
JavaFile(childFilePath.path).toFileSystemItem()
)
this[filePath] = directory.copy(content = updatedContent)
logger.debug("Added child ${childFilePath.path} to ${filePath.path} in FileCache")
}
fun removeContent(filePath: FilePath, childFilePath: FilePath) {
val directory = prepareDirectory(filePath) ?: return
val updatedContent = directory.content
.filter { it.path.path != childFilePath.path }
.toSet()
this[filePath] = directory.copy(content = updatedContent)
logger.debug("Removed child ${childFilePath.path} from ${filePath.path} in FileCache")
}
private fun prepareDirectory(filePath: FilePath): CachedDirectory? {
if (!directoryExists(filePath)) {
logger.warn("Cannot add child to ${filePath.path} because it is not in the cache")
return null
}
val directory = getDirectory(filePath)
if (directory == null) {
logger.warn("Cannot add child to ${filePath.path} because it is not a directory")
return null
}
return directory
}
}
interface CachedFileSystemItem {
val name: String
val path: FilePath
val exists: Boolean
fun clone(exists: Boolean): CachedFileSystemItem
}
data class CachedFile(
override val name: String,
override val path: FilePath,
override val exists: Boolean
) : CachedFileSystemItem {
constructor(file: File) : this(file.name, file.toFilePath(), file.exists() && file.isFile)
override fun clone(exists: Boolean) =
this.copy(exists = exists)
}
data class CachedDirectory(
override val name: String,
override val path: FilePath,
override val exists: Boolean,
val content: Set<CachedFileSystemItem> = setOf()
) : CachedFileSystemItem {
constructor(file: File) : this(file.name, file.toFilePath(), file.exists() && file.isDirectory, file.fetchContent())
val contentFiles: Collection<CachedFile>
get() = content.filterIsInstance<CachedFile>()
override fun clone(exists: Boolean) =
this.copy(exists = exists)
companion object {
private fun File.fetchContent() =
(this.file.listFiles() ?: arrayOf<JavaFile>())
.filterNotNull()
.map { it.toFileSystemItem() }
.toSet()
}
}
fun JavaFile.toFileSystemItem() =
if (this.isDirectory) {
CachedDirectory(File(this))
} else {
CachedFile(File(this))
}

View File

@ -0,0 +1,27 @@
package dev.fyloz.colorrecipesexplorer.service.files
import dev.fyloz.colorrecipesexplorer.utils.FilePath
object FileExistCache {
private val map = hashMapOf<FilePath, Boolean>()
/** Checks if the given [path] is in the cache. */
operator fun contains(path: FilePath) =
path in map
/** Checks if the file at the given [path] exists. */
fun exists(path: FilePath) =
map[path] ?: false
/** Sets the file at the given [path] as existing. */
fun setExists(path: FilePath) =
set(path, true)
/** Sets the file at the given [path] as not existing. */
fun setDoesNotExists(path: FilePath) =
set(path, false)
private fun set(path: FilePath, exists: Boolean) {
map[path] = exists
}
}

View File

@ -5,7 +5,6 @@ import dev.fyloz.colorrecipesexplorer.exception.RestException
import dev.fyloz.colorrecipesexplorer.utils.File
import dev.fyloz.colorrecipesexplorer.utils.FilePath
import dev.fyloz.colorrecipesexplorer.utils.withFileAt
import mu.KotlinLogging
import org.slf4j.Logger
import org.springframework.core.io.ByteArrayResource
import org.springframework.core.io.Resource
@ -28,11 +27,8 @@ interface FileService {
/** Reads the file at the given [path]. */
fun read(path: String): Resource
/** List the files contained in the folder at the given [path]. Returns an empty collection if the directory does not exist. */
fun listDirectoryFiles(path: String): Collection<CachedFile>
/** Completes the path of the given [String] by adding the working directory. */
fun fullPath(path: String): FilePath
fun String.fullPath(): FilePath
}
interface WriteableFileService : FileService {
@ -45,37 +41,28 @@ interface WriteableFileService : FileService {
/** Writes the given [data] to the given [path]. If the file at the path already exists, it will be overwritten if [overwrite] is enabled. */
fun write(data: ByteArrayResource, path: String, overwrite: Boolean)
/** Writes the given [data] to the given [path], and specify the [parentPath]. If the file at the path already exists, it will be overwritten if [overwrite] is enabled. */
fun writeToDirectory(data: MultipartFile, path: String, parentPath: String, overwrite: Boolean)
/** Deletes the file at the given [path]. */
fun delete(path: String)
/** Deletes the file at the given [path], and specify the [parentPath]. */
fun deleteFromDirectory(path: String, parentPath: String)
}
@Service
class FileServiceImpl(
private val creProperties: CreProperties
private val creProperties: CreProperties,
private val logger: Logger
) : WriteableFileService {
private val logger = KotlinLogging.logger {}
override fun exists(path: String): Boolean {
val fullPath = fullPath(path)
return if (fullPath in FileCache) {
FileCache.exists(fullPath)
val fullPath = path.fullPath()
return if (fullPath in FileExistCache) {
FileExistCache.exists(fullPath)
} else {
withFileAt(fullPath) {
(this.exists() && this.isFile).also {
FileCache.setExists(fullPath, it)
}
this.exists() && this.isFile
}
}
}
override fun read(path: String) = ByteArrayResource(
withFileAt(fullPath(path)) {
withFileAt(path.fullPath()) {
if (!exists(path)) throw FileNotFoundException(path)
try {
readBytes()
@ -85,25 +72,13 @@ class FileServiceImpl(
}
)
override fun listDirectoryFiles(path: String): Collection<CachedFile> =
with(fullPath(path)) {
if (this !in FileCache) {
FileCache.load(this)
}
(FileCache.getDirectory(this) ?: return setOf())
.contentFiles
}
override fun create(path: String) {
val fullPath = fullPath(path)
val fullPath = path.fullPath()
if (!exists(path)) {
try {
withFileAt(fullPath) {
this.create()
FileCache.setExists(fullPath)
logger.info("Created file at '${fullPath.path}'")
FileExistCache.setExists(fullPath)
}
} catch (ex: IOException) {
FileCreateException(path).logAndThrow(ex, logger)
@ -113,52 +88,38 @@ class FileServiceImpl(
override fun write(file: MultipartFile, path: String, overwrite: Boolean) =
prepareWrite(path, overwrite) {
logWrittenDataSize(file.size)
file.transferTo(this.toPath())
}
override fun write(data: ByteArrayResource, path: String, overwrite: Boolean) =
prepareWrite(path, overwrite) {
logWrittenDataSize(data.contentLength())
this.writeBytes(data.byteArray)
}
override fun writeToDirectory(data: MultipartFile, path: String, parentPath: String, overwrite: Boolean) {
FileCache.addContent(fullPath(parentPath), fullPath(path))
write(data, path, overwrite)
}
override fun delete(path: String) {
try {
val fullPath = fullPath(path)
val fullPath = path.fullPath()
withFileAt(fullPath) {
if (!exists(path)) throw FileNotFoundException(path)
this.delete()
FileCache.setDoesNotExists(fullPath)
logger.info("Deleted file at '${fullPath.path}'")
FileExistCache.setDoesNotExists(fullPath)
}
} catch (ex: IOException) {
FileDeleteException(path).logAndThrow(ex, logger)
}
}
override fun deleteFromDirectory(path: String, parentPath: String) {
FileCache.removeContent(fullPath(parentPath), fullPath(path))
delete(path)
}
override fun fullPath(path: String): FilePath {
override fun String.fullPath(): FilePath {
BANNED_FILE_PATH_SHARDS
.firstOrNull { path.contains(it) }
?.let { throw InvalidFilePathException(path, it) }
.firstOrNull { this.contains(it) }
?.let { throw InvalidFilePathException(this, it) }
return FilePath("${creProperties.dataDirectory}/$path")
return FilePath("${creProperties.dataDirectory}/$this")
}
private fun prepareWrite(path: String, overwrite: Boolean, op: File.() -> Unit) {
val fullPath = fullPath(path)
val fullPath = path.fullPath()
if (exists(path)) {
if (!overwrite) throw FileExistsException(path)
@ -169,17 +130,11 @@ class FileServiceImpl(
try {
withFileAt(fullPath) {
this.op()
logger.info("Wrote data to file at '${fullPath.path}'")
}
} catch (ex: IOException) {
FileWriteException(path).logAndThrow(ex, logger)
}
}
private fun logWrittenDataSize(size: Long) {
logger.debug("Writing $size bytes to file system...")
}
}
private const val FILE_IO_EXCEPTION_TITLE = "File IO error"

View File

@ -10,26 +10,17 @@ class ResourceFileService(
private val resourceLoader: ResourceLoader
) : FileService {
override fun exists(path: String) =
fullPath(path).resource.exists()
path.fullPath().resource.exists()
override fun read(path: String): Resource =
fullPath(path).resource.also {
path.fullPath().resource.also {
if (!it.exists()) {
throw FileNotFoundException(path)
}
}
override fun listDirectoryFiles(path: String): Collection<CachedFile> {
val content = fullPath(path).resource.file.listFiles() ?: return setOf()
return content
.filterNotNull()
.filter { it.isFile }
.map { it.toFileSystemItem() as CachedFile }
}
override fun fullPath(path: String) =
FilePath("classpath:${path}")
override fun String.fullPath() =
FilePath("classpath:${this}")
val FilePath.resource: Resource
get() = resourceLoader.getResource(this.path)

View File

@ -6,42 +6,33 @@ import java.nio.file.Path
/** Mockable file wrapper, to prevent issues when mocking [java.io.File]. */
class File(val file: JavaFile) {
val name: String
get() = file.name
val isFile: Boolean
get() = file.isFile
val isDirectory: Boolean
get() = file.isDirectory
fun toPath(): Path =
file.toPath()
fun toFilePath(): FilePath =
FilePath(file.path)
file.toPath()
fun exists() =
file.exists()
file.exists()
fun readBytes() =
file.readBytes()
file.readBytes()
fun writeBytes(array: ByteArray) =
file.writeBytes(array)
file.writeBytes(array)
fun create() =
file.create()
file.create()
fun delete(): Boolean =
file.delete()
file.delete()
companion object {
fun from(path: String) =
File(JavaFile(path))
File(JavaFile(path))
fun from(path: FilePath) =
from(path.path)
from(path.path)
}
}
@ -50,7 +41,7 @@ class FilePath(val path: String)
/** Runs the given [block] in the context of a file with the given [fullPath]. */
fun <T> withFileAt(fullPath: FilePath, block: File.() -> T) =
File.from(fullPath).block()
File.from(fullPath).block()
/** Shortcut to create a file and its parent directories. */
fun JavaFile.create() {

View File

@ -6,10 +6,8 @@ import dev.fyloz.colorrecipesexplorer.model.*
import dev.fyloz.colorrecipesexplorer.model.account.group
import dev.fyloz.colorrecipesexplorer.repository.RecipeRepository
import dev.fyloz.colorrecipesexplorer.service.config.ConfigurationService
import dev.fyloz.colorrecipesexplorer.service.files.CachedFile
import dev.fyloz.colorrecipesexplorer.service.files.WriteableFileService
import dev.fyloz.colorrecipesexplorer.service.users.GroupService
import dev.fyloz.colorrecipesexplorer.utils.FilePath
import io.mockk.*
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.Test
@ -17,6 +15,7 @@ import org.junit.jupiter.api.TestInstance
import org.junit.jupiter.api.assertThrows
import org.springframework.mock.web.MockMultipartFile
import org.springframework.web.multipart.MultipartFile
import java.io.File
import java.time.LocalDate
import java.time.Period
import kotlin.test.*
@ -31,17 +30,7 @@ class RecipeServiceTest :
private val recipeStepService: RecipeStepService = mock()
private val configService: ConfigurationService = mock()
override val service: RecipeService =
spy(
RecipeServiceImpl(
repository,
companyService,
mixService,
recipeStepService,
groupService,
mock(),
configService
)
)
spy(RecipeServiceImpl(repository, companyService, mixService, recipeStepService, groupService, mock(), configService))
private val company: Company = company(id = 0L)
override val entity: Recipe = recipe(id = 0L, name = "recipe", company = company)
@ -284,7 +273,18 @@ private class RecipeImageServiceTestContext {
val recipe = spyk(recipe())
val recipeImagesIds = setOf(1L, 10L, 21L)
val recipeImagesNames = recipeImagesIds.map { it.imageName }.toSet()
val recipeImagesFiles = recipeImagesNames.map { CachedFile(it, FilePath(it), true) }
val recipeImagesFiles = recipeImagesNames.map { File(it) }.toTypedArray()
val recipeDirectory = mockk<File> {
every { exists() } returns true
every { isDirectory } returns true
every { listFiles() } returns recipeImagesFiles
}
init {
with(recipeImageService) {
every { recipe.getDirectory() } returns recipeDirectory
}
}
val Long.imageName
get() = "${recipe.name}$RECIPE_IMAGE_ID_DELIMITER$this"
@ -308,8 +308,6 @@ class RecipeImageServiceTest {
@Test
fun `getAllImages() returns a Set containing the name of every files in the recipe's directory`() {
test {
every { fileService.listDirectoryFiles(any()) } returns recipeImagesFiles
val foundImagesNames = recipeImageService.getAllImages(recipe)
assertEquals(recipeImagesNames, foundImagesNames)
@ -319,7 +317,7 @@ class RecipeImageServiceTest {
@Test
fun `getAllImages() returns an empty Set when the recipe's directory does not exists`() {
test {
every { fileService.listDirectoryFiles(any()) } returns emptySet()
every { recipeDirectory.exists() } returns false
assertTrue {
recipeImageService.getAllImages(recipe).isEmpty()
@ -337,15 +335,12 @@ class RecipeImageServiceTest {
val expectedImageName = expectedImageId.imageName
val expectedImagePath = expectedImageName.imagePath
every { fileService.listDirectoryFiles(any()) } returns recipeImagesFiles
every { fileService.writeToDirectory(any(), any(), any(), any()) } just runs
val foundImageName = recipeImageService.download(mockImage, recipe)
assertEquals(expectedImageName, foundImageName)
verify {
fileService.writeToDirectory(mockImage, expectedImagePath, any(), true)
fileService.write(mockImage, expectedImagePath, true)
}
}
}
@ -358,12 +353,10 @@ class RecipeImageServiceTest {
val imageName = recipeImagesIds.first().imageName
val imagePath = imageName.imagePath
every { fileService.deleteFromDirectory(any(), any()) } just runs
recipeImageService.delete(recipe, imageName)
verify {
fileService.deleteFromDirectory(imagePath, any())
fileService.delete(imagePath)
}
}
}

View File

@ -22,7 +22,9 @@ private val mockFilePathPath = Path.of(mockFilePath)
private val mockFileData = byteArrayOf(0x1, 0x8, 0xa, 0xf)
class FileServiceTest {
private val fileService = spyk(FileServiceImpl(creProperties))
private val fileService = spyk(FileServiceImpl(creProperties, mockk {
every { error(any(), any<Exception>()) } just Runs
}))
private val mockFile = mockk<File> {
every { file } returns mockk()
every { exists() } returns true
@ -43,8 +45,8 @@ class FileServiceTest {
}
private fun whenFileCached(cached: Boolean = true, test: () -> Unit) {
mockkObject(FileCache) {
every { FileCache.contains(any()) } returns cached
mockkObject(FileExistCache) {
every { FileExistCache.contains(any()) } returns cached
test()
}
@ -106,34 +108,34 @@ class FileServiceTest {
@Test
fun `exists() returns true when the file at the given path is cached as existing`() {
whenFileCached {
every { FileCache.exists(any()) } returns true
every { FileExistCache.exists(any()) } returns true
assertTrue { fileService.exists(mockFilePath) }
verify {
FileCache.contains(any())
FileCache.exists(any())
FileExistCache.contains(any())
FileExistCache.exists(any())
mockFile wasNot called
}
confirmVerified(FileCache, mockFile)
confirmVerified(FileExistCache, mockFile)
}
}
@Test
fun `exists() returns false when the file at the given path is cached as not existing`() {
whenFileCached {
every { FileCache.exists(any()) } returns false
every { FileExistCache.exists(any()) } returns false
assertFalse { fileService.exists(mockFilePath) }
verify {
FileCache.contains(any())
FileCache.exists(any())
FileExistCache.contains(any())
FileExistCache.exists(any())
mockFile wasNot called
}
confirmVerified(FileCache, mockFile)
confirmVerified(FileExistCache, mockFile)
}
}
@ -180,16 +182,16 @@ class FileServiceTest {
whenFileNotCached {
mockkStatic(File::create) {
every { mockFile.create() } just Runs
every { FileCache.setExists(any()) } just Runs
every { FileExistCache.setExists(any()) } just Runs
fileService.create(mockFilePath)
verify {
mockFile.create()
FileCache.setExists(any())
FileExistCache.setExists(any())
}
confirmVerified(mockFile, FileCache)
confirmVerified(mockFile, FileExistCache)
}
}
}
@ -278,16 +280,16 @@ class FileServiceTest {
whenMockFilePathExists {
whenFileCached {
every { mockFile.delete() } returns true
every { FileCache.setDoesNotExists(any()) } just Runs
every { FileExistCache.setDoesNotExists(any()) } just Runs
fileService.delete(mockFilePath)
verify {
mockFile.delete()
FileCache.setDoesNotExists(any())
FileExistCache.setDoesNotExists(any())
}
confirmVerified(mockFile, FileCache)
confirmVerified(mockFile, FileExistCache)
}
}
}
@ -317,7 +319,7 @@ class FileServiceTest {
@Test
fun `fullPath() appends the given path to the given working directory`() {
with(fileService) {
val fullFilePath = fullPath(mockFilePath)
val fullFilePath = mockFilePath.fullPath()
assertEquals("${creProperties.dataDirectory}/$mockFilePath", fullFilePath.path)
}
@ -329,7 +331,7 @@ class FileServiceTest {
BANNED_FILE_PATH_SHARDS.forEach {
val maliciousPath = "$it/$mockFilePath"
with(assertThrows<InvalidFilePathException> { fullPath(maliciousPath) }) {
with(assertThrows<InvalidFilePathException> { maliciousPath.fullPath() }) {
assertEquals(maliciousPath, this.path)
assertEquals(it, this.fragment)
}

View File

@ -27,7 +27,7 @@ class ResourceFileServiceTest {
private fun existsTest(shouldExists: Boolean, test: (String) -> Unit) {
val path = "unit_test_resource"
with(service) {
every { fullPath(path) } returns mockk {
every { path.fullPath() } returns mockk {
every { resource } returns mockk {
every { exists() } returns shouldExists
}
@ -61,7 +61,7 @@ class ResourceFileServiceTest {
}
val path = "unit_test_path"
with(service) {
every { fullPath(path) } returns mockk {
every { path.fullPath() } returns mockk {
every { resource } returns mockResource
}
@ -92,9 +92,11 @@ class ResourceFileServiceTest {
val path = "unit_test_path"
val expectedPath = "classpath:$path"
val found = service.fullPath(path)
with(service) {
val found = path.fullPath()
assertEquals(expectedPath, found.path)
assertEquals(expectedPath, found.path)
}
}
@Test