Fix unit tests (#817)

* Fix unit tests

* Remove android tests
pull/1706/head
SessionHero01 4 months ago committed by GitHub
parent 41f57ae18d
commit b961ec4a6c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1,143 +0,0 @@
package network.loki.messenger
import androidx.compose.ui.test.hasContentDescriptionExactly
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import org.hamcrest.CoreMatchers.*
import org.hamcrest.MatcherAssert.*
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.groups.compose.CreateGroup
import org.thoughtcrime.securesms.groups.ViewState
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
@RunWith(AndroidJUnit4::class)
@SmallTest
class CreateGroupTests {
@get:Rule
val composeTest = createComposeRule()
@Test
fun testNavigateToCreateGroup() {
val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
// Accessibility IDs
val nameDesc = application.getString(R.string.AccessibilityId_closed_group_edit_group_name)
val buttonDesc = application.getString(R.string.AccessibilityId_create_closed_group_create_button)
var backPressed = false
var closePressed = false
composeTest.setContent {
PreviewTheme {
CreateGroup(
viewState = ViewState.DEFAULT,
onBack = { backPressed = true },
onClose = { closePressed = true },
onContactItemClicked = {},
updateState = {}
)
}
}
with(composeTest) {
onNode(hasContentDescriptionExactly(nameDesc)).performTextInput("Name")
onNode(hasContentDescriptionExactly(buttonDesc)).performClick()
}
assertThat(backPressed, equalTo(false))
assertThat(closePressed, equalTo(false))
}
@Test
fun testFailToCreate() {
val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
// Accessibility IDs
val nameDesc = application.getString(R.string.AccessibilityId_closed_group_edit_group_name)
val buttonDesc = application.getString(R.string.AccessibilityId_create_closed_group_create_button)
var backPressed = false
var closePressed = false
composeTest.setContent {
PreviewTheme {
CreateGroup(
viewState = ViewState.DEFAULT,
onBack = { backPressed = true },
onClose = { closePressed = true },
updateState = {},
onContactItemClicked = {}
)
}
}
with(composeTest) {
onNode(hasContentDescriptionExactly(nameDesc)).performTextInput("")
onNode(hasContentDescriptionExactly(buttonDesc)).performClick()
}
assertThat(backPressed, equalTo(false))
assertThat(closePressed, equalTo(false))
}
@Test
fun testBackButton() {
val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
// Accessibility IDs
val backDesc = application.getString(R.string.new_conversation_dialog_back_button_content_description)
var backPressed = false
composeTest.setContent {
PreviewTheme {
CreateGroup(
viewState = ViewState.DEFAULT,
onBack = { backPressed = true },
onClose = {},
onContactItemClicked = {},
updateState = {}
)
}
}
with (composeTest) {
onNode(hasContentDescriptionExactly(backDesc)).performClick()
}
assertThat(backPressed, equalTo(true))
}
@Test
fun testCloseButton() {
val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
// Accessibility IDs
val closeDesc = application.getString(R.string.new_conversation_dialog_close_button_content_description)
var closePressed = false
composeTest.setContent {
PreviewTheme {
CreateGroup(
viewState = ViewState.DEFAULT,
onBack = { },
onClose = { closePressed = true },
onContactItemClicked = {},
updateState = {}
)
}
}
with (composeTest) {
onNode(hasContentDescriptionExactly(closeDesc)).performClick()
}
assertThat(closePressed, equalTo(true))
}
}

@ -1,257 +0,0 @@
package network.loki.messenger
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.hasContentDescriptionExactly
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.groups.compose.EditGroup
import org.thoughtcrime.securesms.groups.EditGroupViewState
import org.thoughtcrime.securesms.groups.MemberState
import org.thoughtcrime.securesms.groups.MemberViewModel
import org.thoughtcrime.securesms.ui.theme.PreviewTheme
@RunWith(AndroidJUnit4::class)
@SmallTest
class EditGroupTests {
@get:Rule
val composeTest = createComposeRule()
val oneMember = MemberViewModel(
"Test User",
"05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234",
MemberState.InviteSent,
false
)
val twoMember = MemberViewModel(
"Test User 2",
"05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1235",
MemberState.InviteFailed,
false
)
val threeMember = MemberViewModel(
"Test User 3",
"05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1236",
MemberState.Member,
false
)
val fourMember = MemberViewModel(
"Test User 4",
"05abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1237",
MemberState.Admin,
false
)
@Test
fun testDisplaysNameAndDesc() {
val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
// Accessibility IDs
val nameDesc = application.getString(R.string.AccessibilityId_group_name)
val descriptionDesc = application.getString(R.string.AccessibilityId_group_description)
with (composeTest) {
val state = EditGroupViewState(
"TestGroup",
"TestDesc",
emptyList(),
false
)
setContent {
PreviewTheme {
EditGroup(
onBackClick = {},
onAddMemberClick = {},
onResendInviteClick = {},
onPromoteClick = {},
onRemoveClick = {},
onEditName = {},
onMemberSelected = {},
viewState = state
)
}
}
onNode(hasContentDescriptionExactly(nameDesc)).assertTextEquals("TestGroup")
onNode(hasContentDescriptionExactly(descriptionDesc)).assertTextEquals("TestDesc")
}
}
@Test
fun testDisplaysReinviteProperly() {
val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
// Accessibility IDs
val reinviteDesc = application.getString(R.string.AccessibilityId_reinvite_member)
val promoteDesc = application.getString(R.string.AccessibilityId_promote_member)
var reinvited = false
with (composeTest) {
val state = EditGroupViewState(
"TestGroup",
"TestDesc",
listOf(
twoMember
),
// reinvite only shows for admin users
true
)
setContent {
PreviewTheme {
EditGroup(
onBackClick = {},
onAddMemberClick = {},
onResendInviteClick = { reinvited = true },
onPromoteClick = {},
onRemoveClick = {},
onEditName = {},
onMemberSelected = {},
viewState = state
)
}
}
onNodeWithContentDescription(reinviteDesc).assertIsDisplayed().performClick()
onNodeWithContentDescription(promoteDesc).assertDoesNotExist()
assertThat(reinvited, equalTo(true))
}
}
@Test
fun testDisplaysRegularMemberProperly() {
val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
// Accessibility IDs
val reinviteDesc = application.getString(R.string.AccessibilityId_reinvite_member)
val promoteDesc = application.getString(R.string.AccessibilityId_promote_member)
var promoted = false
with (composeTest) {
val state = EditGroupViewState(
"TestGroup",
"TestDesc",
listOf(
threeMember
),
// reinvite only shows for admin users
true
)
setContent {
PreviewTheme {
EditGroup(
onBackClick = {},
onAddMemberClick = {},
onResendInviteClick = {},
onPromoteClick = { promoted = true },
onRemoveClick = {},
onEditName = {},
onMemberSelected = {},
viewState = state
)
}
}
onNodeWithContentDescription(reinviteDesc).assertDoesNotExist()
onNodeWithContentDescription(promoteDesc).assertIsDisplayed().performClick()
assertThat(promoted, equalTo(true))
}
}
@Test
fun testDisplaysAdminProperly() {
val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
// Accessibility IDs
val reinviteDesc = application.getString(R.string.AccessibilityId_reinvite_member)
val promoteDesc = application.getString(R.string.AccessibilityId_promote_member)
with (composeTest) {
val state = EditGroupViewState(
"TestGroup",
"TestDesc",
listOf(
fourMember
),
// reinvite only shows for admin users
true
)
setContent {
PreviewTheme {
EditGroup(
onBackClick = {},
onAddMemberClick = {},
onResendInviteClick = {},
onPromoteClick = {},
onRemoveClick = {},
onEditName = {},
onMemberSelected = {},
viewState = state
)
}
}
onNodeWithContentDescription(reinviteDesc).assertDoesNotExist()
onNodeWithContentDescription(promoteDesc).assertDoesNotExist()
}
}
@Test
fun testDisplaysPendingInviteProperly() {
val application = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
// Accessibility IDs
val reinviteDesc = application.getString(R.string.AccessibilityId_reinvite_member)
val promoteDesc = application.getString(R.string.AccessibilityId_promote_member)
val stateDesc = application.getString(R.string.AccessibilityId_member_state)
val memberDesc = application.getString(R.string.AccessibilityId_contact)
with (composeTest) {
val state = EditGroupViewState(
"TestGroup",
"TestDesc",
listOf(
oneMember
),
// reinvite only shows for admin users
true
)
setContent {
PreviewTheme {
EditGroup(
onBackClick = {},
onAddMemberClick = {},
onResendInviteClick = {},
onPromoteClick = {},
onRemoveClick = {},
onEditName = {},
onMemberSelected = {},
viewState = state
)
}
}
onNodeWithContentDescription(reinviteDesc).assertDoesNotExist()
onNodeWithContentDescription(promoteDesc).assertDoesNotExist()
onNodeWithContentDescription(stateDesc, useUnmergedTree = true).assertTextEquals("InviteSent")
onNodeWithContentDescription(memberDesc, useUnmergedTree = true).assertTextEquals("Test User")
}
}
}

@ -1,183 +0,0 @@
package network.loki.messenger
import android.app.Instrumentation
import android.view.View
import android.content.ClipboardManager
import android.content.Context
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import network.loki.messenger.util.sendMessage
import network.loki.messenger.util.waitFor
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import com.adevinta.android.barista.interaction.PermissionGranter
import com.bumptech.glide.Glide
import network.loki.messenger.util.InputBarButtonDrawableMatcher.Companion.inputButtonWithDrawable
import org.hamcrest.Matchers.allOf
import org.hamcrest.Matchers.not
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar
import org.thoughtcrime.securesms.home.HomeActivity
/**
* Currently not used as part of our CI/Deployment processes !!!!
*/
@RunWith(AndroidJUnit4::class)
@SmallTest
class HomeActivityTests {
@get:Rule
var activityRule = ActivityScenarioRule(HomeActivity::class.java)
private val activityMonitor = Instrumentation.ActivityMonitor(ConversationActivityV2::class.java.name, null, false)
private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
private val context = InstrumentationRegistry.getInstrumentation().targetContext
@Before
fun setUp() {
InstrumentationRegistry.getInstrumentation().addMonitor(activityMonitor)
}
@After
fun tearDown() {
InstrumentationRegistry.getInstrumentation().removeMonitor(activityMonitor)
}
private fun sendMessage(messageToSend: String, linkPreview: LinkPreview? = null) {
// assume in chat activity
onView(allOf(isDescendantOfA(withId(R.id.inputBar)),withId(R.id.inputBarEditText))).perform(ViewActions.replaceText(messageToSend))
if (linkPreview != null) {
val activity = activityMonitor.waitForActivity() as ConversationActivityV2
val glide = Glide.with(activity)
activity.findViewById<InputBar>(R.id.inputBar).updateLinkPreviewDraft(glide, linkPreview)
}
onView(allOf(isDescendantOfA(withId(R.id.inputBar)),inputButtonWithDrawable(R.drawable.ic_arrow_up))).perform(ViewActions.click())
// TODO: text can flaky on cursor reload, figure out a better way to wait for the UI to settle with new data
onView(isRoot()).perform(waitFor(500))
}
private fun objectFromDesc(id: Int) = device.findObject(By.desc(context.getString(id)))
private fun setupLoggedInState(hasViewedSeed: Boolean = false) {
// landing activity
objectFromDesc(R.string.onboardingAccountCreate).click()
// display name selection
objectFromDesc(R.string.displayNameEnter).click()
device.pressKeyCode(65)
device.pressKeyCode(66)
device.pressKeyCode(67)
// Continue with display name
objectFromDesc(R.string.theContinue).click()
// Continue with default push notification setting
objectFromDesc(R.string.theContinue).click()
// PN select
if (hasViewedSeed) {
// has viewed seed is set to false after register activity
TextSecurePreferences.setHasViewedSeed(InstrumentationRegistry.getInstrumentation().targetContext, true)
}
// allow notification permission
PermissionGranter.allowPermissionsIfNeeded(Manifest.permission.POST_NOTIFICATIONS)
}
/* private fun goToMyChat() {
onView(withId(R.id.newConversationButton)).perform(ViewActions.click())
onView(withId(R.id.createPrivateChatButton)).perform(ViewActions.click())
// new chat
Thread.sleep(500)
onView(withId(R.id.publicKeyEditText)).perform(ViewActions.closeSoftKeyboard())
onView(withId(R.id.copyButton)).perform(ViewActions.click())
val context = InstrumentationRegistry.getInstrumentation().targetContext
lateinit var copied: String
InstrumentationRegistry.getInstrumentation().runOnMainSync {
val clipboardManager = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
copied = clipboardManager.primaryClip!!.getItemAt(0).text.toString()
}
onView(withId(R.id.publicKeyEditText)).perform(ViewActions.typeText(copied))
onView(withId(R.id.publicKeyEditText)).perform(ViewActions.closeSoftKeyboard())
onView(withId(R.id.createPrivateChatButton)).perform(ViewActions.click())
}*/
@Test
fun testLaunches_dismiss_seedView() {
setupLoggedInState()
objectFromDesc(R.string.theContinue).click()
objectFromDesc(R.string.copy).click()
pressBack()
onView(withId(R.id.seedReminderView)).check(matches(not(isDisplayed())))
}
@Test
fun testIsVisible_seedView() {
setupLoggedInState()
onView(withId(R.id.seedReminderView)).check(matches(isCompletelyDisplayed()))
}
@Test
fun testIsVisible_alreadyDismissed_seedView() {
setupLoggedInState(hasViewedSeed = true)
onView(withId(R.id.seedReminderView)).check(matches(not(isDisplayed())))
}
/* @Test
fun testChat_withSelf() {
setupLoggedInState()
goToMyChat()
TextSecurePreferences.setLinkPreviewsEnabled(context, true)
with (activityMonitor.waitForActivity() as ConversationActivityV2) {
sendMessage("howdy")
sendMessage("test")
// tests url rewriter doesn't crash
sendMessage("https://www.getsession.org?random_query_parameter=testtesttesttesttesttesttesttest&other_query_parameter=testtesttesttesttesttesttesttest")
sendMessage("https://www.ámazon.com")
}
}
@Test
fun testChat_displaysCorrectUrl() {
setupLoggedInState()
goToMyChat()
TextSecurePreferences.setLinkPreviewsEnabled(InstrumentationRegistry.getInstrumentation().targetContext, true)
// given the link url text
val url = "https://www.ámazon.com"
with (activityMonitor.waitForActivity() as ConversationActivityV2) {
sendMessage(url, LinkPreview(url, "amazon", Optional.absent()))
}
// when the URL span is clicked
onView(withSubstring(url)).perform(ViewActions.click())
// then the URL dialog should be displayed with a known punycode url
val amazonPuny = "https://www.xn--mazon-wqa.com/"
val dialogPromptText = InstrumentationRegistry.getInstrumentation().targetContext.getString(R.string.dialog_open_url_explanation, amazonPuny)
onView(isRoot()).perform(waitFor(1000)) // no other way for this to work apparently
onView(withText(dialogPromptText)).check(matches(isDisplayed()))
}*/
}

@ -1,181 +0,0 @@
package network.loki.messenger
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import androidx.test.platform.app.InstrumentationRegistry
import network.loki.messenger.libsession_util.ConfigBase
import network.loki.messenger.libsession_util.Contacts
import network.loki.messenger.libsession_util.ConversationVolatileConfig
import network.loki.messenger.libsession_util.util.Contact
import network.loki.messenger.libsession_util.util.Conversation
import network.loki.messenger.libsession_util.util.ExpiryMode
import network.loki.messenger.util.applySpiedStorage
import network.loki.messenger.util.maybeGetUserInfo
import network.loki.messenger.util.randomSeedBytes
import network.loki.messenger.util.randomSessionId
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.CoreMatchers.instanceOf
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.argThat
import org.mockito.kotlin.argWhere
import org.mockito.kotlin.eq
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.KeyHelper
import org.session.libsignal.utilities.hexEncodedPublicKey
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
@RunWith(AndroidJUnit4::class)
@SmallTest
class LibSessionTests {
private var fakeHashI = 0
private val nextFakeHash: String
get() = "fakehash${fakeHashI++}"
private fun buildContactMessage(contactList: List<Contact>): ByteArray {
val (key,_) = maybeGetUserInfo()!!
val contacts = Contacts.newInstance(key)
contactList.forEach { contact ->
contacts.set(contact)
}
return contacts.push().config
}
private fun buildVolatileMessage(conversations: List<Conversation>): ByteArray {
val (key, _) = maybeGetUserInfo()!!
val volatile = ConversationVolatileConfig.newInstance(key)
conversations.forEach { conversation ->
volatile.set(conversation)
}
return volatile.push().config
}
private fun fakePollNewConfig(configBase: ConfigBase, toMerge: ByteArray) {
configBase.merge(nextFakeHash to toMerge)
MessagingModuleConfiguration.shared.configFactory.persist(configBase, System.currentTimeMillis())
}
@Before
fun setupUser() {
PreferenceManager.getDefaultSharedPreferences(InstrumentationRegistry.getInstrumentation().targetContext.applicationContext).edit {
putBoolean(TextSecurePreferences.HAS_FORCED_NEW_CONFIG, true).apply()
}
val newBytes = randomSeedBytes().toByteArray()
val context = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext
val kp = KeyPairUtilities.generate(newBytes)
KeyPairUtilities.store(context, kp.seed, kp.ed25519KeyPair, kp.x25519KeyPair)
val registrationID = KeyHelper.generateRegistrationId(false)
TextSecurePreferences.setLocalRegistrationId(context, registrationID)
TextSecurePreferences.setLocalNumber(context, kp.x25519KeyPair.hexEncodedPublicKey)
TextSecurePreferences.setRestorationTime(context, 0)
TextSecurePreferences.setHasViewedSeed(context, false)
}
@Test
fun migration_one_to_ones() {
val applicationContext = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
val storage = applicationContext.applySpiedStorage()
val newContactId = randomSessionId()
val singleContact = Contact(
id = newContactId,
approved = true,
expiryMode = ExpiryMode.NONE
)
val newContactMerge = buildContactMessage(listOf(singleContact))
val contacts = MessagingModuleConfiguration.shared.configFactory.contacts!!
fakePollNewConfig(contacts, newContactMerge)
verify(storage).addLibSessionContacts(argThat {
first().let { it.id == newContactId && it.approved } && size == 1
}, any())
verify(storage).setRecipientApproved(argThat { address.serialize() == newContactId }, eq(true))
}
@Test
fun test_expected_configs() {
val app = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
val storageSpy = spy(app.storage)
app.storage = storageSpy
val randomRecipient = randomSessionId()
val newContact = Contact(
id = randomRecipient,
approved = true,
expiryMode = ExpiryMode.AfterSend(1000)
)
val newConvo = Conversation.OneToOne(
randomRecipient,
SnodeAPI.nowWithOffset,
false
)
val volatiles = MessagingModuleConfiguration.shared.configFactory.convoVolatile!!
val contacts = MessagingModuleConfiguration.shared.configFactory.contacts!!
val newContactMerge = buildContactMessage(listOf(newContact))
val newVolatileMerge = buildVolatileMessage(listOf(newConvo))
fakePollNewConfig(contacts, newContactMerge)
fakePollNewConfig(volatiles, newVolatileMerge)
verify(storageSpy).setExpirationConfiguration(argWhere { config ->
config.expiryMode is ExpiryMode.AfterSend
&& config.expiryMode.expirySeconds == 1000L
})
val threadId = storageSpy.getThreadId(Address.fromSerialized(randomRecipient))!!
val newExpiry = storageSpy.getExpirationConfiguration(threadId)!!
assertThat(newExpiry.expiryMode, instanceOf(ExpiryMode.AfterSend::class.java))
assertThat(newExpiry.expiryMode.expirySeconds, equalTo(1000))
assertThat(newExpiry.expiryMode.expiryMillis, equalTo(1000000))
}
@Test
fun test_overwrite_config() {
val app = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
val storageSpy = spy(app.storage)
app.storage = storageSpy
// Initial state
val randomRecipient = randomSessionId()
val currentContact = Contact(
id = randomRecipient,
approved = true,
expiryMode = ExpiryMode.NONE
)
val newConvo = Conversation.OneToOne(
randomRecipient,
SnodeAPI.nowWithOffset,
false
)
val volatiles = MessagingModuleConfiguration.shared.configFactory.convoVolatile!!
val contacts = MessagingModuleConfiguration.shared.configFactory.contacts!!
val newContactMerge = buildContactMessage(listOf(currentContact))
val newVolatileMerge = buildVolatileMessage(listOf(newConvo))
fakePollNewConfig(contacts, newContactMerge)
fakePollNewConfig(volatiles, newVolatileMerge)
verify(storageSpy).setExpirationConfiguration(argWhere { config ->
config.expiryMode == ExpiryMode.NONE
})
val threadId = storageSpy.getThreadId(Address.fromSerialized(randomRecipient))!!
val currentExpiryConfig = storageSpy.getExpirationConfiguration(threadId)!!
assertThat(currentExpiryConfig.expiryMode, equalTo(ExpiryMode.NONE))
assertThat(currentExpiryConfig.expiryMode.expirySeconds, equalTo(0))
assertThat(currentExpiryConfig.expiryMode.expiryMillis, equalTo(0))
// Set new state and overwrite
val updatedContact = currentContact.copy(expiryMode = ExpiryMode.AfterSend(1000))
val updateContactMerge = buildContactMessage(listOf(updatedContact))
fakePollNewConfig(contacts, updateContactMerge)
val updatedExpiryConfig = storageSpy.getExpirationConfiguration(threadId)!!
assertThat(updatedExpiryConfig.expiryMode, instanceOf(ExpiryMode.AfterSend::class.java))
assertThat(updatedExpiryConfig.expiryMode.expirySeconds, equalTo(1000))
}
}

@ -1,166 +0,0 @@
package network.loki.messenger
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.goterl.lazysodium.utils.Key
import com.goterl.lazysodium.utils.KeyPair
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.session.libsession.messaging.utilities.SodiumUtilities
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Hex
import org.session.libsignal.utilities.toHexString
@RunWith(AndroidJUnit4::class)
class SodiumUtilitiesTest {
private val publicKey: String = "88672ccb97f40bb57238989226cf429b575ba355443f47bc76c5ab144a96c65b"
private val privateKey: String = "30d796c1ddb4dc455fd998a98aa275c247494a9a7bde9c1fee86ae45cd585241"
private val edKeySeed: String = "c010d89eccbaf5d1c6d19df766c6eedf965d4a28a56f87c9fc819edb59896dd9"
private val edPublicKey: String = "bac6e71efd7dfa4a83c98ed24f254ab2c267f9ccdb172a5280a0444ad24e89cc"
private val edSecretKey: String = "c010d89eccbaf5d1c6d19df766c6eedf965d4a28a56f87c9fc819edb59896dd9bac6e71efd7dfa4a83c98ed24f254ab2c267f9ccdb172a5280a0444ad24e89cc"
private val blindedPublicKey: String = "98932d4bccbe595a8789d7eb1629cefc483a0eaddc7e20e8fe5c771efafd9af5"
private val serverPublicKey: String = "c3b3c6f32f0ab5a57f853cc4f30f5da7fda5624b0c77b3fb0829de562ada081d"
private val edKeyPair = KeyPair(Key.fromHexString(edPublicKey), Key.fromHexString(edSecretKey))
@Test
fun generateBlindingFactorSuccess() {
val result = SodiumUtilities.generateBlindingFactor(serverPublicKey)
assertThat(result?.toHexString(), equalTo("84e3eb75028a9b73fec031b7448e322a68ca6485fad81ab1bead56f759ebeb0f"))
}
@Test
fun generateBlindingFactorFailure() {
val result = SodiumUtilities.generateBlindingFactor("Test")
assertNull(result?.toHexString())
}
@Test
fun blindedKeyPairSuccess() {
val result = SodiumUtilities.blindedKeyPair(serverPublicKey, edKeyPair)!!
assertThat(result.publicKey.asHexString.lowercase(), equalTo(blindedPublicKey))
assertThat(result.secretKey.asHexString.take(64).lowercase(), equalTo("16663322d6b684e1c9dcc02b9e8642c3affd3bc431a9ea9e63dbbac88ce7a305"))
}
@Test
fun blindedKeyPairFailurePublicKeyLength() {
val result = SodiumUtilities.blindedKeyPair(
serverPublicKey,
KeyPair(Key.fromHexString(edPublicKey.take(4)), Key.fromHexString(edKeySeed))
)
assertNull(result)
}
@Test
fun blindedKeyPairFailureSecretKeyLength() {
val result = SodiumUtilities.blindedKeyPair(
serverPublicKey,
KeyPair(Key.fromHexString(edPublicKey), Key.fromHexString(edSecretKey.take(4)))
)
assertNull(result)
}
@Test
fun blindedKeyPairFailureBlindingFactor() {
val result = SodiumUtilities.blindedKeyPair("Test", edKeyPair)
assertNull(result)
}
@Test
fun sogsSignature() {
val expectedSignature = "dcc086abdd2a740d9260b008fb37e12aa0ff47bd2bd9e177bbbec37fd46705a9072ce747bda66c788c3775cdd7ad60ad15a478e0886779aad5d795fd7bf8350d"
val result = SodiumUtilities.sogsSignature(
"TestMessage".toByteArray(),
Hex.fromStringCondensed(edSecretKey),
Hex.fromStringCondensed("44d82cc15c0a5056825cae7520b6b52d000a23eb0c5ed94c4be2d9dc41d2d409"),
Hex.fromStringCondensed("0bb7815abb6ba5142865895f3e5286c0527ba4d31dbb75c53ce95e91ffe025a2")
)
assertThat(result?.toHexString(), equalTo(expectedSignature))
}
@Test
fun combineKeysSuccess() {
val result = SodiumUtilities.combineKeys(
Hex.fromStringCondensed(edSecretKey),
Hex.fromStringCondensed(edPublicKey)
)
assertThat(result?.toHexString(), equalTo("1159b5d0fcfba21228eb2121a0f59712fa8276fc6e5547ff519685a40b9819e6"))
}
@Test
fun combineKeysFailure() {
val result = SodiumUtilities.combineKeys(
SodiumUtilities.generatePrivateKeyScalar(Hex.fromStringCondensed(edSecretKey))!!,
Hex.fromStringCondensed(publicKey)
)
assertNull(result?.toHexString())
}
@Test
fun sharedBlindedEncryptionKeySuccess() {
val result = SodiumUtilities.sharedBlindedEncryptionKey(
Hex.fromStringCondensed(edSecretKey),
Hex.fromStringCondensed(blindedPublicKey),
Hex.fromStringCondensed(publicKey),
Hex.fromStringCondensed(blindedPublicKey)
)
assertThat(result?.toHexString(), equalTo("388ee09e4c356b91f1cce5cc0aa0cf59e8e8cade69af61685d09c2d2731bc99e"))
}
@Test
fun sharedBlindedEncryptionKeyFailure() {
val result = SodiumUtilities.sharedBlindedEncryptionKey(
Hex.fromStringCondensed(edSecretKey),
Hex.fromStringCondensed(publicKey),
Hex.fromStringCondensed(edPublicKey),
Hex.fromStringCondensed(publicKey)
)
assertNull(result?.toHexString())
}
@Test
fun accountIdSuccess() {
val result = SodiumUtilities.accountId("05$publicKey", "15$blindedPublicKey", serverPublicKey)
assertTrue(result)
}
@Test
fun accountIdFailureInvalidAccountId() {
val result = SodiumUtilities.accountId("AB$publicKey", "15$blindedPublicKey", serverPublicKey)
assertFalse(result)
}
@Test
fun accountIdFailureInvalidBlindedId() {
val result = SodiumUtilities.accountId("05$publicKey", "AB$blindedPublicKey", serverPublicKey)
assertFalse(result)
}
@Test
fun accountIdFailureBlindingFactor() {
val result = SodiumUtilities.accountId("05$publicKey", "15$blindedPublicKey", "Test")
assertFalse(result)
}
}

@ -1,82 +0,0 @@
package network.loki.messenger.util
import android.Manifest
import android.view.View
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.platform.app.InstrumentationRegistry
import com.adevinta.android.barista.interaction.PermissionGranter
import network.loki.messenger.R
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBar
import org.thoughtcrime.securesms.mms.GlideApp
fun setupLoggedInState(hasViewedSeed: Boolean = false) {
// landing activity
onView(ViewMatchers.withId(R.id.registerButton)).perform(ViewActions.click())
// session ID - register activity
onView(ViewMatchers.withId(R.id.registerButton)).perform(ViewActions.click())
// display name selection
onView(ViewMatchers.withId(R.id.displayNameEditText))
.perform(ViewActions.typeText("test-user123"))
onView(ViewMatchers.withId(R.id.registerButton)).perform(ViewActions.click())
// PN select
if (hasViewedSeed) {
// has viewed seed is set to false after register activity
TextSecurePreferences.setHasViewedSeed(
InstrumentationRegistry.getInstrumentation().targetContext,
true
)
}
onView(ViewMatchers.withId(R.id.backgroundPollingOptionView))
.perform(ViewActions.click())
onView(ViewMatchers.withId(R.id.registerButton)).perform(ViewActions.click())
// allow notification permission
PermissionGranter.allowPermissionsIfNeeded(Manifest.permission.POST_NOTIFICATIONS)
}
fun ConversationActivityV2.sendMessage(messageToSend: String, linkPreview: LinkPreview? = null) {
// assume in chat activity
onView(
Matchers.allOf(
ViewMatchers.isDescendantOfA(ViewMatchers.withId(R.id.inputBar)),
ViewMatchers.withId(R.id.inputBarEditText)
)
).perform(ViewActions.replaceText(messageToSend))
if (linkPreview != null) {
val glide = GlideApp.with(this)
this.findViewById<InputBar>(R.id.inputBar).updateLinkPreviewDraft(glide, linkPreview)
}
onView(
Matchers.allOf(
ViewMatchers.isDescendantOfA(ViewMatchers.withId(R.id.inputBar)),
InputBarButtonDrawableMatcher.inputButtonWithDrawable(R.drawable.ic_arrow_up)
)
).perform(ViewActions.click())
// TODO: text can flaky on cursor reload, figure out a better way to wait for the UI to settle with new data
onView(ViewMatchers.isRoot()).perform(waitFor(500))
}
/**
* Perform action of waiting for a specific time.
*/
fun waitFor(millis: Long): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View>? {
return ViewMatchers.isRoot()
}
override fun getDescription(): String = "Wait for $millis milliseconds."
override fun perform(uiController: UiController, view: View?) {
uiController.loopMainThreadForAtLeast(millis)
}
}
}

