diff --git a/app/src/androidTest/kotlin/br/com/colman/petals/use/io/output/FileWriterTest.kt b/app/src/androidTest/kotlin/br/com/colman/petals/use/io/output/FileWriterTest.kt index 7671a744..6ff139bb 100644 --- a/app/src/androidTest/kotlin/br/com/colman/petals/use/io/output/FileWriterTest.kt +++ b/app/src/androidTest/kotlin/br/com/colman/petals/use/io/output/FileWriterTest.kt @@ -1,73 +1,32 @@ package br.com.colman.petals.use.io.output -import androidx.test.platform.app.InstrumentationRegistry +import android.content.Context +import androidx.test.core.app.ApplicationProvider import br.com.colman.kotest.FunSpec -import io.kotest.core.spec.IsolationMode.InstancePerTest import io.kotest.matchers.file.shouldExist -import io.kotest.matchers.file.shouldNotExist import io.kotest.matchers.shouldBe -import io.kotest.matchers.shouldNotBe -import io.kotest.matchers.string.shouldEndWith -import io.mockk.every -import io.mockk.mockkStatic -import io.mockk.unmockkStatic import java.io.File import java.time.LocalDate class FileWriterTest : FunSpec({ - - val context = InstrumentationRegistry.getInstrumentation().targetContext - val exportsDirectory = File(context.filesDir, "exports") + val context: Context = ApplicationProvider.getApplicationContext() val target = FileWriter(context) + val exportDir = File(context.filesDir, "exports") - beforeEach { exportsDirectory.deleteRecursively() } - - test("Creates exports directory when it doesn't exist") { - exportsDirectory.shouldNotExist() - - target.write(FakeContent1) - - exportsDirectory.shouldExist() + test("should create export directory when using context constructor") { + exportDir.deleteRecursively() + FileWriter(context) + exportDir.shouldExist() } - test("Replaces an existing file") { - val uri1 = target.write(FakeContent1) - val uri2 = target.write(FakeContent2) + test("should write content to file and generate correct file name") { + val content = "Integration test data" + val expectedFileName = "PetalsExport-${LocalDate.now()}.csv" + val expectedFile = File(exportDir, expectedFileName) - uri1 shouldBe uri2 + target.write(content) - val content = context.contentResolver.openInputStream(uri2)!! - content.bufferedReader().readText() shouldBe FakeContent2 + expectedFile.shouldExist() + expectedFile.readText() shouldBe content } - - test("Uses PetalsExport-date.csv as the default file name") { - val uri = target.write(FakeContent1) - - uri.path shouldEndWith "PetalsExport-${LocalDate.now()}.csv" - } - - test("Generates different file names on different dates") { - val fixedDate = LocalDate.of(2024, 2, 9) - mockkStatic(LocalDate::class) - every { LocalDate.now() } returns fixedDate - - val uri1 = target.write(FakeContent1) - - val newDate = fixedDate.plusDays(1) - every { LocalDate.now() } returns newDate - - val uri2 = target.write(FakeContent2) - - uri1 shouldNotBe uri2 - - uri1.path shouldEndWith "PetalsExport-$fixedDate.csv" - uri2.path shouldEndWith "PetalsExport-$newDate.csv" - - unmockkStatic(LocalDate::class) - } - - isolationMode = InstancePerTest }) - -private const val FakeContent1 = "abc" -private const val FakeContent2 = "def" diff --git a/app/src/main/kotlin/br/com/colman/petals/use/io/output/FileWriter.kt b/app/src/main/kotlin/br/com/colman/petals/use/io/output/FileWriter.kt index 640a6c76..1d58e400 100644 --- a/app/src/main/kotlin/br/com/colman/petals/use/io/output/FileWriter.kt +++ b/app/src/main/kotlin/br/com/colman/petals/use/io/output/FileWriter.kt @@ -7,26 +7,32 @@ import br.com.colman.petals.BuildConfig.APPLICATION_ID import java.io.File import java.time.LocalDate -class FileWriter(private val context: Context) { +class FileWriter( + private val exportDirectory: File, + private val fileToUriConverter: (File) -> Uri, +) { - private val exportsDir by lazy { getExportDirectory() } + init { + if (!exportDirectory.exists()) exportDirectory.mkdirs() + } + + constructor(context: Context) : this( + File(context.filesDir, "exports"), + { getUriForFile(context, APPLICATION_ID, it) } + ) fun write(content: String): Uri { val exportedFile = createAndWriteFile(content) - return getUriForFile(context, APPLICATION_ID, exportedFile) + return fileToUriConverter(exportedFile) } private fun createAndWriteFile(content: String): File { val fileName = generateFileName() - return File(exportsDir, fileName).apply { writeText(content) } + return File(exportDirectory, fileName).apply { writeText(content) } } private fun generateFileName(): String { val date = LocalDate.now() return "PetalsExport-$date.csv" } - - private fun getExportDirectory() = File(context.filesDir, "exports").apply { - if (!exists()) mkdirs() - } } diff --git a/app/src/main/kotlin/br/com/colman/petals/use/io/output/UseOutputModule.kt b/app/src/main/kotlin/br/com/colman/petals/use/io/output/UseOutputModule.kt index 955456b4..55894a89 100644 --- a/app/src/main/kotlin/br/com/colman/petals/use/io/output/UseOutputModule.kt +++ b/app/src/main/kotlin/br/com/colman/petals/use/io/output/UseOutputModule.kt @@ -6,6 +6,6 @@ import org.koin.dsl.module val UseOutputModule = module { single { UseCsvHeaders(get()) } singleOf(::UseCsvSerializer) - singleOf(::FileWriter) + single { FileWriter(get()) } singleOf(::UseExporter) } diff --git a/app/src/test/kotlin/br/com/colman/petals/use/io/output/FileWriterTest.kt b/app/src/test/kotlin/br/com/colman/petals/use/io/output/FileWriterTest.kt new file mode 100644 index 00000000..92802d85 --- /dev/null +++ b/app/src/test/kotlin/br/com/colman/petals/use/io/output/FileWriterTest.kt @@ -0,0 +1,56 @@ +package br.com.colman.petals.use.io.output + +import android.net.Uri +import io.kotest.core.spec.style.FunSpec +import io.kotest.engine.spec.tempdir +import io.kotest.matchers.file.shouldExist +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import java.io.File +import java.time.LocalDate + +class FileWriterTest : FunSpec({ + + val exportDir = tempdir() + val fileToUri = mockk<(File) -> Uri>() + + val target = FileWriter(exportDir, fileToUri) + + test("should create export directory if it does not exist") { + exportDir.deleteRecursively() + + FileWriter(exportDir, fileToUri) + + exportDir.shouldExist() + } + + test("should generate correct file name and write to file") { + val expectedFileName = "PetalsExport-${LocalDate.now()}.csv" + val expectedFile = File(exportDir, expectedFileName) + val expectedUri = mockk() + every { fileToUri(expectedFile) } returns expectedUri + + target.write("Test Content") + + expectedFile.shouldExist() + expectedFile.name shouldBe expectedFileName + } + + test("should write content to file and return its URI") { + val content = "Sample data" + val expectedFile = File(exportDir, "PetalsExport-${LocalDate.now()}.csv") + val expectedUri = mockk() + + every { fileToUri(expectedFile) } returns expectedUri + + val resultUri = target.write(content) + + expectedFile.shouldExist() + expectedFile.readText() shouldBe content + resultUri shouldBe expectedUri + + verify { fileToUri(expectedFile) } + } +}) diff --git a/app/src/test/kotlin/br/com/colman/petals/use/io/output/UseOutputModuleTest.kt b/app/src/test/kotlin/br/com/colman/petals/use/io/output/UseOutputModuleTest.kt index a5b6e9a8..8c3e4f67 100644 --- a/app/src/test/kotlin/br/com/colman/petals/use/io/output/UseOutputModuleTest.kt +++ b/app/src/test/kotlin/br/com/colman/petals/use/io/output/UseOutputModuleTest.kt @@ -4,7 +4,9 @@ import android.content.Context import android.content.res.Resources import br.com.colman.petals.use.repository.UseRepository import io.kotest.core.spec.style.FunSpec +import io.kotest.engine.spec.tempdir import io.kotest.matchers.shouldNotBe +import io.mockk.every import io.mockk.mockk import org.koin.dsl.koinApplication import org.koin.dsl.module @@ -16,7 +18,9 @@ class UseOutputModuleTest : FunSpec({ module { single { mockk() } single { mockk(relaxed = true) } - single { mockk() } + single { mockk { + every { filesDir } returns tempdir() + } } } ) }.koin