diff --git a/app/src/test/java/com/amazon/connect/chat/androidchatexample/utils/CommonUtilsTest.kt b/app/src/test/java/com/amazon/connect/chat/androidchatexample/utils/CommonUtilsTest.kt new file mode 100644 index 0000000..56cc812 --- /dev/null +++ b/app/src/test/java/com/amazon/connect/chat/androidchatexample/utils/CommonUtilsTest.kt @@ -0,0 +1,137 @@ +package com.amazon.connect.chat.androidchatexample.utils + +import com.amazon.connect.chat.sdk.model.ContentType +import com.amazon.connect.chat.sdk.model.MessageDirection +import com.amazon.connect.chat.sdk.model.MessageStatus +import org.junit.Assert.* +import org.junit.Test +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import java.util.TimeZone + +class CommonUtilsTest { + + @Test + fun formatTime_validTimestamp_returnsFormattedTime() { + val timestamp = "2023-05-15T14:30:45.123Z" + + val result = CommonUtils.formatTime(timestamp) + + assertTrue(result.matches(Regex("\\d{2}:\\d{2}"))) + } + + @Test + fun formatTime_invalidTimestamp_throwsParseException() { + val invalidTimestamp = "invalid-timestamp" + + try { + CommonUtils.formatTime(invalidTimestamp) + fail("Expected a ParseException to be thrown") + } catch (e: java.text.ParseException) { + // Test passes if exception is thrown + assertTrue(e.message?.contains("Unparseable date") ?: false) + } + } + + @Test + fun formatDate_forLogsTrue_returnsCorrectFormat() { + val timeMillis = 1684159845123L // 2023-05-15T14:30:45.123Z + + val result = CommonUtils.formatDate(timeMillis, true) + + // Create the same formatter to verify the expected result + val utcFormatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US).apply { + timeZone = TimeZone.getTimeZone("UTC") + } + val expected = utcFormatter.format(Date(timeMillis)) + assertEquals(expected, result) + } + + @Test + fun formatDate_forLogsFalse_returnsCorrectFormat() { + val timeMillis = 1684159845123L // 2023-05-15T14:30:45.123Z + + val result = CommonUtils.formatDate(timeMillis, false) + + // Create the same formatter to verify the expected result + val utcFormatter = SimpleDateFormat("yyyy-MM-dd_HH-mm", Locale.US).apply { + timeZone = TimeZone.getTimeZone("UTC") + } + val expected = utcFormatter.format(Date(timeMillis)) + assertEquals(expected, result) + } + + @Test + fun customMessageStatus_delivered_returnsCorrectString() { + val status = MessageStatus.Delivered + + val result = CommonUtils.customMessageStatus(status) + + assertEquals("Delivered", result) + } + + @Test + fun customMessageStatus_read_returnsCorrectString() { + val status = MessageStatus.Read + + val result = CommonUtils.customMessageStatus(status) + + assertEquals("Read", result) + } + + @Test + fun customMessageStatus_sending_returnsCorrectString() { + val status = MessageStatus.Sending + + val result = CommonUtils.customMessageStatus(status) + + assertEquals("Sending", result) + } + + @Test + fun customMessageStatus_failed_returnsDefaultMessage() { + val status = MessageStatus.Failed + + val result = CommonUtils.customMessageStatus(status) + + assertTrue(result.isNotEmpty()) + } + + @Test + fun customMessageStatus_sent_returnsCorrectString() { + val status = MessageStatus.Sent + + val result = CommonUtils.customMessageStatus(status) + + assertEquals("Sent", result) + } + + @Test + fun customMessageStatus_null_returnsEmptyString() { + val status: MessageStatus? = null + + val result = CommonUtils.customMessageStatus(status) + + assertEquals("", result) + } + + @Test + fun retryButtonEnabled_failedStatus_returnsTrue() { + val status = MessageStatus.Failed + + val result = CommonUtils.retryButtonEnabled(status) + + assertTrue(result) + } + + @Test + fun retryButtonEnabled_otherStatus_returnsFalse() { + // Test with various non-failed statuses + assertFalse(CommonUtils.retryButtonEnabled(MessageStatus.Delivered)) + assertFalse(CommonUtils.retryButtonEnabled(MessageStatus.Read)) + assertFalse(CommonUtils.retryButtonEnabled(MessageStatus.Sending)) + assertFalse(CommonUtils.retryButtonEnabled(MessageStatus.Sent)) + assertFalse(CommonUtils.retryButtonEnabled(null)) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/amazon/connect/chat/androidchatexample/utils/CustomLoggerTest.kt b/app/src/test/java/com/amazon/connect/chat/androidchatexample/utils/CustomLoggerTest.kt new file mode 100644 index 0000000..e823625 --- /dev/null +++ b/app/src/test/java/com/amazon/connect/chat/androidchatexample/utils/CustomLoggerTest.kt @@ -0,0 +1,126 @@ +package com.amazon.connect.chat.androidchatexample.utils + +import org.junit.After +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream + +class CustomLoggerTest { + + @get:Rule + val tempFolder = TemporaryFolder() + + private lateinit var customLogger: CustomLogger + + private val originalOut = System.out + private val outputStreamCaptor = ByteArrayOutputStream() + + @Before + fun setUp() { + customLogger = CustomLogger() + + // Capture System.out.println output + System.setOut(PrintStream(outputStreamCaptor)) + } + + @After + fun tearDown() { + // Restore original System.out + System.setOut(originalOut) + } + + @Test + fun logVerbose_shouldPrintMessageWithVerbosePrefix() { + val message = "Test verbose message" + + customLogger.logVerbose { message } + + assertTrue(outputStreamCaptor.toString().contains("VERBOSE: $message")) + } + + @Test + fun logInfo_shouldPrintMessageWithInfoPrefix() { + val message = "Test info message" + + customLogger.logInfo { message } + + assertTrue(outputStreamCaptor.toString().contains("INFO: $message")) + } + + @Test + fun logDebug_shouldPrintMessageWithDebugPrefix() { + val message = "Test debug message" + + customLogger.logDebug { message } + + assertTrue(outputStreamCaptor.toString().contains("DEBUG: $message")) + } + + @Test + fun logWarn_shouldPrintMessageWithWarnPrefix() { + val message = "Test warn message" + + customLogger.logWarn { message } + + assertTrue(outputStreamCaptor.toString().contains("WARN: $message")) + } + + @Test + fun logError_shouldPrintMessageWithErrorPrefix() { + val message = "Test error message" + + customLogger.logError { message } + + assertTrue(outputStreamCaptor.toString().contains("ERROR: $message")) + } + + @Test + fun setLogOutputDir_shouldSetOutputDirectory() { + val tempDir = tempFolder.newFolder("logs") + + customLogger.setLogOutputDir(tempDir) + + // No assertion needed, just verifying no exception is thrown + } + + @Test + fun writeToAppTempFile_shouldHandleNullOutputFileDir() { + // Given + // outputFileDir is null by default + + customLogger.logInfo { "Test message" } + + // No assertion needed, just verifying no exception is thrown + // Wait a bit to allow the coroutine to execute + Thread.sleep(100) + } + + @Test + fun writeToAppTempFile_shouldHandleValidDirectory() { + val tempDir = tempFolder.newFolder("logs") + customLogger.setLogOutputDir(tempDir) + + customLogger.logInfo { "Test message for file" } + + // No assertion needed, just verifying no exception is thrown + // Wait a bit to allow the coroutine to execute + Thread.sleep(100) + } + + @Test + fun writeToAppTempFile_shouldHandleNonExistentDirectory() { + val nonExistentDir = File("/non/existent/directory") + customLogger.setLogOutputDir(nonExistentDir) + + customLogger.logInfo { "Test message" } + + // No assertion needed, just verifying no exception is thrown + // Wait a bit to allow the coroutine to execute + Thread.sleep(100) + } +} \ No newline at end of file diff --git a/app/src/test/java/com/amazon/connect/chat/androidchatexample/utils/FileUtilsTest.kt b/app/src/test/java/com/amazon/connect/chat/androidchatexample/utils/FileUtilsTest.kt new file mode 100644 index 0000000..56f7674 --- /dev/null +++ b/app/src/test/java/com/amazon/connect/chat/androidchatexample/utils/FileUtilsTest.kt @@ -0,0 +1,64 @@ +package com.amazon.connect.chat.androidchatexample.utils + +import org.junit.Assert.assertEquals +import org.junit.Test +import java.io.File +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class FileUtilsTest { + + @Test + fun testFileNameExtraction() { + // Test the extension extraction logic that's part of getMimeType + val fileName1 = "document.pdf" + val fileName2 = "image.jpg" + val fileName3 = "noextension" + val fileName4 = ".hiddenfile" + val fileName5 = "archive.tar.gz" + + assertEquals("pdf", fileName1.substringAfterLast('.', "")) + assertEquals("jpg", fileName2.substringAfterLast('.', "")) + assertEquals("", fileName3.substringAfterLast('.', "")) + assertEquals("hiddenfile", fileName4.substringAfterLast('.', "")) + assertEquals("gz", fileName5.substringAfterLast('.', "")) + } + + @Test + fun testFilePathConstruction() { + // Test file path construction logic + val dirPath = "/cache" + val fileName = "test.txt" + + val tempDir = File(dirPath, "attachments") + val file = File(tempDir, fileName) + + assertEquals("/cache/attachments/test.txt", file.path) + } + + @Test + fun testFilePathWithSpecialCharacters() { + // Test file path with special characters + val dirPath = "/user/data" + val fileName = "test file with spaces.pdf" + + val tempDir = File(dirPath, "attachments") + val file = File(tempDir, fileName) + + assertEquals("/user/data/attachments/test file with spaces.pdf", file.path) + } + + @Test + fun testNestedDirectoryConstruction() { + // Test nested directory construction + val baseDir = "/storage/emulated/0" + val nestedPath = "Download/documents/reports" + val fileName = "annual_report.xlsx" + + val tempDir = File(baseDir, nestedPath) + val file = File(tempDir, fileName) + + assertEquals("/storage/emulated/0/Download/documents/reports/annual_report.xlsx", file.path) + } +} diff --git a/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/CommonUtilsTest.kt b/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/CommonUtilsTest.kt new file mode 100644 index 0000000..db8b056 --- /dev/null +++ b/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/CommonUtilsTest.kt @@ -0,0 +1,73 @@ +package com.amazon.connect.chat.sdk.utils + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.TimeZone + +class CommonUtilsTest { + + @Test + fun parseErrorMessage_withNullMessage_returnsDefaultErrorMessage() { + val nullMessage: String? = null + + val result = CommonUtils.parseErrorMessage(nullMessage) + + assertEquals("An unknown error occurred", result) + } + + @Test + fun parseErrorMessage_withValidMessage_returnsOriginalMessage() { + val errorMessage = "Connection timeout" + + val result = CommonUtils.parseErrorMessage(errorMessage) + + assertEquals(errorMessage, result) + } + + @Test + fun parseErrorMessage_withEmptyMessage_returnsEmptyString() { + val emptyMessage = "" + + val result = CommonUtils.parseErrorMessage(emptyMessage) + + assertEquals(emptyMessage, result) + } + + @Test + fun getCurrentISOTime_returnsCorrectFormat() { + val result = CommonUtils.getCurrentISOTime() + + // Verify the format is correct by parsing it back + val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.getDefault()) + formatter.timeZone = TimeZone.getTimeZone("UTC") + + // This will throw if the format is incorrect + val parsedDate = formatter.parse(result) + + // Additional verification: the parsed date should be close to current time + val currentTime = System.currentTimeMillis() + val parsedTime = parsedDate?.time ?: 0 + + // Allow 5 seconds difference to account for test execution time + assertTrue(Math.abs(currentTime - parsedTime) < 5000) + } + + @Test + fun testFileExtensionExtraction() { + // Test the extension extraction logic in getMimeType + val fileName1 = "document.pdf" + val fileName2 = "image.jpg" + val fileName3 = "noextension" + val fileName4 = ".hiddenfile" + val fileName5 = "archive.tar.gz" + + assertEquals("pdf", fileName1.substringAfterLast('.', "")) + assertEquals("jpg", fileName2.substringAfterLast('.', "")) + assertEquals("", fileName3.substringAfterLast('.', "")) + assertEquals("hiddenfile", fileName4.substringAfterLast('.', "")) + assertEquals("gz", fileName5.substringAfterLast('.', "")) + } +} \ No newline at end of file diff --git a/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/ConstantsTest.kt b/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/ConstantsTest.kt new file mode 100644 index 0000000..eb93da0 --- /dev/null +++ b/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/ConstantsTest.kt @@ -0,0 +1,75 @@ +package com.amazon.connect.chat.sdk.utils + +import com.amazonaws.regions.Regions +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test + +class ConstantsTest { + + @Test + fun testConstantValues() { + // Test string constants + assertEquals("AWSConnectParticipant", Constants.AWS_CONNECT_PARTICIPANT_KEY) + assertEquals("AGENT", Constants.AGENT) + assertEquals("CUSTOMER", Constants.CUSTOMER) + assertEquals("SYSTEM", Constants.SYSTEM) + assertEquals("UNKNOWN", Constants.UNKNOWN) + assertEquals("MESSAGE", Constants.MESSAGE) + assertEquals("ATTACHMENT", Constants.ATTACHMENT) + assertEquals("EVENT", Constants.EVENT) + assertEquals("QuickReply", Constants.QUICK_REPLY) + assertEquals("ListPicker", Constants.LIST_PICKER) + assertEquals("Panel", Constants.PANEL) + assertEquals("TimePicker", Constants.TIME_PICKER) + assertEquals("Carousel", Constants.CAROUSEL) + } + + @Test + fun testNumericConstants() { + assertEquals(5.0, Constants.MESSAGE_RECEIPT_THROTTLE_TIME, 0.0) + assertEquals(3.0, Constants.MESSAGE_RECEIPT_DELIVERED_THROTTLE_TIME, 0.0) + } + + @Test + fun testListConstants() { + val requestTypes = Constants.ACPS_REQUEST_TYPES + assertEquals(2, requestTypes.size) + assertTrue(requestTypes.contains("WEBSOCKET")) + assertTrue(requestTypes.contains("CONNECTION_CREDENTIALS")) + } + + @Test + fun testRegionConstant() { + assertEquals(Regions.US_WEST_2, Constants.DEFAULT_REGION) + } + + @Test + fun testErrorMessages() { + val testReason = "network error" + assertEquals("Failed to create connection: network error.", Constants.Error.connectionCreated(testReason)) + assertEquals("Failed to create connection: network error.", Constants.Error.connectionFailed(testReason)) + } + + @Test + fun testAttachmentTypeMap() { + // Test the size of the map + assertEquals(16, Constants.attachmentTypeMap.size) + + // Test some key mappings + assertEquals("text/csv", Constants.attachmentTypeMap["csv"]) + assertEquals("application/pdf", Constants.attachmentTypeMap["pdf"]) + assertEquals("image/png", Constants.attachmentTypeMap["png"]) + assertEquals("text/plain", Constants.attachmentTypeMap["txt"]) + + // Test some document formats + assertEquals("application/msword", Constants.attachmentTypeMap["doc"]) + assertEquals("application/vnd.openxmlformats-officedocument.wordprocessingml.document", + Constants.attachmentTypeMap["docx"]) + + // Test some media formats + assertEquals("image/jpeg", Constants.attachmentTypeMap["jpg"]) + assertEquals("video/mp4", Constants.attachmentTypeMap["mp4"]) + assertEquals("audio/wav", Constants.attachmentTypeMap["wav"]) + } +} diff --git a/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/MetricsUtilsTest.kt b/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/MetricsUtilsTest.kt new file mode 100644 index 0000000..bfb154d --- /dev/null +++ b/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/MetricsUtilsTest.kt @@ -0,0 +1,40 @@ +package com.amazon.connect.chat.sdk.utils + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Test +import java.text.SimpleDateFormat +import java.util.Locale +import java.util.TimeZone + +class MetricsUtilsTest { + + @Test + fun getCurrentMetricTimestamp_returnsCorrectFormat() { + val timestamp = MetricsUtils.getCurrentMetricTimestamp() + + // Print the timestamp to see its actual format + println("Timestamp: $timestamp") + + // Verify the timestamp can be parsed back using the same formatter + val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX", Locale.getDefault()) + formatter.timeZone = TimeZone.getTimeZone("UTC") + val parsedDate = formatter.parse(timestamp) + + // Verify the parsed date is close to current time + val currentTime = System.currentTimeMillis() + val parsedTime = parsedDate?.time ?: 0 + assertTrue("Parsed time should be close to current time", + Math.abs(currentTime - parsedTime) < 5000) // Within 5 seconds + } + + @Test + fun getMetricsEndpoint_returnsCorrectEndpoint() { + val endpoint = MetricsUtils.getMetricsEndpoint() + + assertEquals( + "https://ieluqbvv.telemetry.connect.us-west-2.amazonaws.com/prod/", + endpoint + ) + } +} diff --git a/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/TranscriptItemUtilsTest.kt b/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/TranscriptItemUtilsTest.kt index a60fa78..1077d4d 100644 --- a/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/TranscriptItemUtilsTest.kt +++ b/chat-sdk/src/test/java/com/amazon/connect/chat/sdk/utils/TranscriptItemUtilsTest.kt @@ -1,17 +1,26 @@ package com.amazon.connect.chat.sdk.utils +import com.amazon.connect.chat.sdk.model.ContentType +import com.amazon.connect.chat.sdk.model.MessageDirection +import com.amazon.connect.chat.sdk.model.MessageStatus +import com.amazonaws.services.connectparticipant.model.ChatItemType +import com.amazonaws.services.connectparticipant.model.Item +import com.amazonaws.services.connectparticipant.model.ParticipantRole +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertTrue +import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner -import org.junit.Test -import org.junit.Assert.assertNotNull -import com.amazonaws.services.connectparticipant.model.Item - +import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) +@Config(manifest = Config.NONE) class TranscriptItemUtilsTest { private val INTERACTIVE_MESSAGE_CONTENTS = arrayOf( // List Picker - "{\"templateIdentifier\":\"uuid\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"\$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"\$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"\$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}}", + "{\"templateIdentifier\":\"uuid\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}}", // Time Picker "{\"templateType\":\"TimePicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"Schedule appointment\",\"subtitle\":\"Tap to select option\",\"timeslots\":[{\"date\":\"2026-01-02T00:00+00:00\",\"duration\":60},{\"date\":\"2026-01-03T00:00+00:00\",\"duration\":60},{\"date\":\"2026-01-04T00:00+00:00\",\"duration\":60}],\"location\":{\"title\":\"Oscar\",\"latitude\":47.616299,\"longitude\":-122.333031,\"radius\":1},\"timeZoneOffset\":-420}}}", // Panel @@ -19,21 +28,103 @@ class TranscriptItemUtilsTest { // Quick Reply "{\"templateType\":\"QuickReply\",\"version\":\"1.0\",\"data\":{\"replyMessage\":{\"title\":\"Thanks for selecting!\"},\"content\":{\"title\":\"How was your experience?\",\"elements\":[{\"title\":\"Great\"},{\"title\":\"Good\"},{\"title\":\"Ok\"},{\"title\":\"Poor\"},{\"title\":\"Terrible\"}]}}}", // Carousel - "{\"templateType\":\"Carousel\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"Select from produce carousel\",\"omitTitleFromCarouselResponse\":false,\"elements\":[{\"templateIdentifier\":\"interactiveCarousel001\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"\$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"\$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"\$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}},{\"templateIdentifier\":\"interactiveCarousel002\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"\$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"\$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"\$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}},{\"templateIdentifier\":\"interactiveCarousel003\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"\$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"\$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"\$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}},{\"templateIdentifier\":\"interactiveCarousel004\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"\$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"\$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"\$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}},{\"templateIdentifier\":\"interactiveCarousel005\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"\$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"\$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"\$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}}]}}}" + "{\"templateType\":\"Carousel\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"Select from produce carousel\",\"omitTitleFromCarouselResponse\":false,\"elements\":[{\"templateIdentifier\":\"interactiveCarousel001\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}},{\"templateIdentifier\":\"interactiveCarousel002\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}},{\"templateIdentifier\":\"interactiveCarousel003\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}},{\"templateIdentifier\":\"interactiveCarousel004\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}},{\"templateIdentifier\":\"interactiveCarousel005\",\"templateType\":\"ListPicker\",\"version\":\"1.0\",\"data\":{\"content\":{\"title\":\"What produce would you like to buy?\",\"subtitle\":\"Tap to select option\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/fruits.jpg\",\"elements\":[{\"title\":\"Apple\",\"subtitle\":\"$1.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/apple.jpg\"},{\"title\":\"Orange\",\"subtitle\":\"$1.50\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/orange.jpg\"},{\"title\":\"Banana\",\"subtitle\":\"$2.00\",\"imageType\":\"URL\",\"imageData\":\"https://undefined.s3.undefined.amazonaws.com/banana.jpg\"}]}}}]}}}}" ) @Test - fun test_serializeTranscriptItem(){ + fun test_serializeTranscriptItem() { INTERACTIVE_MESSAGE_CONTENTS.forEach { content -> val item = Item() .withId("e1d3bc93-fc72-421b-95f6-13b37626668b") .withContent(content) .withContentType("application/vnd.amazonaws.connect.message.interactive") .withParticipantId("c8528570-62e6-47d9-ab5d-b6e152a07935") - .withParticipantRole("SYSTEM") - .withType("MESSAGE") + .withParticipantRole(ParticipantRole.SYSTEM) + .withType(ChatItemType.MESSAGE) val result = TranscriptItemUtils.serializeTranscriptItem(item) assertNotNull(result) } } + + @Test + fun test_serializeTranscriptItem_withMinimalProperties() { + // Create an item with minimal properties + val item = Item() + .withId(null) + .withContent(null) + .withContentType(null) + + // Test serialization + val result = TranscriptItemUtils.serializeTranscriptItem(item) + + // Verify result is not null and contains expected empty/null values + assertNotNull(result) + assertTrue(result?.contains("\"Id\":\"\"") == true) + assertTrue(result?.contains("\"ParticipantRole\":null") == true) + assertTrue(result?.contains("\"ContentType\":\"\"") == true) + assertTrue(result?.contains("\"Content\":\"\"") == true) + assertTrue(result?.contains("\"Type\":null") == true) + } + + @Test + fun test_serializeTranscriptItem_withMalformedContent() { + // Create an item with malformed JSON content + val item = Item() + .withContent("{malformed json") + .withContentType("application/json") + + // Test serialization + val result = TranscriptItemUtils.serializeTranscriptItem(item) + + // Verify result is not null and contains the malformed content + assertNotNull(result) + assertTrue(result?.contains("\"Content\":\"{malformed json\"") == true) + } + + @Test + fun test_createDummyEndedEvent() { + // Test creating a dummy ended event + val event = TranscriptItemUtils.createDummyEndedEvent() + + // Verify event properties + assertNotNull(event) + assertEquals(ContentType.ENDED.type, event.contentType) + assertEquals("chat-ended-event", event.id) + assertNull(event.text) + assertNotNull(event.timeStamp) + assertNotNull(event.serializedContent) + } + + @Test + fun test_createDummyMessage() { + // Test creating a dummy message + val content = "Test message" + val contentType = "text/plain" + val status = MessageStatus.Sent + val attachmentId = "att-456" + val displayName = "Test User" + + val message = TranscriptItemUtils.createDummyMessage( + content, contentType, status, attachmentId, displayName + ) + + // Verify message properties + assertNotNull(message) + assertEquals("CUSTOMER", message.participant) + assertEquals(content, message.text) + assertEquals(contentType, message.contentType) + assertEquals(MessageDirection.OUTGOING, message.messageDirection) + assertEquals(attachmentId, message.attachmentId) + assertEquals(displayName, message.displayName) + assertNotNull(message.id) + assertNotNull(message.timeStamp) + + // Verify metadata - use safe call operator since metadata might be null + assertNotNull(message.metadata) + assertEquals(message.id, message.metadata?.id) + assertEquals(status, message.metadata?.status) + assertEquals(message.timeStamp, message.metadata?.timeStamp) + assertEquals(contentType, message.metadata?.contentType) + assertEquals(MessageDirection.OUTGOING, message.metadata?.eventDirection) + } } \ No newline at end of file