Loading k9mail/src/test/java/com/fsck/k9/KotlinHelper.kt 0 → 100644 +6 −0 Original line number Diff line number Diff line package com.fsck.k9 import org.mockito.Mockito import org.mockito.stubbing.OngoingStubbing fun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(methodCall) k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo51Test.javadeleted 100644 → 0 +0 −222 Original line number Diff line number Diff line package com.fsck.k9.mailstore.migrations; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.fsck.k9.Account; import com.fsck.k9.mailstore.StorageManager; import org.apache.commons.io.IOUtils; import org.apache.james.mime4j.codec.QuotedPrintableInputStream; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) public class MigrationTo51Test { private MigrationsHelper mockMigrationsHelper; private Account account; private StorageManager storageManager; private SQLiteDatabase database; @Before public void before() { mockMigrationsHelper = mock(MigrationsHelper.class); account = mock(Account.class); storageManager = StorageManager.getInstance(RuntimeEnvironment.application); storageManager.getDefaultProviderId(); when(account.getUuid()).thenReturn("001"); when(account.getLocalStorageProviderId()).thenReturn(storageManager.getDefaultProviderId()); when(mockMigrationsHelper.getContext()).thenReturn(RuntimeEnvironment.application); when(mockMigrationsHelper.getAccount()).thenReturn(account); database = createWithV50Table(); } private SQLiteDatabase createWithV50Table() { SQLiteDatabase database = SQLiteDatabase.create(null); database.execSQL("CREATE TABLE messages (" + "id INTEGER PRIMARY KEY, " + "deleted INTEGER default 0, " + "folder_id INTEGER, " + "uid TEXT, " + "subject TEXT, " + "date INTEGER, " + "sender_list TEXT, " + "to_list TEXT, " + "cc_list TEXT, " + "bcc_list TEXT, " + "reply_to_list TEXT, " + "attachment_count INTEGER, " + "internal_date INTEGER, " + "message_id TEXT, " + "preview TEXT, " + "mime_type TEXT, " + "html_content TEXT, " + "text_content TEXT, " + "flags TEXT, " + "normalized_subject_hash INTEGER, " + "empty INTEGER default 0, " + "read INTEGER default 0, " + "flagged INTEGER default 0, " + "answered INTEGER default 0 )"); database.execSQL("CREATE TABLE headers ("+ "id INTEGER PRIMARY KEY," + "name TEXT," + "value TEXT," + "message_id INTEGER)"); database.execSQL("CREATE TABLE attachments ("+ "id INTEGER PRIMARY KEY," + "size INTEGER, " + "name TEXT, " + "mime_type TEXT, " + "store_data TEXT, " + "content_uri TEXT, " + "content_id TEXT, " + "content_disposition TEXT," + "message_id INTEGER)"); return database; } private void addTextPlainMessage() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"",null,"Text","text/plain",0}); } private void addTextHtmlMessage() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html></html>","Text","text/html",0}); } private void addMultipartAlternativeMessage() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html></html>",null,"multipart/alternative",0}); } private void addMultipartMixedMessage() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html></html>","Text","multipart/mixed",0}); } private void addMultipartMixedMessageWithAttachment() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html><img src=\"testUri\" /></html>",null,"multipart/mixed",1}); database.execSQL( "INSERT INTO attachments (size, name, mime_type, store_data, " + "content_uri, content_id, content_disposition, message_id) VALUES(?,?,?,?, ?,?,?,?)", new Object[]{1, "a.jpg", "image/jpg", "a", "testUri", "content*user@host", "disposition", 1} ); } private void addMultipartMixedMessageWithAttachmentWithUnusualContentId() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html><img src=\"testUri\" /></html>",null,"multipart/mixed",1}); database.execSQL( "INSERT INTO attachments (size, name, mime_type, store_data, " + "content_uri, content_id, content_disposition, message_id) VALUES(?,?,?,?, ?,?,?,?)", new Object[]{1, "a.jpg", "image/jpg", "a", "testUri", "a$b@host", "disposition", 1} ); } @Test public void db51MigrateMessageFormat_canMigrateEmptyMessagesTable() { MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateTextPlainMessage() { addTextPlainMessage(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateTextHtmlMessage() { addTextHtmlMessage(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateMultipartAlternativeMessage() { addMultipartAlternativeMessage(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateMultipartMixedMessage() { addMultipartMixedMessage(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateMultipartMixedMessageWithAttachment() { addMultipartMixedMessageWithAttachment(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_withMultipartMixedMessageWithAttachment_stores_messagePart() { addMultipartMixedMessageWithAttachment(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); Cursor cursor = database.query("message_parts", new String[]{"data"}, "mime_type = 'text/html'", null, null, null, null); boolean isNotEmpty = cursor.moveToNext(); assertTrue(isNotEmpty); } @Test public void db51MigrateMessageFormat_withMultipartMixedMessageWithAttachment_updatesContentReference() throws IOException { addMultipartMixedMessageWithAttachment(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); Cursor cursor = database.query("message_parts", new String[]{"data"}, "mime_type = 'text/html'", null, null, null, null); boolean isNotEmpty = cursor.moveToNext(); assertTrue(isNotEmpty); String partDataAsString = IOUtils.toString( new QuotedPrintableInputStream(new ByteArrayInputStream(cursor.getBlob(0)))); assertEquals("<html><img src=\"cid:content*user@host\" /></html>", partDataAsString); } @Test public void db51MigrateMessageFormat_withMultipartMixedMessageWithAttachmentWithUnusualContentID_updatesContentReference() throws IOException { addMultipartMixedMessageWithAttachmentWithUnusualContentId(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); Cursor cursor = database.query("message_parts", new String[]{"data"}, "mime_type = 'text/html'", null, null, null, null); boolean isNotEmpty = cursor.moveToNext(); assertTrue(isNotEmpty); String partDataAsString = IOUtils.toString( new QuotedPrintableInputStream(new ByteArrayInputStream(cursor.getBlob(0)))); assertEquals("<html><img src=\"cid:a$b@host\" /></html>", partDataAsString); } } k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo51Test.kt 0 → 100644 +234 −0 Original line number Diff line number Diff line package com.fsck.k9.mailstore.migrations import android.database.sqlite.SQLiteDatabase import com.fsck.k9.Account import com.fsck.k9.mailstore.StorageManager import com.fsck.k9.whenever import org.apache.commons.io.IOUtils import org.apache.james.mime4j.codec.QuotedPrintableInputStream import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config import java.io.ByteArrayInputStream @RunWith(RobolectricTestRunner::class) @Config(manifest = Config.NONE) class MigrationTo51Test { private lateinit var mockMigrationsHelper: MigrationsHelper private lateinit var database: SQLiteDatabase @Before fun setUp() { val storageManager = StorageManager.getInstance(RuntimeEnvironment.application) storageManager.defaultProviderId val account = mock(Account::class.java) whenever(account.uuid).thenReturn("001") whenever(account.localStorageProviderId).thenReturn(storageManager.defaultProviderId) mockMigrationsHelper = mock(MigrationsHelper::class.java) whenever(mockMigrationsHelper.context).thenReturn(RuntimeEnvironment.application) whenever(mockMigrationsHelper.account).thenReturn(account) database = createWithV50Table() } @Test fun db51MigrateMessageFormat_canMigrateEmptyMessagesTable() { MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateTextPlainMessage() { addTextPlainMessage() MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateTextHtmlMessage() { addTextHtmlMessage() MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateMultipartAlternativeMessage() { addMultipartAlternativeMessage() MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateMultipartMixedMessage() { addMultipartMixedMessage() MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateMultipartMixedMessageWithAttachment() { addMultipartMixedMessageWithAttachment(attachmentContentId = "content*user@host") MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_withMultipartMixedMessageWithAttachment_storesMessagePart() { addMultipartMixedMessageWithAttachment(attachmentContentId = "content*user@host") MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) val isNotEmpty = loadHtmlMessagePartCursor().moveToNext() assertTrue(isNotEmpty) } @Test fun db51MigrateMessageFormat_withMultipartMixedMessageWithAttachment_updatesContentReference() { addMultipartMixedMessageWithAttachment(attachmentContentId = "content*user@host") MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) assertEquals("""<html><img src="cid:content*user@host" /></html>""", htmlMessagePartBody()) } @Test fun db51MigrateMessageFormat_withMultipartMixedMessageWithAttachmentWithUnusualContentID_updatesContentReference() { addMultipartMixedMessageWithAttachment(attachmentContentId = "a\$b@host") MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) assertEquals("<html><img src=\"cid:a\$b@host\" /></html>", htmlMessagePartBody()) } private fun createWithV50Table(): SQLiteDatabase { val database = SQLiteDatabase.create(null) database.execSQL(""" CREATE TABLE messages ( id INTEGER PRIMARY KEY, deleted INTEGER default 0, folder_id INTEGER, uid TEXT, subject TEXT, date INTEGER, sender_list TEXT, to_list TEXT, cc_list TEXT, bcc_list TEXT, reply_to_list TEXT, attachment_count INTEGER, internal_date INTEGER, message_id TEXT, preview TEXT, mime_type TEXT, html_content TEXT, text_content TEXT, flags TEXT, normalized_subject_hash INTEGER, empty INTEGER default 0, read INTEGER default 0, flagged INTEGER default 0, answered INTEGER default 0 ) """.trimIndent() ) database.execSQL(""" CREATE TABLE headers ( id INTEGER PRIMARY KEY, name TEXT, value TEXT, message_id INTEGER ) """.trimIndent() ) database.execSQL(""" CREATE TABLE attachments ( id INTEGER PRIMARY KEY, size INTEGER, name TEXT, mime_type TEXT, store_data TEXT, content_uri TEXT, content_id TEXT, content_disposition TEXT, message_id INTEGER ) """.trimIndent() ) return database } private fun addTextPlainMessage() { insertMessage(mimeType = "text/plain", textContent = "Text") } private fun addTextHtmlMessage() { insertMessage(mimeType = "text/html", htmlContent = "<html></html>") } private fun addMultipartAlternativeMessage() { insertMessage(mimeType = "multipart/alternative", htmlContent = "<html></html>") } private fun addMultipartMixedMessage() { insertMessage(mimeType = "multipart/mixed", htmlContent = "<html></html>", textContent = "Text") } private fun addMultipartMixedMessageWithAttachment(attachmentContentId: String) { insertMessage( mimeType = "multipart/mixed", htmlContent = """<html><img src="testUri" /></html>""", attachmentCount = 1 ) insertImageAttachment(attachmentContentId) } private fun insertMessage( mimeType: String, htmlContent: String? = null, textContent: String? = null, attachmentCount: Int = 0 ) { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, mime_type, attachment_count) " + "VALUES (?, ?, ?, ?, ?)", arrayOf("", htmlContent, textContent, mimeType, attachmentCount) ) } private fun insertImageAttachment(cid: String) { database.execSQL( """ INSERT INTO attachments (size, name, mime_type, store_data, content_uri, content_id, content_disposition, message_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?) """.trimIndent(), arrayOf(1, "a.jpg", "image/jpeg", "a", "testUri", cid, "disposition", 1) ) } private fun loadHtmlMessagePartCursor() = database.query("message_parts", arrayOf("data"), "mime_type = 'text/html'", null, null, null, null) private fun htmlMessagePartBody(): String { val cursor = loadHtmlMessagePartCursor() if (!cursor.moveToNext()) { throw AssertionError("No message part found") } return IOUtils.toString( QuotedPrintableInputStream( ByteArrayInputStream(cursor.getBlob(0)) ) ) } } Loading
k9mail/src/test/java/com/fsck/k9/KotlinHelper.kt 0 → 100644 +6 −0 Original line number Diff line number Diff line package com.fsck.k9 import org.mockito.Mockito import org.mockito.stubbing.OngoingStubbing fun <T> whenever(methodCall: T): OngoingStubbing<T> = Mockito.`when`(methodCall)
k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo51Test.javadeleted 100644 → 0 +0 −222 Original line number Diff line number Diff line package com.fsck.k9.mailstore.migrations; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.fsck.k9.Account; import com.fsck.k9.mailstore.StorageManager; import org.apache.commons.io.IOUtils; import org.apache.james.mime4j.codec.QuotedPrintableInputStream; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @RunWith(RobolectricTestRunner.class) @Config(manifest = Config.NONE) public class MigrationTo51Test { private MigrationsHelper mockMigrationsHelper; private Account account; private StorageManager storageManager; private SQLiteDatabase database; @Before public void before() { mockMigrationsHelper = mock(MigrationsHelper.class); account = mock(Account.class); storageManager = StorageManager.getInstance(RuntimeEnvironment.application); storageManager.getDefaultProviderId(); when(account.getUuid()).thenReturn("001"); when(account.getLocalStorageProviderId()).thenReturn(storageManager.getDefaultProviderId()); when(mockMigrationsHelper.getContext()).thenReturn(RuntimeEnvironment.application); when(mockMigrationsHelper.getAccount()).thenReturn(account); database = createWithV50Table(); } private SQLiteDatabase createWithV50Table() { SQLiteDatabase database = SQLiteDatabase.create(null); database.execSQL("CREATE TABLE messages (" + "id INTEGER PRIMARY KEY, " + "deleted INTEGER default 0, " + "folder_id INTEGER, " + "uid TEXT, " + "subject TEXT, " + "date INTEGER, " + "sender_list TEXT, " + "to_list TEXT, " + "cc_list TEXT, " + "bcc_list TEXT, " + "reply_to_list TEXT, " + "attachment_count INTEGER, " + "internal_date INTEGER, " + "message_id TEXT, " + "preview TEXT, " + "mime_type TEXT, " + "html_content TEXT, " + "text_content TEXT, " + "flags TEXT, " + "normalized_subject_hash INTEGER, " + "empty INTEGER default 0, " + "read INTEGER default 0, " + "flagged INTEGER default 0, " + "answered INTEGER default 0 )"); database.execSQL("CREATE TABLE headers ("+ "id INTEGER PRIMARY KEY," + "name TEXT," + "value TEXT," + "message_id INTEGER)"); database.execSQL("CREATE TABLE attachments ("+ "id INTEGER PRIMARY KEY," + "size INTEGER, " + "name TEXT, " + "mime_type TEXT, " + "store_data TEXT, " + "content_uri TEXT, " + "content_id TEXT, " + "content_disposition TEXT," + "message_id INTEGER)"); return database; } private void addTextPlainMessage() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"",null,"Text","text/plain",0}); } private void addTextHtmlMessage() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html></html>","Text","text/html",0}); } private void addMultipartAlternativeMessage() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html></html>",null,"multipart/alternative",0}); } private void addMultipartMixedMessage() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html></html>","Text","multipart/mixed",0}); } private void addMultipartMixedMessageWithAttachment() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html><img src=\"testUri\" /></html>",null,"multipart/mixed",1}); database.execSQL( "INSERT INTO attachments (size, name, mime_type, store_data, " + "content_uri, content_id, content_disposition, message_id) VALUES(?,?,?,?, ?,?,?,?)", new Object[]{1, "a.jpg", "image/jpg", "a", "testUri", "content*user@host", "disposition", 1} ); } private void addMultipartMixedMessageWithAttachmentWithUnusualContentId() { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, " + "mime_type, attachment_count) " + "VALUES(?,?,?,?,?)", new Object[]{"","<html><img src=\"testUri\" /></html>",null,"multipart/mixed",1}); database.execSQL( "INSERT INTO attachments (size, name, mime_type, store_data, " + "content_uri, content_id, content_disposition, message_id) VALUES(?,?,?,?, ?,?,?,?)", new Object[]{1, "a.jpg", "image/jpg", "a", "testUri", "a$b@host", "disposition", 1} ); } @Test public void db51MigrateMessageFormat_canMigrateEmptyMessagesTable() { MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateTextPlainMessage() { addTextPlainMessage(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateTextHtmlMessage() { addTextHtmlMessage(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateMultipartAlternativeMessage() { addMultipartAlternativeMessage(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateMultipartMixedMessage() { addMultipartMixedMessage(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_canMigrateMultipartMixedMessageWithAttachment() { addMultipartMixedMessageWithAttachment(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); } @Test public void db51MigrateMessageFormat_withMultipartMixedMessageWithAttachment_stores_messagePart() { addMultipartMixedMessageWithAttachment(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); Cursor cursor = database.query("message_parts", new String[]{"data"}, "mime_type = 'text/html'", null, null, null, null); boolean isNotEmpty = cursor.moveToNext(); assertTrue(isNotEmpty); } @Test public void db51MigrateMessageFormat_withMultipartMixedMessageWithAttachment_updatesContentReference() throws IOException { addMultipartMixedMessageWithAttachment(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); Cursor cursor = database.query("message_parts", new String[]{"data"}, "mime_type = 'text/html'", null, null, null, null); boolean isNotEmpty = cursor.moveToNext(); assertTrue(isNotEmpty); String partDataAsString = IOUtils.toString( new QuotedPrintableInputStream(new ByteArrayInputStream(cursor.getBlob(0)))); assertEquals("<html><img src=\"cid:content*user@host\" /></html>", partDataAsString); } @Test public void db51MigrateMessageFormat_withMultipartMixedMessageWithAttachmentWithUnusualContentID_updatesContentReference() throws IOException { addMultipartMixedMessageWithAttachmentWithUnusualContentId(); MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper); Cursor cursor = database.query("message_parts", new String[]{"data"}, "mime_type = 'text/html'", null, null, null, null); boolean isNotEmpty = cursor.moveToNext(); assertTrue(isNotEmpty); String partDataAsString = IOUtils.toString( new QuotedPrintableInputStream(new ByteArrayInputStream(cursor.getBlob(0)))); assertEquals("<html><img src=\"cid:a$b@host\" /></html>", partDataAsString); } }
k9mail/src/test/java/com/fsck/k9/mailstore/migrations/MigrationTo51Test.kt 0 → 100644 +234 −0 Original line number Diff line number Diff line package com.fsck.k9.mailstore.migrations import android.database.sqlite.SQLiteDatabase import com.fsck.k9.Account import com.fsck.k9.mailstore.StorageManager import com.fsck.k9.whenever import org.apache.commons.io.IOUtils import org.apache.james.mime4j.codec.QuotedPrintableInputStream import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.mock import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config import java.io.ByteArrayInputStream @RunWith(RobolectricTestRunner::class) @Config(manifest = Config.NONE) class MigrationTo51Test { private lateinit var mockMigrationsHelper: MigrationsHelper private lateinit var database: SQLiteDatabase @Before fun setUp() { val storageManager = StorageManager.getInstance(RuntimeEnvironment.application) storageManager.defaultProviderId val account = mock(Account::class.java) whenever(account.uuid).thenReturn("001") whenever(account.localStorageProviderId).thenReturn(storageManager.defaultProviderId) mockMigrationsHelper = mock(MigrationsHelper::class.java) whenever(mockMigrationsHelper.context).thenReturn(RuntimeEnvironment.application) whenever(mockMigrationsHelper.account).thenReturn(account) database = createWithV50Table() } @Test fun db51MigrateMessageFormat_canMigrateEmptyMessagesTable() { MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateTextPlainMessage() { addTextPlainMessage() MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateTextHtmlMessage() { addTextHtmlMessage() MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateMultipartAlternativeMessage() { addMultipartAlternativeMessage() MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateMultipartMixedMessage() { addMultipartMixedMessage() MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_canMigrateMultipartMixedMessageWithAttachment() { addMultipartMixedMessageWithAttachment(attachmentContentId = "content*user@host") MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) } @Test fun db51MigrateMessageFormat_withMultipartMixedMessageWithAttachment_storesMessagePart() { addMultipartMixedMessageWithAttachment(attachmentContentId = "content*user@host") MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) val isNotEmpty = loadHtmlMessagePartCursor().moveToNext() assertTrue(isNotEmpty) } @Test fun db51MigrateMessageFormat_withMultipartMixedMessageWithAttachment_updatesContentReference() { addMultipartMixedMessageWithAttachment(attachmentContentId = "content*user@host") MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) assertEquals("""<html><img src="cid:content*user@host" /></html>""", htmlMessagePartBody()) } @Test fun db51MigrateMessageFormat_withMultipartMixedMessageWithAttachmentWithUnusualContentID_updatesContentReference() { addMultipartMixedMessageWithAttachment(attachmentContentId = "a\$b@host") MigrationTo51.db51MigrateMessageFormat(database, mockMigrationsHelper) assertEquals("<html><img src=\"cid:a\$b@host\" /></html>", htmlMessagePartBody()) } private fun createWithV50Table(): SQLiteDatabase { val database = SQLiteDatabase.create(null) database.execSQL(""" CREATE TABLE messages ( id INTEGER PRIMARY KEY, deleted INTEGER default 0, folder_id INTEGER, uid TEXT, subject TEXT, date INTEGER, sender_list TEXT, to_list TEXT, cc_list TEXT, bcc_list TEXT, reply_to_list TEXT, attachment_count INTEGER, internal_date INTEGER, message_id TEXT, preview TEXT, mime_type TEXT, html_content TEXT, text_content TEXT, flags TEXT, normalized_subject_hash INTEGER, empty INTEGER default 0, read INTEGER default 0, flagged INTEGER default 0, answered INTEGER default 0 ) """.trimIndent() ) database.execSQL(""" CREATE TABLE headers ( id INTEGER PRIMARY KEY, name TEXT, value TEXT, message_id INTEGER ) """.trimIndent() ) database.execSQL(""" CREATE TABLE attachments ( id INTEGER PRIMARY KEY, size INTEGER, name TEXT, mime_type TEXT, store_data TEXT, content_uri TEXT, content_id TEXT, content_disposition TEXT, message_id INTEGER ) """.trimIndent() ) return database } private fun addTextPlainMessage() { insertMessage(mimeType = "text/plain", textContent = "Text") } private fun addTextHtmlMessage() { insertMessage(mimeType = "text/html", htmlContent = "<html></html>") } private fun addMultipartAlternativeMessage() { insertMessage(mimeType = "multipart/alternative", htmlContent = "<html></html>") } private fun addMultipartMixedMessage() { insertMessage(mimeType = "multipart/mixed", htmlContent = "<html></html>", textContent = "Text") } private fun addMultipartMixedMessageWithAttachment(attachmentContentId: String) { insertMessage( mimeType = "multipart/mixed", htmlContent = """<html><img src="testUri" /></html>""", attachmentCount = 1 ) insertImageAttachment(attachmentContentId) } private fun insertMessage( mimeType: String, htmlContent: String? = null, textContent: String? = null, attachmentCount: Int = 0 ) { database.execSQL( "INSERT INTO messages (flags, html_content, text_content, mime_type, attachment_count) " + "VALUES (?, ?, ?, ?, ?)", arrayOf("", htmlContent, textContent, mimeType, attachmentCount) ) } private fun insertImageAttachment(cid: String) { database.execSQL( """ INSERT INTO attachments (size, name, mime_type, store_data, content_uri, content_id, content_disposition, message_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?) """.trimIndent(), arrayOf(1, "a.jpg", "image/jpeg", "a", "testUri", cid, "disposition", 1) ) } private fun loadHtmlMessagePartCursor() = database.query("message_parts", arrayOf("data"), "mime_type = 'text/html'", null, null, null, null) private fun htmlMessagePartBody(): String { val cursor = loadHtmlMessagePartCursor() if (!cursor.moveToNext()) { throw AssertionError("No message part found") } return IOUtils.toString( QuotedPrintableInputStream( ByteArrayInputStream(cursor.getBlob(0)) ) ) } }