diff --git a/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt b/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt index e8c88c1b41..79c96b2b6b 100644 --- a/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt +++ b/feature/pdf/src/commonMain/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapper.kt @@ -65,15 +65,13 @@ class LoiReportMapper( ), mapBlock = buildMapBlock(details), ) - val dateMillis = submission.lastModified.clientTimestamp - val timestamp = - "${dateFormatter.formatDate(dateMillis)}_${dateFormatter.formatTime(dateMillis)}" + val fileName = - listOf(details.surveyName, loiReport.loiName, details.userName, timestamp) + listOf(details.surveyName, loiReport.loiName, details.userName) .map { it.filter(::isSafeFileChar) } .filter { it.isNotBlank() } .joinToString("_") - .take(100) + .take(60) + "_${submission.id}" return PdfExportService.Request( document = document, diff --git a/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt b/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt index 3b8bbc73fb..e0b504291f 100644 --- a/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt +++ b/feature/pdf/src/commonTest/kotlin/org/groundplatform/feature/pdf/mapper/LoiReportMapperTest.kt @@ -18,6 +18,7 @@ package org.groundplatform.feature.pdf.mapper import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull +import kotlin.test.assertTrue import kotlinx.coroutines.test.runTest import org.groundplatform.domain.model.geometry.Coordinates import org.groundplatform.domain.model.geometry.LinearRing @@ -46,7 +47,7 @@ class LoiReportMapperTest { ) @Test - fun `file name joins survey loi user and timestamp with underscores`() = runTest { + fun `file name joins survey loi user and submission id with underscores`() = runTest { val request = mapper.map( loiReport = @@ -58,7 +59,7 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("Survey_Loi_User_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("Survey_Loi_User_$TEST_SUBMISSION_ID", request!!.fileName) } @Test @@ -77,7 +78,7 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("MySurvey_loiname_useremailcom_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("MySurvey_loiname_useremailcom_$TEST_SUBMISSION_ID", request!!.fileName) } @Test @@ -93,7 +94,7 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("loi_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("loi_$TEST_SUBMISSION_ID", request!!.fileName) } @Test @@ -109,7 +110,7 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("แบบสำรวจ_ເພີ່ມຈຸດສຳຫຼວດ_テスト_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("แบบสำรวจ_ເພີ່ມຈຸດສຳຫຼວດ_テスト_$TEST_SUBMISSION_ID", request!!.fileName) } @Test @@ -128,11 +129,11 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals("CaféSãoJosé_ß_Test_$TEST_TIMESTAMP", request!!.fileName) + assertEquals("CaféSãoJosé_ß_Test_$TEST_SUBMISSION_ID", request!!.fileName) } @Test - fun `file name is capped at 100 characters`() = runTest { + fun `file name is capped at 60 characters and still ends with the submission id`() = runTest { val request = mapper.map( loiReport = @@ -144,26 +145,58 @@ class LoiReportMapperTest { submission = submission, ) - assertEquals(100, request!!.fileName.length) + val fileName = request!!.fileName + assertEquals(60, fileName.removeSuffix("_$TEST_SUBMISSION_ID").length) + assertTrue(fileName.endsWith("_$TEST_SUBMISSION_ID")) } @Test - fun `timestamp comes from the submission's own last modified date`() = runTest { + fun `file name stays within the file system byte limit for multi-byte scripts`() = runTest { val request = mapper.map( loiReport = FakeDataGenerator.newLoiReport( - loiName = "Loi", + loiName = "ເພີ່ມຈຸດສຳຫຼວດ".repeat(20), submissionDetails = - FakeDataGenerator.newSubmissionDetails(surveyName = "Survey", userName = "User"), + FakeDataGenerator.newSubmissionDetails( + surveyName = "แบบสำรวจ".repeat(20), + userName = "テスト".repeat(20), + ), ), + submission = submission, + ) + + assertTrue(request!!.fileName.encodeToByteArray().size <= 255) + } + + @Test + fun `submissions sharing survey, loi name and user get different file names`() = runTest { + val loiReport = + FakeDataGenerator.newLoiReport( + loiName = "Loi", + submissionDetails = + FakeDataGenerator.newSubmissionDetails(surveyName = "Survey", userName = "User"), + ) + + val first = mapper.map(loiReport, FakeDataGenerator.newSubmission(id = "a")) + val second = mapper.map(loiReport, FakeDataGenerator.newSubmission(id = "b")) + + assertEquals("Survey_Loi_User_a", first!!.fileName) + assertEquals("Survey_Loi_User_b", second!!.fileName) + } + + @Test + fun `header timestamp comes from the submission's own last modified date`() = runTest { + val request = + mapper.map( + loiReport = FakeDataGenerator.newLoiReport(), submission = FakeDataGenerator.newSubmission( lastModified = AuditInfo(FakeDataGenerator.newUser(), clientTimestamp = 42L) ), ) - assertEquals("Survey_Loi_User_DATE42_TIME42", request!!.fileName) + assertEquals("DATE(42) TIME(42)", request!!.document.header.timestamp) } @Test @@ -286,11 +319,11 @@ class LoiReportMapperTest { ) ) ) - const val TEST_TIMESTAMP = "DATE0_TIME0" - /** Submission with a fixed last-modified timestamp so date assertions are deterministic. */ + const val TEST_SUBMISSION_ID = "submissionId" val submission = FakeDataGenerator.newSubmission( - lastModified = AuditInfo(FakeDataGenerator.newUser(), clientTimestamp = 0L) + id = TEST_SUBMISSION_ID, + lastModified = AuditInfo(FakeDataGenerator.newUser(), clientTimestamp = 0L), ) } }