GnunetChatRemoteTest.kt (15672B)
1 package org.gnunet.gnunetmessenger.ipc 2 3 import androidx.test.ext.junit.runners.AndroidJUnit4 4 import androidx.test.core.app.ApplicationProvider 5 import kotlinx.coroutines.Dispatchers 6 import kotlinx.coroutines.test.runTest 7 import kotlinx.coroutines.withContext 8 import kotlinx.coroutines.withTimeout 9 import org.gnunet.gnunetmessenger.model.ChatAccount 10 import org.gnunet.gnunetmessenger.model.ChatContext 11 import org.gnunet.gnunetmessenger.model.ChatMessage 12 import org.gnunet.gnunetmessenger.model.ChatHandle 13 import org.gnunet.gnunetmessenger.model.ChatGroup 14 import org.gnunet.gnunetmessenger.model.GnunetReturnValue 15 import org.gnunet.gnunetmessenger.model.MessengerApp 16 import org.gnunet.gnunetmessenger.service.GnunetChat 17 import org.gnunet.gnunetmessenger.service.boundimpl.GnunetChatBoundService 18 import org.junit.After 19 import org.junit.Assert.assertEquals 20 import org.junit.Assert.assertTrue 21 import org.junit.Assert.assertNotNull 22 import org.junit.Before 23 import org.junit.Test 24 import org.junit.runner.RunWith 25 import kotlinx.coroutines.delay 26 27 @RunWith(AndroidJUnit4::class) 28 class GnunetChatRemoteTest { 29 30 private val appContext = ApplicationProvider.getApplicationContext<android.content.Context>() 31 private val gnunetChat: GnunetChatBoundService = GnunetChatBoundService(appContext) 32 private var messageLog = mutableListOf<Pair<ChatContext, ChatMessage>>() 33 34 @Before 35 fun setUp() { 36 messageLog.clear() 37 } 38 39 @After 40 fun tearDown() = runTest { 41 // sauber vom Service abmelden 42 gnunetChat.unbind() 43 // Wait for unbind to complete and clean up 44 delay(1000) 45 } 46 47 @Test 48 fun startChat_and_getProfileName_works() = runTest { 49 // 1. Chat starten – callback ignorieren wir erstmal 50 val handle: ChatHandle = gnunetChat.startChat( 51 messengerApp = MessengerApp() 52 ) { ctx: ChatContext, msg: ChatMessage -> 53 // Messages sammeln für Tests 54 messageLog.add(ctx to msg) 55 } 56 57 // 2. Wait for handle to be initialized with pointer from async operation 58 withContext(Dispatchers.Default.limitedParallelism(1)) { 59 withTimeout(5_000) { 60 while (handle.pointer == 0L) { 61 delay(100) 62 } 63 } 64 } 65 66 // 3. Verify handle is ready 67 assertTrue("Handle.pointer sollte != 0 sein", handle.pointer != 0L) 68 69 // 4. Remote-Call auf die Server-App: getProfileName() 70 val profileName = gnunetChat.getProfileName(handle) 71 72 // Aktuell sollte der Default-Name aus deiner Session-Struktur kommen 73 assertEquals("GNUnet", profileName) 74 } 75 76 @Test 77 fun createAccount_then_iterateAccounts_sees_it() = runTest { 78 val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 79 messageLog.add(ctx to msg) 80 } 81 82 // Wait for handle to be initialized 83 withContext(Dispatchers.Default.limitedParallelism(1)) { 84 withTimeout(5_000) { 85 while (handle.pointer == 0L) { 86 delay(100) 87 } 88 } 89 } 90 91 val name = "MyTestAccount" 92 93 val result = gnunetChat.createAccount(handle, name) 94 assertEquals(GnunetReturnValue.OK, result) 95 96 val accounts = mutableListOf<ChatAccount>() 97 98 // iterateAccounts ist nicht suspend -> wir warten mit Timeout, 99 // bis mindestens ein Account mit passendem Namen im Callback war. 100 gnunetChat.iterateAccounts(handle) { acc -> 101 accounts += acc 102 } 103 104 withContext(Dispatchers.Default.limitedParallelism(1)) { 105 withTimeout(3_000) { 106 while (accounts.none { it.name == name }) { 107 delay(50) 108 } 109 } 110 } 111 112 assertTrue(accounts.any { it.name == name }) 113 } 114 115 @Test 116 fun testMultipleAccountCreation() = runTest { 117 val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 118 messageLog.add(ctx to msg) 119 } 120 121 // Wait for handle to be initialized 122 withContext(Dispatchers.Default.limitedParallelism(1)) { 123 withTimeout(5_000) { 124 while (handle.pointer == 0L) { 125 delay(100) 126 } 127 } 128 } 129 130 val accountNames = listOf("Account1", "Account2", "Account3") 131 132 // Create multiple accounts 133 accountNames.forEach { name -> 134 val result = gnunetChat.createAccount(handle, name) 135 assertEquals("Failed to create account: $name", GnunetReturnValue.OK, result) 136 } 137 138 // Verify all accounts are returned 139 val accounts = mutableListOf<ChatAccount>() 140 gnunetChat.iterateAccounts(handle) { acc -> 141 accounts += acc 142 } 143 144 withContext(Dispatchers.Default.limitedParallelism(1)) { 145 withTimeout(5_000) { 146 while (accounts.size < accountNames.size) { 147 delay(50) 148 } 149 } 150 } 151 152 accountNames.forEach { name -> 153 assertTrue("Account $name not found in iteration", 154 accounts.any { it.name == name }) 155 } 156 } 157 158 @Test 159 fun testProfileNameUpdate() = runTest { 160 val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 161 messageLog.add(ctx to msg) 162 } 163 164 // Wait for handle to be initialized 165 withContext(Dispatchers.Default.limitedParallelism(1)) { 166 withTimeout(5_000) { 167 while (handle.pointer == 0L) { 168 delay(100) 169 } 170 } 171 } 172 173 val newName = "TestProfileName" 174 175 // Update profile name 176 gnunetChat.setProfileName(handle, newName) 177 178 // Verify update 179 val retrievedName = gnunetChat.getProfileName(handle) 180 assertEquals(newName, retrievedName) 181 } 182 183 @Test 184 fun testGroupCreation() = runTest { 185 val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 186 messageLog.add(ctx to msg) 187 } 188 189 // Wait for handle to be initialized 190 withContext(Dispatchers.Default.limitedParallelism(1)) { 191 withTimeout(30_000) { 192 while (handle.pointer == 0L) { 193 delay(100) 194 } 195 } 196 } 197 val groupName = "TestGroup" 198 199 // Create group 200 val group = gnunetChat.createGroup(handle, groupName) 201 assertNotNull("Group should not be null", group) 202 assertEquals(groupName, group.name) 203 204 // Verify group appears in iteration 205 val groups = mutableListOf<ChatGroup>() 206 gnunetChat.iterateGroups(handle) { grp -> 207 groups += grp 208 0 // Return GNUNet_OK (Int as required by interface) 209 } 210 211 withContext(Dispatchers.Default.limitedParallelism(1)) { 212 withTimeout(3_000) { 213 while (groups.none { it.name == groupName }) { 214 delay(50) 215 } 216 } 217 } 218 219 assertTrue("Group $groupName not found in iteration", 220 groups.any { it.name == groupName }) 221 } 222 223 @Test 224 fun testContactsIteration() = runTest { 225 val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 226 messageLog.add(ctx to msg) 227 } 228 229 // Wait for handle to be initialized 230 withContext(Dispatchers.Default.limitedParallelism(1)) { 231 withTimeout(5_000) { 232 while (handle.pointer == 0L) { 233 delay(100) 234 } 235 } 236 } 237 238 // Wait a bit for any initial messages to clear 239 delay(500) 240 241 val contacts = mutableListOf<String>() 242 243 gnunetChat.iterateContacts(handle) { contact -> 244 contacts.add(contact.name ?: "Unknown") 245 0 // Return GNUNet_OK (Int as required by interface) 246 } 247 248 // Wait longer for contacts to arrive - server needs to process the iteration 249 withContext(Dispatchers.Default.limitedParallelism(1)) { 250 withTimeout(5_000) { 251 var attempts = 0 252 while (contacts.isEmpty() && attempts < 100) { 253 delay(50) 254 attempts++ 255 } 256 } 257 } 258 259 assertTrue("Should receive at least one contact, got: $contacts", contacts.isNotEmpty()) 260 } 261 262 @Test 263 fun testAttributeOperations() = runTest { 264 val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 265 messageLog.add(ctx to msg) 266 } 267 268 // Wait for handle to be initialized 269 withContext(Dispatchers.Default.limitedParallelism(1)) { 270 withTimeout(5_000) { 271 while (handle.pointer == 0L) { 272 delay(100) 273 } 274 } 275 } 276 277 // Set attribute 278 val testKey = "test_key" 279 val testValue = "test_value" 280 gnunetChat.setAttribute(handle, testKey, testValue) 281 282 // Get attributes and verify 283 val attributes = mutableListOf<Pair<String, String>>() 284 gnunetChat.getAttributes(handle) { key, value -> 285 attributes.add(key to value) 286 } 287 288 withContext(Dispatchers.Default.limitedParallelism(1)) { 289 withTimeout(3_000) { 290 while (attributes.none { it.first == testKey }) { 291 delay(50) 292 } 293 } 294 } 295 296 assertTrue("Attribute $testKey not found", 297 attributes.any { it.first == testKey && it.second == testValue }) 298 } 299 300 @Test 301 fun testMessageReception() = runTest { 302 val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 303 messageLog.add(ctx to msg) 304 } 305 306 // Wait for handle to be initialized 307 withContext(Dispatchers.Default.limitedParallelism(1)) { 308 withTimeout(5_000) { 309 while (handle.pointer == 0L) { 310 delay(100) 311 } 312 } 313 } 314 315 // Create account which should trigger message 316 val accountName = "MessageTestAccount" 317 gnunetChat.createAccount(handle, accountName) 318 319 // Wait for message to be received 320 withContext(Dispatchers.Default.limitedParallelism(1)) { 321 withTimeout(3_000) { 322 while (messageLog.isEmpty()) { 323 delay(50) 324 } 325 } 326 } 327 328 assertTrue("Should receive at least one message", messageLog.isNotEmpty()) 329 val (ctx, msg) = messageLog.first() 330 assertNotNull("Message context should not be null", ctx) 331 assertNotNull("Message should not be null", msg) 332 assertNotNull("Message text should not be null", msg.text) 333 } 334 335 @Test 336 fun testLobbyOperations() = runTest { 337 val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 338 messageLog.add(ctx to msg) 339 } 340 341 // Wait for handle to be initialized 342 withContext(Dispatchers.Default.limitedParallelism(1)) { 343 withTimeout(5_000) { 344 while (handle.pointer == 0L) { 345 delay(100) 346 } 347 } 348 } 349 350 var lobbyUri = "" 351 352 gnunetChat.lobbyOpen(handle) { uri -> 353 lobbyUri = uri 354 } 355 356 withContext(Dispatchers.Default.limitedParallelism(1)) { 357 withTimeout(3_000) { 358 while (lobbyUri.isEmpty()) { 359 delay(50) 360 } 361 } 362 } 363 364 assertTrue("Lobby URI should not be empty", lobbyUri.isNotEmpty()) 365 } 366 367 @Test 368 fun testResetClearsAllData() = runTest { 369 val handle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 370 messageLog.add(ctx to msg) 371 } 372 373 // Wait for handle to be initialized 374 withContext(Dispatchers.Default.limitedParallelism(1)) { 375 withTimeout(5_000) { 376 while (handle.pointer == 0L) { 377 delay(100) 378 } 379 } 380 } 381 382 // Create some test data 383 val accountName = "TestAccountReset" 384 val groupName = "TestGroupReset" 385 val profileName = "TestProfileReset" 386 387 // Create account 388 val createResult = gnunetChat.createAccount(handle, accountName) 389 assertEquals("Account creation should succeed", GnunetReturnValue.OK, createResult) 390 391 // Create group 392 val group = gnunetChat.createGroup(handle, groupName) 393 assertNotNull("Group should be created", group) 394 assertEquals(groupName, group.name) 395 396 // Set profile name 397 gnunetChat.setProfileName(handle, profileName) 398 assertEquals("Profile name should be set", profileName, gnunetChat.getProfileName(handle)) 399 400 // Verify data exists before reset 401 val accountsBefore = mutableListOf<ChatAccount>() 402 gnunetChat.iterateAccounts(handle) { acc -> 403 accountsBefore += acc 404 } 405 406 withContext(Dispatchers.Default.limitedParallelism(1)) { 407 withTimeout(3_000) { 408 while (accountsBefore.none { it.name == accountName }) { 409 delay(50) 410 } 411 } 412 } 413 414 assertTrue("Account should exist before reset", 415 accountsBefore.any { it.name == accountName }) 416 417 val groupsBefore = mutableListOf<ChatGroup>() 418 gnunetChat.iterateGroups(handle) { grp -> 419 groupsBefore += grp 420 0 421 } 422 423 withContext(Dispatchers.Default.limitedParallelism(1)) { 424 withTimeout(3_000) { 425 while (groupsBefore.none { it.name == groupName }) { 426 delay(50) 427 } 428 } 429 } 430 431 assertTrue("Group should exist before reset", 432 groupsBefore.any { it.name == groupName }) 433 434 // Perform reset 435 gnunetChat.reset() 436 437 // Note: The local ChatHandle object's pointer is not automatically set to 0 438 // because it's a local variable. The reset clears server state, but we need 439 // to start a new session with a fresh handle. 440 441 // Start a new session after reset 442 val newHandle = gnunetChat.startChat(MessengerApp()) { ctx, msg -> 443 messageLog.add(ctx to msg) 444 } 445 446 // Wait for new handle to be initialized 447 withContext(Dispatchers.Default.limitedParallelism(1)) { 448 withTimeout(5_000) { 449 while (newHandle.pointer == 0L) { 450 delay(100) 451 } 452 } 453 } 454 455 // Verify old data is gone 456 val accountsAfter = mutableListOf<ChatAccount>() 457 gnunetChat.iterateAccounts(newHandle) { acc -> 458 accountsAfter += acc 459 } 460 461 // Give time for iteration to complete 462 delay(500) 463 464 // The created account should be gone (only default mock accounts remain) 465 assertTrue("Created account should not exist after reset", 466 !accountsAfter.any { it.name == accountName }) 467 468 // Profile should be back to default 469 val defaultProfile = gnunetChat.getProfileName(newHandle) 470 assertTrue("Profile should be reset to default", profileName != defaultProfile) 471 assertEquals("Profile should be default 'GNUnet'", "GNUnet", defaultProfile) 472 } 473 }