@ -1,24 +0,0 @@
package network.loki.messenger.util
import android.view.View
import androidx.annotation.DrawableRes
import org.hamcrest.Description
import org.hamcrest.TypeSafeMatcher
import org.thoughtcrime.securesms.conversation.v2.input_bar.InputBarButton
class InputBarButtonDrawableMatcher(@DrawableRes private val expectedId: Int): TypeSafeMatcher<View>() {
companion object {
@JvmStatic fun inputButtonWithDrawable(@DrawableRes expectedId: Int) = InputBarButtonDrawableMatcher(expectedId)
}
override fun describeTo(description: Description?) {
description?.appendText("with drawable on button with resource id: $expectedId")
}
override fun matchesSafely(item: View): Boolean {
if (item !is InputBarButton) return false
return item.getIconID() == expectedId
}
}

@ -1,31 +0,0 @@
package network.loki.messenger.util
import androidx.test.platform.app.InstrumentationRegistry
import org.mockito.kotlin.spy
import org.session.libsignal.utilities.hexEncodedPublicKey
import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
import org.thoughtcrime.securesms.database.Storage
import kotlin.random.Random
fun maybeGetUserInfo(): Pair<ByteArray, String>? {
val appContext = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as ApplicationContext
val prefs = appContext.prefs
val localUserPublicKey = prefs.getLocalNumber()
val secretKey = with(appContext) {
val edKey = KeyPairUtilities.getUserED25519KeyPair(this) ?: return null
edKey.secretKey.asBytes
}
return if (localUserPublicKey == null || secretKey == null) null
else secretKey to localUserPublicKey
}
fun ApplicationContext.applySpiedStorage(): Storage {
val storageSpy = spy(storage)!!
storage = storageSpy
return storageSpy
}
fun randomSeedBytes() = (0 until 16).map { Random.nextInt(UByte.MAX_VALUE.toInt()).toByte() }
fun randomKeyPair() = KeyPairUtilities.generate(randomSeedBytes().toByteArray())
fun randomSessionId() = randomKeyPair().x25519KeyPair.hexEncodedPublicKey

