diff --git a/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt b/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt index 200e9fa6..5ab677b2 100644 --- a/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt +++ b/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt @@ -666,6 +666,127 @@ class ChatViewModelTest { } } + // ── Attachments ────────────────────────────────────────────────────────── + + /** Add [count] dummy attachments so a test starts with a populated list. */ + private fun TestScope.addDummyAttachments( + viewModel: ChatViewModel, + count: Int, + ) { + repeat(count) { i -> + viewModel.addAttachment("uri$i", "file$i.txt", "text/plain", (i + 1) * 100L) + } + advanceUntilIdle() + } + + @Test + fun testAddAttachment() = + runTest { + val viewModel = createViewModel() + advanceUntilIdle() + + viewModel.addAttachment( + uri = "content://dummy/1", + name = "dummy.txt", + mimeType = "text/plain", + size = 1024L, + ) + advanceUntilIdle() + + val pending = viewModel.uiState.value.pendingAttachments + assertEquals(1, pending.size) + assertEquals("content://dummy/1", pending[0].uri) + assertEquals("dummy.txt", pending[0].name) + } + + @Test + fun testRemoveAttachment_validIndex() = + runTest { + val viewModel = createViewModel() + advanceUntilIdle() + + viewModel.addAttachment("uri1", "file1.txt", "text/plain", 100) + viewModel.addAttachment("uri2", "file2.txt", "text/plain", 200) + advanceUntilIdle() + + assertEquals(2, viewModel.uiState.value.pendingAttachments.size) + + viewModel.removeAttachment(0) + advanceUntilIdle() + + val pending = viewModel.uiState.value.pendingAttachments + assertEquals(1, pending.size) + assertEquals("uri2", pending[0].uri) + } + + @Test + fun testRemoveAttachment_invalidIndex() = + runTest { + val viewModel = createViewModel() + advanceUntilIdle() + + viewModel.addAttachment("uri1", "file1.txt", "text/plain", 100) + advanceUntilIdle() + + assertEquals(1, viewModel.uiState.value.pendingAttachments.size) + + // Out of bounds index should not crash or change list + viewModel.removeAttachment(5) + viewModel.removeAttachment(-1) + advanceUntilIdle() + + val pending = viewModel.uiState.value.pendingAttachments + assertEquals(1, pending.size) + assertEquals("uri1", pending[0].uri) + } + + @Test + fun testRemoveAttachment_mixedValidAndInvalidSequence() = + runTest { + val viewModel = createViewModel() + advanceUntilIdle() + addDummyAttachments(viewModel, 3) // [uri0, uri1, uri2] + + // 1. Remove a valid index (the middle item) → list shrinks correctly. + viewModel.removeAttachment(1) + advanceUntilIdle() + assertEquals(2, viewModel.uiState.value.pendingAttachments.size) + assertEquals("uri0", viewModel.uiState.value.pendingAttachments[0].uri) + assertEquals("uri2", viewModel.uiState.value.pendingAttachments[1].uri) + + // 2. Fire invalid removals (out of bounds + negative) — must be no-ops. + viewModel.removeAttachment(99) + viewModel.removeAttachment(-1) + advanceUntilIdle() + assertEquals(2, viewModel.uiState.value.pendingAttachments.size) + + // 3. Another valid removal on the shifted list → still consistent. + viewModel.removeAttachment(1) + advanceUntilIdle() + val pending = viewModel.uiState.value.pendingAttachments + assertEquals(1, pending.size) + assertEquals("uri0", pending[0].uri) + } + + @Test + fun testClearAttachments() = + runTest { + val viewModel = createViewModel() + advanceUntilIdle() + + viewModel.addAttachment("uri1", "file1.txt", "text/plain", 100) + viewModel.addAttachment("uri2", "file2.txt", "text/plain", 200) + advanceUntilIdle() + + assertEquals(2, viewModel.uiState.value.pendingAttachments.size) + + viewModel.clearAttachments() + advanceUntilIdle() + + val pending = viewModel.uiState.value.pendingAttachments + assertTrue(pending.isEmpty()) + } + // ── Send message ───────────────────────────────────────────────────────── @Test