mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-27 09:45:25 +00:00
updates
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
package com.bitchat.android.mesh
|
||||
|
||||
import com.bitchat.android.model.RoutedPacket
|
||||
import com.bitchat.android.protocol.BitchatPacket
|
||||
import com.bitchat.android.protocol.MessageType
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class BluetoothConnectionManagerTest {
|
||||
private lateinit var manager: BluetoothConnectionManager
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
manager = BluetoothConnectionManager(
|
||||
RuntimeEnvironment.getApplication(),
|
||||
"0011223344556677"
|
||||
)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
manager.stopServices()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `inactive manager rejects a broadcast instead of reporting it queued`() {
|
||||
val packet = BitchatPacket(
|
||||
version = 1u,
|
||||
type = MessageType.MESSAGE.value,
|
||||
senderID = byteArrayOf(0, 1, 2, 3, 4, 5, 6, 7),
|
||||
recipientID = null,
|
||||
timestamp = 1uL,
|
||||
payload = byteArrayOf(1),
|
||||
ttl = 7u
|
||||
)
|
||||
|
||||
assertFalse(manager.broadcastPacket(RoutedPacket(packet)))
|
||||
}
|
||||
}
|
||||
@@ -160,6 +160,51 @@ class PrivateMediaTransferPreparerTest {
|
||||
assertTrue(!fragmented)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `impossible content size rejects before policy encryption signing or fragmentation`() {
|
||||
var policyChecked = false
|
||||
var encrypted = false
|
||||
var finalized = false
|
||||
var fragmented = false
|
||||
val absolutePayloadUpperBound =
|
||||
com.bitchat.android.util.AppConstants.Fragmentation.MAX_FRAGMENTS_PER_ID *
|
||||
com.bitchat.android.util.AppConstants.Fragmentation.MAX_FRAGMENT_SIZE
|
||||
val preparer = PrivateMediaTransferPreparer(
|
||||
senderID = senderID,
|
||||
ttl = 7u,
|
||||
policyProvider = {
|
||||
policyChecked = true
|
||||
PrivateMediaPolicyDecision.Encrypted(authenticatedSession)
|
||||
},
|
||||
encrypt = { _, _, _ ->
|
||||
encrypted = true
|
||||
PrivateMediaEncryptionResult.Success(byteArrayOf(1))
|
||||
},
|
||||
finalizeRoutedAndSigned = {
|
||||
finalized = true
|
||||
it
|
||||
},
|
||||
fragment = { _, _ ->
|
||||
fragmented = true
|
||||
emptyList()
|
||||
}
|
||||
)
|
||||
|
||||
val outcome = preparer.prepare(
|
||||
"peer",
|
||||
recipientID,
|
||||
file(absolutePayloadUpperBound + 1),
|
||||
false
|
||||
)
|
||||
|
||||
assertTrue(outcome is PrivateMediaBuildOutcome.Rejected)
|
||||
assertTrue((outcome as PrivateMediaBuildOutcome.Rejected).reason.contains("256"))
|
||||
assertTrue(!policyChecked)
|
||||
assertTrue(!encrypted)
|
||||
assertTrue(!finalized)
|
||||
assertTrue(!fragmented)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no route accepts 256 final fragments and rejects 257`() {
|
||||
assertExactBoundary(route = null)
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.bitchat.android.mesh.PrivateMediaWireMode
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
@@ -23,7 +24,11 @@ import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.whenever
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import java.io.File
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class MediaSendingManagerMigrationTest {
|
||||
@@ -45,6 +50,7 @@ class MediaSendingManagerMigrationTest {
|
||||
MessageManager(state),
|
||||
mock(),
|
||||
CoroutineScope(SupervisorJob() + Dispatchers.Unconfined),
|
||||
mediaWorkDispatcher = Dispatchers.Unconfined,
|
||||
getMeshService = { mesh }
|
||||
)
|
||||
file = kotlin.io.path.createTempFile("private-media", ".jpg").toFile().apply {
|
||||
@@ -72,6 +78,40 @@ class MediaSendingManagerMigrationTest {
|
||||
verify(mesh, never()).sendFilePrivate(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `private preparation runs on the configured media worker`() {
|
||||
val executor = Executors.newSingleThreadExecutor { runnable ->
|
||||
Thread(runnable, "private-media-test-worker")
|
||||
}
|
||||
val dispatcher = executor.asCoroutineDispatcher()
|
||||
try {
|
||||
val preparationThread = AtomicReference<String>()
|
||||
val prepared = CountDownLatch(1)
|
||||
whenever(mesh.prepareFilePrivate(eq(peerID), any(), any(), eq(false)))
|
||||
.thenAnswer {
|
||||
preparationThread.set(Thread.currentThread().name)
|
||||
prepared.countDown()
|
||||
PrivateMediaPreparation.Rejected("test complete")
|
||||
}
|
||||
val asynchronousManager = MediaSendingManager(
|
||||
state,
|
||||
MessageManager(state),
|
||||
mock(),
|
||||
CoroutineScope(SupervisorJob() + Dispatchers.Unconfined),
|
||||
mediaWorkDispatcher = dispatcher,
|
||||
getMeshService = { mesh }
|
||||
)
|
||||
|
||||
asynchronousManager.sendImageNote(peerID, null, file.absolutePath)
|
||||
|
||||
assertTrue(prepared.await(5, TimeUnit.SECONDS))
|
||||
assertTrue(preparationThread.get().contains("private-media-test-worker"))
|
||||
} finally {
|
||||
dispatcher.close()
|
||||
executor.shutdownNow()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `pinned downgrade rejection is visible without a file echo or send`() {
|
||||
whenever(mesh.prepareFilePrivate(eq(peerID), any(), any(), eq(false)))
|
||||
@@ -225,6 +265,28 @@ class MediaSendingManagerMigrationTest {
|
||||
assertTrue(state.privateChats.value[peerID].isNullOrEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `failed prepared commit rolls back the local file echo`() {
|
||||
whenever(mesh.prepareFilePrivate(eq(peerID), any(), any(), eq(false)))
|
||||
.thenAnswer { invocation ->
|
||||
PrivateMediaPreparation.Ready(
|
||||
PreparedPrivateMediaTransfer(
|
||||
transferId = invocation.getArgument(2),
|
||||
wireMode = PrivateMediaWireMode.ENCRYPTED_NOISE_0X20
|
||||
) {
|
||||
false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
manager.sendImageNote(peerID, null, file.absolutePath)
|
||||
|
||||
val messages = state.privateChats.value[peerID].orEmpty()
|
||||
assertEquals(1, messages.size)
|
||||
assertTrue(messages.single().content.contains("could not be committed"))
|
||||
assertTrue(messages.none { it.type == com.bitchat.android.model.BitchatMessageType.Image })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cancelled consent cannot later send or echo`() {
|
||||
whenever(mesh.prepareFilePrivate(eq(peerID), any(), any(), eq(false)))
|
||||
|
||||
Reference in New Issue
Block a user