@ -443,8 +443,8 @@ class DisappearingMessagesViewModelTest {
mockStuff(mode)
whenever(recipient.address).thenReturn(GROUP_ADDRESS)
whenever(recipient.isGroupOrCommunityRecipient).thenReturn(true)
whenever(recipient.isLegacyGroupRecipient).thenReturn(true)
whenever(recipient.isGroupRecipient).thenReturn(true)
whenever(groupDb.getGroup(any<String>())).thenReturn(Optional.of(groupRecord))
whenever(groupRecord.admins).thenReturn(
buildList {

@ -44,7 +44,7 @@ class ConversationViewModelTest: BaseViewModelTest() {
threadDb = mock(),
textSecurePreferences = mock(),
lokiMessageDb = mock(),
application = mock(),
application = application,
reactionDb = mock(),
configFactory = mock(),
groupManagerV2 = mock()

@ -107,7 +107,8 @@ class MentionViewModelTest {
storage = mock {
on { getOpenGroup(threadID) } doReturn openGroup
},
dispatcher = StandardTestDispatcher()
dispatcher = StandardTestDispatcher(),
configFactory = mock()
)
}
@ -180,12 +181,12 @@ class MentionViewModelTest {
// Should have normalised message with selected candidate
assertThat(mentionViewModel.normalizeMessageBody())
.isEqualTo("Hi @pubkey1 ")
.isEqualTo("Hi @pubkey1")
// Should have correct normalised message even with the last space deleted
editable.delete(editable.length - 1, editable.length)
assertThat(mentionViewModel.normalizeMessageBody())
.isEqualTo("Hi @pubkey1 ")
.isEqualTo("Hi @pubkey1")
}
}
}
Loading…
Cancel
Save