/* * Copyright (c) 2022 Proton AG * * This file is part of Proton Mail. * * Proton Mail is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Proton Mail is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Proton Mail. If not, see https://www.gnu.org/licenses/. */ package ch.protonmail.android.api.models.room.contacts import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.test.InstrumentationRegistry import ch.protonmail.android.api.models.ContactEncryptedData import ch.protonmail.android.api.models.MessageRecipient import ch.protonmail.android.api.models.room.testValue import ch.protonmail.android.core.Constants import ch.protonmail.android.data.local.ContactDao import ch.protonmail.android.data.local.ContactDatabase import ch.protonmail.android.data.local.model.ContactData import ch.protonmail.android.data.local.model.ContactEmail import ch.protonmail.android.data.local.model.FullContactDetails import kotlinx.coroutines.runBlocking import org.hamcrest.Matchers.`is` import org.junit.Assert import org.junit.Rule import org.junit.Test import kotlin.test.BeforeTest internal class ContactDaoTest { private val context = InstrumentationRegistry.getTargetContext() private var databaseFactory = ContactDatabase.buildInMemoryDatabase(context) private var database = databaseFactory.getDao() private val initiallyEmptyDatabase = databaseFactory.getDao() @get:Rule var instantTaskExecutorRule = InstantTaskExecutorRule() private val contactData = listOf( ContactData(contactId = "aa", name = "aaa").apply { dbId = 2 }, ContactData(contactId = "bb", name = "bbb").apply { dbId = 4 }, ContactData(contactId = "cc", name = "ccc").apply { dbId = 3 }, ContactData(contactId = "dd", name = "ddd").apply { dbId = 1 }, ContactData(contactId = "ee", name = "eee").apply { dbId = 7 } ) private val contactEmails = listOf( ContactEmail( contactEmailId = "a", email = "a@a.com", contactId = "aa", labelIds = listOf("aaa", "aaaa", "aaaaa"), name = "ce1", lastUsedTime = 111 ), ContactEmail( contactEmailId = "b", email = "b@b.com", contactId = "bb", labelIds = listOf("bbb", "bbbb", "bbbbb"), name = "ce2", lastUsedTime = 113 ), ContactEmail( contactEmailId = "c", email = "c@c.com", contactId = "bb", labelIds = listOf("ccc", "cccc", "ccccc"), name = "ce3", lastUsedTime = 115 ), ContactEmail( contactEmailId = "d", email = "b@b.com", contactId = "dd", labelIds = listOf("ddd", "dddd", "ddddd"), name = "ce4", lastUsedTime = 114 ), ContactEmail( contactEmailId = "e", email = "e@e.com", contactId = "ee", labelIds = listOf("eee", "eeee", "eeeee"), name = "ce5", lastUsedTime = 112 ) ) private val fullContactDetails = listOf( FullContactDetails( contactId = "a", name = "aa", uid = "aaa", createTime = 1, modifyTime = 1, size = 5, defaults = 3, encryptedData = mutableListOf( ContactEncryptedData("aaaa", "aaaaa", Constants.VCardType.SIGNED), ContactEncryptedData("aaaaaa", "aaaaaaa", Constants.VCardType.SIGNED_ENCRYPTED) ) ), FullContactDetails( contactId = "b", name = "bb", uid = "bbb", createTime = 5, modifyTime = 7, size = 12, defaults = 2, encryptedData = mutableListOf( ContactEncryptedData("bbbb", "bbbbb", Constants.VCardType.SIGNED), ContactEncryptedData("bbbbbb", "bbbbbbb", Constants.VCardType.SIGNED_ENCRYPTED) ) ), FullContactDetails( contactId = "c", name = "cc", uid = "ccc", createTime = 12, modifyTime = 1100, size = 2, defaults = 123, encryptedData = mutableListOf( ContactEncryptedData("cccc", "ccccc", Constants.VCardType.SIGNED), ContactEncryptedData("cccccc", "ccccccc", Constants.VCardType.SIGNED_ENCRYPTED) ) ), FullContactDetails( contactId = "d", name = "dd", uid = "ddd", createTime = 3, modifyTime = 12, size = 112, defaults = 31, encryptedData = mutableListOf( ContactEncryptedData("dddd", "ddddd", Constants.VCardType.SIGNED), ContactEncryptedData("dddddd", "ddddddd", Constants.VCardType.SIGNED_ENCRYPTED) ) ), FullContactDetails( contactId = "e", name = "ee", uid = "eee", createTime = 1, modifyTime = 131, size = 12, defaults = 321, encryptedData = mutableListOf( ContactEncryptedData("eeee", "eeeee", Constants.VCardType.SIGNED), ContactEncryptedData("eeeeee", "eeeeeee", Constants.VCardType.SIGNED_ENCRYPTED) ) ), FullContactDetails("f") ) private fun ContactDao.populate() { runBlocking { saveAllContactsData(contactData) saveAllContactsEmails(contactEmails) } fullContactDetails.forEach(this::insertFullContactDetailsBlocking) } private fun assertDatabaseState( expectedContactData: Iterable = contactData, expectedContactEmails: Iterable = contactEmails, expectedFullContactDetails: Iterable = fullContactDetails ) { val expectedContactDataSet = expectedContactData.toSet() val expectedContactEmailsSet = expectedContactEmails.toSet() //hack as encrypted data has equals not defined val expectedFullContactDetailsSet = expectedFullContactDetails .map { it.apply { encryptedData = mutableListOf() } }.toSet() val actualContactDataSet = database.findAllContactDataAsync().testValue!!.toSet() val actualContactEmailsSet = database.findAllContactsEmailsAsync().testValue!!.toSet() //hack as encrypted data has equals not defined val actualFullContactDetailsSet = expectedFullContactDetails .map(FullContactDetails::contactId) .map(database::findFullContactDetailsByIdBlocking) .map { it?.apply { encryptedData = mutableListOf() } } .toSet() Assert.assertEquals(expectedContactDataSet, actualContactDataSet) Assert.assertEquals(expectedContactEmailsSet, actualContactEmailsSet) Assert.assertEquals(expectedFullContactDetailsSet, actualFullContactDetailsSet) } @BeforeTest fun setUp() { database.populate() } @Test fun findContactDataById() { val expected = contactData[3] val actual = database.findContactDataByIdBlocking(expected.contactId!!) Assert.assertEquals(expected, actual) assertDatabaseState() } @Test fun findContactDataByDbId() { val expected = contactData[3] val actual = database.findContactDataByDbId(expected.dbId!!) Assert.assertEquals(expected, actual) assertDatabaseState() } @Test fun findAllContactDataAsync() { val expected = contactData val actual = database.findAllContactDataAsync().testValue Assert.assertEquals(expected, actual) assertDatabaseState() } @Test fun clearContactDataCache() { database.clearContactDataCache() assertDatabaseState(expectedContactData = emptyList()) } @Test fun saveContactData() { val inserted = ContactData("z", "zz") val expected = contactData + inserted database.saveContactDataBlocking(inserted) assertDatabaseState(expectedContactData = expected) } @Test fun saveAllContactsData() { runBlocking { val inserted = listOf( ContactData("y", "yy"), ContactData("z", "zz") ) val expected = contactData + inserted database.saveAllContactsData(inserted) assertDatabaseState(expectedContactData = expected) } } @Test fun saveAllContactsData1() { val inserted = listOf( ContactData("y", "yy"), ContactData("z", "zz") ) val expected = contactData + inserted database.saveAllContactsData(*inserted.toTypedArray()) assertDatabaseState(expectedContactData = expected) } @Test fun deleteContactData() { val deleted = contactData[3] val expected = contactData - deleted database.deleteContactData(deleted) assertDatabaseState(expectedContactData = expected) } @Test fun deleteContactsData() { val deleted = listOf(contactData[3], contactData[1]) val expected = contactData - deleted database.deleteContactsData(deleted) assertDatabaseState(expectedContactData = expected) } @Test fun findContactEmailById() = runBlocking{ val expected = contactEmails[2] val actual = database.findContactEmailById(expected.contactEmailId!!) Assert.assertEquals(expected, actual) assertDatabaseState() } @Test fun findContactEmailByEmail() { val expected = contactEmails[2] val actual = database.findContactEmailByEmailBlocking(expected.email) Assert.assertEquals(expected, actual) assertDatabaseState() } @Test fun findContactEmailsByContactId() { val contactId = contactEmails[2].contactId!! val expected = contactEmails.filter { it.contactId == contactId } val actual = database.findContactEmailsByContactIdBlocking(contactId) Assert.assertEquals(expected, actual) assertDatabaseState() } @Test fun findAllContactsEmailsAsync() { val expected = contactEmails.toSet() val actual = database.findAllContactsEmailsAsync().testValue?.toSet() Assert.assertEquals(expected, actual) assertDatabaseState() } @Test fun findAllMessageRecipients() { // given val expected = contactEmails .sortedByDescending { it.lastUsedTime } .map { contactEmail -> MessageRecipient(contactData.find { it.contactId == contactEmail.contactId }?.name, contactEmail.email) }.toSet() // when val actual = database.findAllMessageRecipients().blockingFirst().toSet() // then Assert.assertEquals(expected, actual) assertDatabaseState() } @Test fun clearByEmail() { val deletedEmail = contactEmails[1].email val expected = contactEmails.filterNot { it.email == deletedEmail } database.clearByEmailBlocking(deletedEmail) assertDatabaseState(expectedContactEmails = expected) } @Test fun clearContactEmailsCache() { val expected = emptyList() database.clearContactEmailsCache() assertDatabaseState(expectedContactEmails = expected) } @Test fun deleteContactEmail() { val deleted = listOf(contactEmails[3], contactEmails[1]) val expected = contactEmails - deleted database.deleteContactEmail(*deleted.toTypedArray()) assertDatabaseState(expectedContactEmails = expected) } @Test fun deleteAllContactsEmails() { val deleted = listOf(contactEmails[3], contactEmails[1]) val expected = contactEmails - deleted database.deleteAllContactsEmailsBlocking(deleted) assertDatabaseState(expectedContactEmails = expected) } @Test fun saveContactEmail() = runBlocking { val inserted = ContactEmail( "z", "z@z.com", contactId = "zzz", labelIds = listOf("zzzz", "zzzzz", "zzzzzz"), name = "ce1", lastUsedTime = 116 ) val expected = contactEmails + inserted database.saveContactEmail(inserted) assertDatabaseState(expectedContactEmails = expected) } @Test fun saveAllContactsEmails() { val inserted = listOf( ContactEmail( "y", "y@y.com", contactId = "yyy", labelIds = listOf("yyyy", "yyyyy", "yyyyyy"), name = "ce1", lastUsedTime = 118 ), ContactEmail( "z", "z@z.com", contactId = "zzz", labelIds = listOf("zzzz", "zzzzz", "zzzzzz"), name = "ce2", lastUsedTime = 116 ) ) val expected = contactEmails + inserted database.saveAllContactsEmailsBlocking(inserted) assertDatabaseState(expectedContactEmails = expected) } @Test fun saveAllContactsEmails1() = runBlocking{ val inserted = listOf( ContactEmail( "y", "y@y.com", contactId = "yyy", labelIds = listOf("yyyy", "yyyyy", "yyyyyy"), name = "ce1", lastUsedTime = 118 ), ContactEmail( "z", "z@z.com", contactId = "zzz", labelIds = listOf("zzzz", "zzzzz", "zzzzzz"), name = "ce2", lastUsedTime = 116 ) ) val expected = contactEmails + inserted database.saveAllContactsEmails(inserted) assertDatabaseState(expectedContactEmails = expected) } @Test fun insertFullContactDetails() { val inserted = FullContactDetails( contactId = "z", name = "zz", uid = "zzz", createTime = 1, modifyTime = 131, size = 12, defaults = 321, encryptedData = mutableListOf( ContactEncryptedData("zzzz", "zzzzz", Constants.VCardType.SIGNED), ContactEncryptedData("zzzzzz", "zzzzzzz", Constants.VCardType.SIGNED_ENCRYPTED) ) ) val expected = fullContactDetails + inserted database.insertFullContactDetailsBlocking(inserted) assertDatabaseState(expectedFullContactDetails = expected) } @Test fun findFullContactDetailsById() { val expected = fullContactDetails[1] val actual = database.findFullContactDetailsByIdBlocking(expected.contactId) Assert.assertThat( actual, `is`(FullContactsDetailsMatcher(expected)) ) assertDatabaseState() } @Test fun clearFullContactDetailsCache() { val expected = emptyList() database.clearFullContactDetailsCache() assertDatabaseState(expectedFullContactDetails = expected) } @Test fun deleteFullContactsDetails() { val deleted = fullContactDetails[1] val expected = fullContactDetails - deleted database.deleteFullContactsDetails(deleted) val found = database.findFullContactDetailsByIdBlocking(deleted.contactId) Assert.assertNull(found) assertDatabaseState(expectedFullContactDetails = expected) } @Test fun testContactEmailsConverter() = runBlocking { val email1 = ContactEmail( "e1", "1@1.1", "a", labelIds = listOf("la", "lc"), lastUsedTime = 121 ) val email2 = ContactEmail( "e2", "2@2.2", "b", labelIds = listOf("la", "lc"), lastUsedTime = 123 ) val email3 = ContactEmail( "e3", "3@3.3", "c", labelIds = listOf("la", "lc"), lastUsedTime = 122 ) initiallyEmptyDatabase.saveAllContactsEmails(listOf(email1, email2, email3)) val emailFromDb = initiallyEmptyDatabase.findContactEmailById("e1") Assert.assertNotNull(emailFromDb) val listOfGroups = emailFromDb?.labelIds Assert.assertNotNull(listOfGroups) val expectedGroupId = "la" Assert.assertEquals(expectedGroupId, listOfGroups?.get(0)) } }