Best Kotest code snippet using io.kotest.core.spec.style.scopes.ShouldSpecContainerScope
OlmEventServiceTest.kt
Source:OlmEventServiceTest.kt
...3import io.kotest.assertions.throwables.shouldThrow4import io.kotest.assertions.timing.continually5import io.kotest.core.spec.style.ShouldSpec6import io.kotest.core.spec.style.scopes.ContainerScope7import io.kotest.core.spec.style.scopes.ShouldSpecContainerScope8import io.kotest.matchers.collections.shouldHaveSize9import io.kotest.matchers.collections.shouldNotContain10import io.kotest.matchers.nulls.beNull11import io.kotest.matchers.should12import io.kotest.matchers.shouldBe13import io.kotest.matchers.shouldNotBe14import io.kotest.matchers.types.shouldBeInstanceOf15import io.ktor.client.engine.mock.*16import kotlinx.coroutines.*17import kotlinx.coroutines.flow.collect18import kotlinx.coroutines.flow.first19import kotlinx.coroutines.flow.take20import kotlinx.coroutines.flow.takeWhile21import kotlinx.datetime.Clock22import kotlinx.datetime.DateTimeUnit23import kotlinx.datetime.Instant.Companion.fromEpochMilliseconds24import kotlinx.datetime.minus25import kotlinx.serialization.ExperimentalSerializationApi26import net.folivo.trixnity.client.mockMatrixClientServerApiClient27import net.folivo.trixnity.client.mocks.OlmSignServiceMock28import net.folivo.trixnity.client.store.*29import net.folivo.trixnity.clientserverapi.client.MatrixClientServerApiClient30import net.folivo.trixnity.clientserverapi.model.keys.ClaimKeys31import net.folivo.trixnity.clientserverapi.model.users.SendToDevice32import net.folivo.trixnity.core.model.EventId33import net.folivo.trixnity.core.model.RoomId34import net.folivo.trixnity.core.model.UserId35import net.folivo.trixnity.core.model.events.*36import net.folivo.trixnity.core.model.events.Event.MessageEvent37import net.folivo.trixnity.core.model.events.Event.StateEvent38import net.folivo.trixnity.core.model.events.m.DummyEventContent39import net.folivo.trixnity.core.model.events.m.RoomKeyEventContent40import net.folivo.trixnity.core.model.events.m.room.EncryptedEventContent.MegolmEncryptedEventContent41import net.folivo.trixnity.core.model.events.m.room.EncryptedEventContent.OlmEncryptedEventContent42import net.folivo.trixnity.core.model.events.m.room.EncryptedEventContent.OlmEncryptedEventContent.CiphertextInfo43import net.folivo.trixnity.core.model.events.m.room.EncryptedEventContent.OlmEncryptedEventContent.CiphertextInfo.OlmMessageType.INITIAL_PRE_KEY44import net.folivo.trixnity.core.model.events.m.room.EncryptedEventContent.OlmEncryptedEventContent.CiphertextInfo.OlmMessageType.ORDINARY45import net.folivo.trixnity.core.model.events.m.room.EncryptionEventContent46import net.folivo.trixnity.core.model.events.m.room.MemberEventContent47import net.folivo.trixnity.core.model.events.m.room.Membership.JOIN48import net.folivo.trixnity.core.model.events.m.room.RoomMessageEventContent.TextMessageEventContent49import net.folivo.trixnity.core.model.keys.*50import net.folivo.trixnity.core.model.keys.Key.Curve25519Key51import net.folivo.trixnity.core.model.keys.Key.Ed25519Key52import net.folivo.trixnity.core.serialization.createEventContentSerializerMappings53import net.folivo.trixnity.core.serialization.createMatrixEventJson54import net.folivo.trixnity.olm.*55import net.folivo.trixnity.olm.OlmMessage.OlmMessageType56import net.folivo.trixnity.testutils.PortableMockEngineConfig57import net.folivo.trixnity.testutils.matrixJsonEndpoint58import kotlin.test.assertNotNull59import kotlin.time.Duration.Companion.milliseconds60class OlmEventServiceTest : ShouldSpec(body)61private val body: ShouldSpec.() -> Unit = {62 timeout = 30_00063 val json = createMatrixEventJson()64 val mappings = createEventContentSerializerMappings()65 val alice = UserId("alice", "server")66 val bob = UserId("bob", "server")67 val aliceDeviceId = "ALICEDEVICE"68 val bobDeviceId = "BOBDEVICE"69 lateinit var aliceAccount: OlmAccount70 lateinit var bobAccount: OlmAccount71 val relatesTo = RelatesTo.Reference(EventId("$1fancyEvent"))72 lateinit var store: Store73 lateinit var storeScope: CoroutineScope74 val signService = OlmSignServiceMock()75 lateinit var api: MatrixClientServerApiClient76 lateinit var apiConfig: PortableMockEngineConfig77 lateinit var cut: OlmEventService78 lateinit var aliceCurveKey: Curve25519Key79 lateinit var aliceEdKey: Ed25519Key80 lateinit var bobCurveKey: Curve25519Key81 lateinit var bobEdKey: Ed25519Key82 @OptIn(ExperimentalSerializationApi::class)83 val decryptedOlmEventSerializer = json.serializersModule.getContextual(DecryptedOlmEvent::class)84 requireNotNull(decryptedOlmEventSerializer)85 @OptIn(ExperimentalSerializationApi::class)86 val decryptedMegolmEventSerializer = json.serializersModule.getContextual(DecryptedMegolmEvent::class)87 requireNotNull(decryptedMegolmEventSerializer)88 beforeEach {89 aliceAccount = OlmAccount.create()90 bobAccount = OlmAccount.create()91 aliceCurveKey = Curve25519Key(aliceDeviceId, aliceAccount.identityKeys.curve25519)92 aliceEdKey = Ed25519Key(aliceDeviceId, aliceAccount.identityKeys.ed25519)93 bobCurveKey = Curve25519Key(bobDeviceId, bobAccount.identityKeys.curve25519)94 bobEdKey = Ed25519Key(bobDeviceId, bobAccount.identityKeys.ed25519)95 storeScope = CoroutineScope(Dispatchers.Default)96 store = InMemoryStore(storeScope).apply { init() }97 store.keys.updateDeviceKeys(bob) {98 mapOf(99 bobDeviceId to StoredDeviceKeys(100 Signed(101 DeviceKeys(102 userId = bob,103 deviceId = bobDeviceId,104 algorithms = setOf(EncryptionAlgorithm.Olm, EncryptionAlgorithm.Megolm),105 keys = Keys(keysOf(bobCurveKey, bobEdKey))106 ), mapOf(bob to keysOf(Ed25519Key("BOBD", "bobEdKey")))107 ), KeySignatureTrustLevel.Valid(true)108 )109 )110 }111 signService.returnVerify = VerifyResult.Valid112 val (newApi, newApiConfig) = mockMatrixClientServerApiClient(json)113 api = newApi114 apiConfig = newApiConfig115 cut = OlmEventService(116 "",117 alice,118 aliceDeviceId,119 aliceEdKey,120 aliceCurveKey,121 json,122 aliceAccount,123 store,124 api,125 signService126 )127 }128 afterEach {129 storeScope.cancel()130 aliceAccount.free()131 bobAccount.free()132 }133 fun OlmAccount.getOneTimeKey(): String {134 generateOneTimeKeys(1)135 return oneTimeKeys.curve25519.values.first()136 .also { markKeysAsPublished() }137 }138 fun MockEngineConfig.claimKeysEndpoint() {139 val bobsFakeSignedCurveKey =140 Key.SignedCurve25519Key(bobDeviceId, bobAccount.getOneTimeKey(), mapOf())141 matrixJsonEndpoint(json, mappings, ClaimKeys()) {142 it.oneTimeKeys shouldBe (mapOf(bob to mapOf(bobDeviceId to KeyAlgorithm.SignedCurve25519)))143 ClaimKeys.Response(144 emptyMap(),145 mapOf(bob to mapOf(bobDeviceId to keysOf(bobsFakeSignedCurveKey)))146 )147 }148 }149 context(OlmEventService::handleOlmEncryptedToDeviceEvents.name) {150 context("exceptions") {151 val event = Event.ToDeviceEvent(152 OlmEncryptedEventContent(153 mapOf(), Curve25519Key(null, "")154 ),155 UserId("sender", "server")156 )157 should("catch exceptions") {158 cut.handleOlmEncryptedToDeviceEvents(event)159 }160 }161 should("emit decrypted events") {162 freeAfter(OlmUtility.create()) { olmUtility ->163 val bobStore = InMemoryStore(storeScope).apply { init() }164 val bobOlmService =165 OlmService("", bob, bobDeviceId, bobStore, api, json, bobAccount, olmUtility)166 store.olm.storeAccount(aliceAccount, "")167 val aliceSignService = OlmSignService(alice, aliceDeviceId, json, store, aliceAccount, olmUtility)168 val cutWithAccount = OlmEventService(169 "",170 alice,171 aliceDeviceId,172 Ed25519Key(null, aliceAccount.identityKeys.ed25519),173 Curve25519Key(null, aliceAccount.identityKeys.curve25519),174 json,175 aliceAccount,176 store,177 api,178 aliceSignService179 )180 store.keys.updateDeviceKeys(bob) {181 mapOf(182 bobDeviceId to StoredDeviceKeys(183 Signed(184 DeviceKeys(185 userId = bob,186 deviceId = bobDeviceId,187 algorithms = setOf(EncryptionAlgorithm.Olm, EncryptionAlgorithm.Megolm),188 keys = Keys(189 keysOf(190 bobOlmService.getSelfSignedDeviceKeys().signed.get<Curve25519Key>()!!,191 bobOlmService.getSelfSignedDeviceKeys().signed.get<Ed25519Key>()!!192 )193 )194 ), mapOf()195 ), KeySignatureTrustLevel.Valid(true)196 )197 )198 }199 bobStore.keys.updateDeviceKeys(alice) {200 mapOf(201 aliceDeviceId to StoredDeviceKeys(202 Signed(203 DeviceKeys(204 userId = alice,205 deviceId = aliceDeviceId,206 algorithms = setOf(EncryptionAlgorithm.Olm, EncryptionAlgorithm.Megolm),207 keys = Keys(208 keysOf(209 Curve25519Key(null, aliceAccount.identityKeys.curve25519),210 Ed25519Key(null, aliceAccount.identityKeys.ed25519)211 )212 )213 ), mapOf()214 ), KeySignatureTrustLevel.Valid(true)215 )216 )217 }218 apiConfig.endpoints {219 matrixJsonEndpoint(json, mappings, ClaimKeys()) {220 it.oneTimeKeys shouldBe mapOf(alice to mapOf(aliceDeviceId to KeyAlgorithm.SignedCurve25519))221 ClaimKeys.Response(222 emptyMap(),223 mapOf(224 alice to mapOf(225 aliceDeviceId to keysOf(226 aliceSignService.signCurve25519Key(227 Curve25519Key(228 aliceDeviceId,229 aliceAccount.getOneTimeKey()230 )231 )232 )233 )234 )235 )236 }237 }238 val outboundSession = OlmOutboundGroupSession.create()239 val eventContent = RoomKeyEventContent(240 RoomId("room", "server"),241 outboundSession.sessionId,242 outboundSession.sessionKey,243 EncryptionAlgorithm.Megolm244 )245 val encryptedEvent = Event.ToDeviceEvent(246 bobOlmService.event.encryptOlm(247 eventContent,248 alice,249 aliceDeviceId250 ), bob251 )252 val emittedEvent = async { cutWithAccount.decryptedOlmEvents.first() }253 delay(50)254 cutWithAccount.handleOlmEncryptedToDeviceEvents(encryptedEvent)255 assertSoftly(256 emittedEvent.await()257 ) {258 assertNotNull(this)259 encrypted shouldBe encryptedEvent260 decrypted shouldBe DecryptedOlmEvent(261 eventContent,262 bob,263 keysOf(bobOlmService.getSelfSignedDeviceKeys().signed.get<Ed25519Key>()!!.copy(keyId = null)),264 alice,265 keysOf(Ed25519Key(null, aliceAccount.identityKeys.ed25519))266 )267 }268 }269 }270 }271 context(OlmEventService::encryptOlm.name) {272 val eventContent = RoomKeyEventContent(273 RoomId("room", "server"),274 "sessionId",275 "sessionKey",276 EncryptionAlgorithm.Megolm,277 )278 lateinit var decryptedOlmEvent: DecryptedOlmEvent<RoomKeyEventContent>279 beforeEach {280 decryptedOlmEvent = DecryptedOlmEvent(281 content = eventContent,282 sender = alice,283 senderKeys = keysOf(aliceEdKey.copy(keyId = null)),284 recipient = bob,285 recipientKeys = keysOf(bobEdKey.copy(keyId = null))286 )287 }288 context("without stored olm encrypt session") {289 beforeEach {290 apiConfig.endpoints { claimKeysEndpoint() }291 }292 should("encrypt") {293 val encryptedMessage = cut.encryptOlm(eventContent, bob, bobDeviceId)294 val encryptedCipherText = encryptedMessage.ciphertext[bobCurveKey.value]295 assertNotNull(encryptedCipherText)296 encryptedMessage.senderKey shouldBe aliceCurveKey297 encryptedCipherText.type shouldBe INITIAL_PRE_KEY298 freeAfter(299 OlmSession.createInboundFrom(300 account = bobAccount,301 identityKey = aliceCurveKey.value,302 oneTimeKeyMessage = encryptedCipherText.body303 )304 ) { bobSession ->305 json.decodeFromString(306 decryptedOlmEventSerializer,307 bobSession.decrypt(OlmMessage(encryptedCipherText.body, OlmMessageType.INITIAL_PRE_KEY))308 ) shouldBe decryptedOlmEvent309 }310 store.olm.getOlmSessions(bobCurveKey)!! shouldHaveSize 1311 }312 should("throw exception when one time key is invalid") {313 signService.returnVerify = VerifyResult.Invalid("dino")314 shouldThrow<KeyException.KeyVerificationFailedException> {315 cut.encryptOlm(eventContent, bob, bobDeviceId).ciphertext.entries.first().value316 }.message shouldBe "dino"317 store.olm.getOlmSessions(bobCurveKey) should beNull()318 }319 }320 context("with stored olm encrypt session") {321 should("encrypt event with stored session") {322 freeAfter(323 OlmSession.createOutbound(324 bobAccount,325 aliceCurveKey.value,326 aliceAccount.getOneTimeKey()327 )328 ) { bobSession ->329 val storedOlmSession = freeAfter(330 OlmSession.createInbound(aliceAccount, bobSession.encrypt("first message").cipherText)331 ) { aliceSession ->332 StoredOlmSession(333 bobCurveKey,334 aliceSession.sessionId,335 Clock.System.now(),336 Clock.System.now(),337 aliceSession.pickle("")338 )339 }340 store.olm.updateOlmSessions(bobCurveKey) { setOf(storedOlmSession) }341 val encryptedMessage = cut.encryptOlm(eventContent, bob, bobDeviceId)342 val encryptedCipherText = encryptedMessage.ciphertext[bobCurveKey.value]343 assertNotNull(encryptedCipherText)344 encryptedMessage.senderKey shouldBe aliceCurveKey345 encryptedCipherText.type shouldBe INITIAL_PRE_KEY346 json.decodeFromString(347 decryptedOlmEventSerializer,348 bobSession.decrypt(349 OlmMessage(350 encryptedCipherText.body,351 OlmMessageType.INITIAL_PRE_KEY352 )353 )354 ) shouldBe decryptedOlmEvent355 store.olm.getOlmSessions(bobCurveKey)!! shouldHaveSize 1356 store.olm.getOlmSessions(bobCurveKey)?.first() shouldNotBe storedOlmSession357 }358 }359 }360 }361 context(OlmEventService::decryptOlm.name) {362 val eventContent = RoomKeyEventContent(363 RoomId("room", "server"),364 "sessionId",365 "sessionKey",366 EncryptionAlgorithm.Megolm367 )368 lateinit var decryptedOlmEvent: DecryptedOlmEvent<RoomKeyEventContent>369 beforeEach {370 decryptedOlmEvent = DecryptedOlmEvent(371 content = eventContent,372 sender = bob,373 senderKeys = keysOf(bobEdKey),374 recipient = alice,375 recipientKeys = keysOf(aliceEdKey)376 )377 }378 context("without stored decrypt olm session") {379 should("decrypt pre key message from new session") {380 val encryptedMessage = freeAfter(381 OlmSession.createOutbound(382 bobAccount,383 aliceCurveKey.value,384 aliceAccount.getOneTimeKey()385 )386 ) { bobSession ->387 bobSession.encrypt(json.encodeToString(decryptedOlmEventSerializer, decryptedOlmEvent))388 }389 cut.decryptOlm(390 OlmEncryptedEventContent(391 ciphertext = mapOf(392 aliceCurveKey.value to CiphertextInfo(encryptedMessage.cipherText, INITIAL_PRE_KEY)393 ),394 senderKey = bobCurveKey395 ), bob396 ) shouldBe decryptedOlmEvent397 store.olm.getOlmSessions(bobCurveKey)!! shouldHaveSize 1398 // we check, that the one time key cannot be used twice399 shouldThrow<OlmLibraryException> {400 OlmSession.createInboundFrom(aliceAccount, bobCurveKey.value, encryptedMessage.cipherText)401 }402 }403 should("not decrypt pre key message, when the 5 last created sessions are not older then 1 hour") {404 val encryptedMessage = freeAfter(405 OlmSession.createOutbound(406 bobAccount,407 aliceCurveKey.value,408 aliceAccount.getOneTimeKey()409 )410 ) { bobSession ->411 bobSession.encrypt(json.encodeToString(decryptedOlmEventSerializer, decryptedOlmEvent))412 }413 repeat(5) { pseudoSessionId ->414 freeAfter(OlmAccount.create()) { dummyAccount ->415 freeAfter(416 OlmSession.createOutbound(417 aliceAccount,418 dummyAccount.identityKeys.curve25519,419 dummyAccount.getOneTimeKey()420 )421 ) { aliceSession ->422 val storedOlmSession = StoredOlmSession(423 bobCurveKey,424 pseudoSessionId.toString(),425 Clock.System.now(),426 Clock.System.now(),427 aliceSession.pickle("")428 )429 store.olm.updateOlmSessions(bobCurveKey) {430 it?.plus(storedOlmSession) ?: setOf(431 storedOlmSession432 )433 }434 }435 }436 }437 shouldThrow<SessionException.PreventToManySessions> {438 cut.decryptOlm(439 OlmEncryptedEventContent(440 ciphertext = mapOf(441 aliceCurveKey.value to CiphertextInfo(encryptedMessage.cipherText, INITIAL_PRE_KEY)442 ),443 senderKey = bobCurveKey444 ), bob445 )446 }447 }448 should("throw on ordinary message") {449 var sendToDeviceEvents: Map<UserId, Map<String, ToDeviceEventContent>>? = null450 apiConfig.endpoints {451 claimKeysEndpoint()452 matrixJsonEndpoint(453 json, mappings,454 SendToDevice("m.room.encrypted", "txn"),455 skipUrlCheck = true456 ) {457 sendToDeviceEvents = it.messages458 }459 }460 val encryptedMessage = freeAfter(461 OlmSession.createOutbound(462 bobAccount,463 aliceCurveKey.value,464 aliceAccount.getOneTimeKey()465 )466 ) { bobSession ->467 bobSession.encrypt(json.encodeToString(decryptedOlmEventSerializer, decryptedOlmEvent))468 }469 shouldThrow<SessionException.CouldNotDecrypt> {470 cut.decryptOlm(471 OlmEncryptedEventContent(472 ciphertext = mapOf(473 aliceCurveKey.value to CiphertextInfo(encryptedMessage.cipherText, ORDINARY)474 ),475 senderKey = bobCurveKey476 ), bob477 )478 }479 val encryptedEventContent =480 sendToDeviceEvents?.get(bob)?.get(bobDeviceId)?.shouldBeInstanceOf<OlmEncryptedEventContent>()481 val ciphertext = encryptedEventContent?.ciphertext?.get(bobCurveKey.value)?.body482 assertNotNull(ciphertext)483 freeAfter(OlmSession.createInbound(bobAccount, ciphertext)) { session ->484 json.decodeFromString(485 decryptedOlmEventSerializer,486 session.decrypt(OlmMessage(ciphertext, OlmMessageType.INITIAL_PRE_KEY))487 ).content shouldBe DummyEventContent488 }489 }490 }491 context("with stored decrypt olm session") {492 should("decrypt pre key message from stored session") {493 freeAfter(494 OlmSession.createOutbound(495 aliceAccount,496 bobCurveKey.value,497 bobAccount.getOneTimeKey()498 )499 ) { aliceSession ->500 val firstMessage = aliceSession.encrypt("first message")501 val encryptedMessage = freeAfter(502 OlmSession.createInbound(bobAccount, firstMessage.cipherText)503 ) { bobSession ->504 // we do not decrypt the message, so the next is an initial pre key message505 bobSession.encrypt(json.encodeToString(decryptedOlmEventSerializer, decryptedOlmEvent))506 }507 val storedOlmSession = StoredOlmSession(508 bobCurveKey,509 aliceSession.sessionId,510 Clock.System.now(),511 Clock.System.now(),512 aliceSession.pickle("")513 )514 store.olm.updateOlmSessions(bobCurveKey) { setOf(storedOlmSession) }515 cut.decryptOlm(516 OlmEncryptedEventContent(517 ciphertext = mapOf(518 aliceCurveKey.value to CiphertextInfo(encryptedMessage.cipherText, INITIAL_PRE_KEY)519 ),520 senderKey = bobCurveKey521 ), bob522 ) shouldBe decryptedOlmEvent523 store.olm.getOlmSessions(bobCurveKey)?.first() shouldNotBe storedOlmSession524 }525 }526 should("decrypt ordinary message") {527 freeAfter(528 OlmSession.createOutbound(529 aliceAccount,530 bobCurveKey.value,531 bobAccount.getOneTimeKey()532 )533 ) { aliceSession ->534 val firstMessage = aliceSession.encrypt("first message")535 val encryptedMessage = freeAfter(536 OlmSession.createInbound(bobAccount, firstMessage.cipherText)537 ) { bobSession ->538 bobSession.decrypt(firstMessage)539 bobSession.encrypt(json.encodeToString(decryptedOlmEventSerializer, decryptedOlmEvent))540 }541 val storedOlmSession = StoredOlmSession(542 bobCurveKey,543 aliceSession.sessionId,544 Clock.System.now(),545 Clock.System.now(),546 aliceSession.pickle("")547 )548 store.olm.updateOlmSessions(bobCurveKey) { setOf(storedOlmSession) }549 cut.decryptOlm(550 OlmEncryptedEventContent(551 ciphertext = mapOf(552 aliceCurveKey.value to CiphertextInfo(encryptedMessage.cipherText, ORDINARY)553 ),554 senderKey = bobCurveKey555 ), bob556 ) shouldBe decryptedOlmEvent557 store.olm.getOlmSessions(bobCurveKey)?.first() shouldNotBe storedOlmSession558 }559 }560 should("try multiple sessions descended by last used") {561 freeAfter(562 OlmSession.createOutbound(aliceAccount, bobCurveKey.value, bobAccount.getOneTimeKey()),563 OlmSession.createOutbound(aliceAccount, bobCurveKey.value, bobAccount.getOneTimeKey()),564 OlmSession.createOutbound(aliceAccount, bobCurveKey.value, bobAccount.getOneTimeKey()),565 ) { aliceSession1, aliceSession2, aliceSession3 ->566 val firstMessage = aliceSession1.encrypt("first message")567 val encryptedMessage = freeAfter(568 OlmSession.createInbound(bobAccount, firstMessage.cipherText)569 ) { bobSession ->570 bobSession.decrypt(firstMessage)571 bobSession.encrypt(json.encodeToString(decryptedOlmEventSerializer, decryptedOlmEvent))572 }573 val storedOlmSession1 = StoredOlmSession(574 bobCurveKey,575 aliceSession1.sessionId,576 Clock.System.now(),577 Clock.System.now(),578 aliceSession1.pickle("")579 )580 val storedOlmSession2 = StoredOlmSession(581 bobCurveKey,582 aliceSession2.sessionId,583 fromEpochMilliseconds(24),584 Clock.System.now(),585 aliceSession2.pickle("")586 )587 val storedOlmSession3 = StoredOlmSession(588 bobCurveKey,589 aliceSession3.sessionId,590 Clock.System.now(),591 Clock.System.now(),592 aliceSession3.pickle("")593 )594 store.olm.updateOlmSessions(bobCurveKey) {595 setOf(596 storedOlmSession2,597 storedOlmSession1,598 storedOlmSession3599 )600 }601 cut.decryptOlm(602 OlmEncryptedEventContent(603 ciphertext = mapOf(604 aliceCurveKey.value to CiphertextInfo(encryptedMessage.cipherText, ORDINARY)605 ),606 senderKey = bobCurveKey607 ), bob608 ) shouldBe decryptedOlmEvent609 store.olm.getOlmSessions(bobCurveKey)!! shouldNotContain storedOlmSession1610 }611 }612 }613 context("handle olm event with manipulated") {614 suspend fun ContainerScope.handleManipulation(manipulatedOlmEvent: DecryptedOlmEvent<RoomKeyEventContent>) {615 val job1 = launch {616 store.keys.outdatedKeys.first { it.isNotEmpty() }617 store.keys.outdatedKeys.value = setOf()618 }619 freeAfter(620 OlmSession.createOutbound(621 aliceAccount,622 bobCurveKey.value,623 bobAccount.getOneTimeKey()624 )625 ) { aliceSession ->626 val firstMessage = aliceSession.encrypt("first message")627 val encryptedMessage = freeAfter(628 OlmSession.createInbound(bobAccount, firstMessage.cipherText)629 ) { bobSession ->630 bobSession.decrypt(firstMessage)631 bobSession.encrypt(json.encodeToString(decryptedOlmEventSerializer, manipulatedOlmEvent))632 }633 val storedOlmSession = StoredOlmSession(634 bobCurveKey,635 aliceSession.sessionId,636 Clock.System.now(),637 Clock.System.now(),638 aliceSession.pickle("")639 )640 store.olm.updateOlmSessions(bobCurveKey) { setOf(storedOlmSession) }641 shouldThrow<DecryptionException> {642 cut.decryptOlm(643 OlmEncryptedEventContent(644 ciphertext = mapOf(645 aliceCurveKey.value to CiphertextInfo(encryptedMessage.cipherText, ORDINARY)646 ),647 senderKey = bobCurveKey648 ), bob649 )650 }651 }652 job1.cancel()653 }654 should("sender") {655 handleManipulation(decryptedOlmEvent.copy(sender = UserId("cedric", "server")))656 }657 should("senderKeys") {658 handleManipulation(decryptedOlmEvent.copy(senderKeys = keysOf(Ed25519Key("CEDRICKEY", "cedrics key"))))659 }660 should("recipient") {661 handleManipulation(decryptedOlmEvent.copy(recipient = UserId("cedric", "server")))662 }663 should("recipientKeys") {664 handleManipulation(665 decryptedOlmEvent.copy(recipientKeys = keysOf(Ed25519Key("CEDRICKEY", "cedrics key")))666 )667 }668 }669 }670 context(OlmEventService::encryptMegolm.name) {671 val eventContent = TextMessageEventContent("Hi", relatesTo = relatesTo)672 val room = RoomId("room", "server")673 val decryptedMegolmEvent = DecryptedMegolmEvent(eventContent, room)674 beforeEach {675 store.room.update(room) { Room(room, membership = JOIN, membersLoaded = true) }676 listOf(677 StateEvent(678 MemberEventContent(membership = JOIN),679 EventId("\$event1"),680 alice,681 room,682 1234,683 stateKey = alice.full684 ),685 StateEvent(686 MemberEventContent(membership = JOIN),687 EventId("\$event2"),688 bob,689 room,690 1235,691 stateKey = bob.full692 )693 ).forEach { store.roomState.update(it) }694 }695 suspend fun ShouldSpecContainerScope.testEncryption(696 settings: EncryptionEventContent,697 expectedMessageCount: Int,698 ) {699 should("encrypt message") {700 var sendToDeviceEvents: Map<UserId, Map<String, ToDeviceEventContent>>? = null701 apiConfig.endpoints {702 claimKeysEndpoint()703 matrixJsonEndpoint(704 json, mappings,705 SendToDevice("m.room.encrypted", "txn"),706 skipUrlCheck = true707 ) {708 sendToDeviceEvents = it.messages709 }...
KeySecretServiceTest.kt
Source:KeySecretServiceTest.kt
1package net.folivo.trixnity.client.key2import io.kotest.assertions.assertSoftly3import io.kotest.assertions.timing.continually4import io.kotest.core.spec.style.ShouldSpec5import io.kotest.core.spec.style.scopes.ShouldSpecContainerScope6import io.kotest.matchers.collections.shouldHaveSize7import io.kotest.matchers.shouldBe8import io.kotest.matchers.shouldNot9import io.kotest.matchers.shouldNotBe10import io.kotest.matchers.string.beEmpty11import io.kotest.matchers.types.shouldBeInstanceOf12import io.ktor.http.HttpStatusCode.Companion.InternalServerError13import io.ktor.util.*14import kotlinx.coroutines.*15import kotlinx.coroutines.flow.MutableStateFlow16import kotlinx.coroutines.flow.first17import kotlinx.datetime.Clock18import kotlinx.serialization.json.JsonPrimitive19import kotlinx.serialization.json.encodeToJsonElement20import net.folivo.trixnity.client.crypto.IOlmService21import net.folivo.trixnity.client.crypto.KeySignatureTrustLevel22import net.folivo.trixnity.client.crypto.KeySignatureTrustLevel.*23import net.folivo.trixnity.client.mockMatrixClientServerApiClient24import net.folivo.trixnity.client.mocks.KeyBackupServiceMock25import net.folivo.trixnity.client.mocks.OlmEventServiceMock26import net.folivo.trixnity.client.store.*27import net.folivo.trixnity.client.store.AllowedSecretType.*28import net.folivo.trixnity.clientserverapi.client.SyncState29import net.folivo.trixnity.clientserverapi.model.keys.GetRoomKeyBackupVersion30import net.folivo.trixnity.clientserverapi.model.keys.GetRoomKeysBackupVersionResponse31import net.folivo.trixnity.clientserverapi.model.users.SendToDevice32import net.folivo.trixnity.core.ErrorResponse33import net.folivo.trixnity.core.MatrixServerException34import net.folivo.trixnity.core.model.UserId35import net.folivo.trixnity.core.model.events.DecryptedOlmEvent36import net.folivo.trixnity.core.model.events.Event37import net.folivo.trixnity.core.model.events.Event.GlobalAccountDataEvent38import net.folivo.trixnity.core.model.events.ToDeviceEventContent39import net.folivo.trixnity.core.model.events.m.KeyRequestAction40import net.folivo.trixnity.core.model.events.m.MegolmBackupV1EventContent41import net.folivo.trixnity.core.model.events.m.crosssigning.MasterKeyEventContent42import net.folivo.trixnity.core.model.events.m.crosssigning.SelfSigningKeyEventContent43import net.folivo.trixnity.core.model.events.m.crosssigning.UserSigningKeyEventContent44import net.folivo.trixnity.core.model.events.m.room.EncryptedEventContent45import net.folivo.trixnity.core.model.events.m.secret.SecretKeyRequestEventContent46import net.folivo.trixnity.core.model.events.m.secret.SecretKeySendEventContent47import net.folivo.trixnity.core.model.events.m.secretstorage.SecretKeyEventContent48import net.folivo.trixnity.core.model.keys.*49import net.folivo.trixnity.core.serialization.createEventContentSerializerMappings50import net.folivo.trixnity.core.serialization.createMatrixEventJson51import net.folivo.trixnity.olm.OlmPkDecryption52import net.folivo.trixnity.olm.OlmPkSigning53import net.folivo.trixnity.olm.freeAfter54import net.folivo.trixnity.testutils.PortableMockEngineConfig55import net.folivo.trixnity.testutils.matrixJsonEndpoint56import kotlin.random.Random57import kotlin.test.assertNotNull58import kotlin.time.Duration.Companion.days59import kotlin.time.Duration.Companion.milliseconds60class KeySecretServiceTest : ShouldSpec(body)61private val body: ShouldSpec.() -> Unit = {62 timeout = 30_00063 val json = createMatrixEventJson()64 val mappings = createEventContentSerializerMappings()65 val alice = UserId("alice", "server")66 val bob = UserId("bob", "server")67 val aliceDevice = "ALICEDEVICE"68 val bobDevice = "BOBDEVICE"69 lateinit var scope: CoroutineScope70 lateinit var store: Store71 lateinit var olmEvent: OlmEventServiceMock72 val keyBackup = KeyBackupServiceMock()73 lateinit var apiConfig: PortableMockEngineConfig74 val currentSyncState = MutableStateFlow(SyncState.STOPPED)75 lateinit var cut: KeySecretService76 beforeTest {77 olmEvent = OlmEventServiceMock()78 scope = CoroutineScope(Dispatchers.Default)79 store = InMemoryStore(scope).apply { init() }80 val (api, newApiConfig) = mockMatrixClientServerApiClient(json)81 apiConfig = newApiConfig82 cut = KeySecretService(alice, aliceDevice, store, olmEvent, keyBackup, api, currentSyncState)83 }84 afterTest {85 scope.cancel()86 }87 val encryptedEvent = Event.ToDeviceEvent(88 EncryptedEventContent.OlmEncryptedEventContent(89 ciphertext = mapOf(),90 senderKey = Key.Curve25519Key(null, "")91 ), bob92 )93 context(KeySecretService::handleEncryptedIncomingKeyRequests.name) {94 var sendToDeviceEvents: Map<UserId, Map<String, ToDeviceEventContent>>? = null95 beforeTest {96 sendToDeviceEvents = null97 store.account.userId.value = alice98 store.keys.updateDeviceKeys(alice) {99 mapOf(100 aliceDevice to StoredDeviceKeys(101 Signed(DeviceKeys(alice, aliceDevice, setOf(), keysOf()), null),102 Valid(true)103 )104 )105 }106 apiConfig.endpoints {107 matrixJsonEndpoint(108 json, mappings,109 SendToDevice("m.room.encrypted", "txn"),110 skipUrlCheck = true111 ) {112 sendToDeviceEvents = it.messages113 }114 }115 store.keys.secrets.value =116 mapOf(117 M_CROSS_SIGNING_USER_SIGNING to StoredSecret(118 GlobalAccountDataEvent(UserSigningKeyEventContent(mapOf())),119 "secretUserSigningKey"120 )121 )122 olmEvent.returnEncryptOlm = {123 EncryptedEventContent.OlmEncryptedEventContent(124 ciphertext = mapOf(),125 senderKey = Key.Curve25519Key("", "")126 )127 }128 }129 should("ignore request from other user") {130 cut.handleEncryptedIncomingKeyRequests(131 IOlmService.DecryptedOlmEventContainer(132 encryptedEvent, DecryptedOlmEvent(133 SecretKeyRequestEventContent(134 M_CROSS_SIGNING_USER_SIGNING.id,135 KeyRequestAction.REQUEST,136 bobDevice,137 "requestId"138 ),139 bob, keysOf(), alice, keysOf()140 )141 )142 )143 cut.processIncomingKeyRequests()144 sendToDeviceEvents shouldBe null145 }146 should("add request on request") {147 cut.handleEncryptedIncomingKeyRequests(148 IOlmService.DecryptedOlmEventContainer(149 encryptedEvent, DecryptedOlmEvent(150 SecretKeyRequestEventContent(151 M_CROSS_SIGNING_USER_SIGNING.id,152 KeyRequestAction.REQUEST,153 aliceDevice,154 "requestId"155 ),156 alice, keysOf(), alice, keysOf()157 )158 )159 )160 cut.processIncomingKeyRequests()161 sendToDeviceEvents?.get(alice)?.get(aliceDevice) shouldNotBe null162 }163 should("remove request on request cancellation") {164 cut.handleEncryptedIncomingKeyRequests(165 IOlmService.DecryptedOlmEventContainer(166 encryptedEvent, DecryptedOlmEvent(167 SecretKeyRequestEventContent(168 M_CROSS_SIGNING_USER_SIGNING.id,169 KeyRequestAction.REQUEST,170 aliceDevice,171 "requestId"172 ),173 alice, keysOf(), alice, keysOf()174 )175 )176 )177 cut.handleEncryptedIncomingKeyRequests(178 IOlmService.DecryptedOlmEventContainer(179 encryptedEvent, DecryptedOlmEvent(180 SecretKeyRequestEventContent(181 M_CROSS_SIGNING_USER_SIGNING.id,182 KeyRequestAction.REQUEST_CANCELLATION,183 aliceDevice,184 "requestId"185 ),186 alice, keysOf(), alice, keysOf()187 )188 )189 )190 cut.processIncomingKeyRequests()191 sendToDeviceEvents shouldBe null192 }193 }194 context(KeySecretService::processIncomingKeyRequests.name) {195 var sendToDeviceEvents: Map<UserId, Map<String, ToDeviceEventContent>>? = null196 beforeTest {197 sendToDeviceEvents = null198 store.account.userId.value = alice199 apiConfig.endpoints {200 matrixJsonEndpoint(201 json, mappings,202 SendToDevice("m.room.encrypted", "txn"),203 skipUrlCheck = true204 ) {205 sendToDeviceEvents = it.messages206 }207 }208 store.keys.secrets.value =209 mapOf(210 M_CROSS_SIGNING_USER_SIGNING to StoredSecret(211 GlobalAccountDataEvent(UserSigningKeyEventContent(mapOf())),212 "secretUserSigningKey"213 )214 )215 olmEvent.returnEncryptOlm = {216 EncryptedEventContent.OlmEncryptedEventContent(217 ciphertext = mapOf(),218 senderKey = Key.Curve25519Key("", "")219 )220 }221 }222 suspend fun ShouldSpecContainerScope.answerRequest(returnedTrustLevel: KeySignatureTrustLevel) {223 should("answer request with trust level $returnedTrustLevel") {224 store.keys.updateDeviceKeys(alice) {225 mapOf(226 aliceDevice to StoredDeviceKeys(227 SignedDeviceKeys(DeviceKeys(alice, aliceDevice, setOf(), keysOf()), mapOf()),228 returnedTrustLevel229 )230 )231 }232 cut.handleEncryptedIncomingKeyRequests(233 IOlmService.DecryptedOlmEventContainer(234 encryptedEvent, DecryptedOlmEvent(235 SecretKeyRequestEventContent(236 M_CROSS_SIGNING_USER_SIGNING.id,237 KeyRequestAction.REQUEST,238 aliceDevice,239 "requestId"240 ),241 alice, keysOf(), alice, keysOf()242 )243 )244 )245 cut.processIncomingKeyRequests()246 cut.processIncomingKeyRequests()247 sendToDeviceEvents?.get(alice)?.get(aliceDevice) shouldNotBe null248 }249 }250 answerRequest(Valid(true))251 answerRequest(CrossSigned(true))252 suspend fun ShouldSpecContainerScope.notAnswerRequest(returnedTrustLevel: KeySignatureTrustLevel) {253 should("not answer request with trust level $returnedTrustLevel") {254 store.keys.updateDeviceKeys(alice) {255 mapOf(256 aliceDevice to StoredDeviceKeys(257 SignedDeviceKeys(DeviceKeys(alice, aliceDevice, setOf(), keysOf()), mapOf()),258 returnedTrustLevel259 )260 )261 }262 cut.handleEncryptedIncomingKeyRequests(263 IOlmService.DecryptedOlmEventContainer(264 encryptedEvent, DecryptedOlmEvent(265 SecretKeyRequestEventContent(266 M_CROSS_SIGNING_USER_SIGNING.id,...
RoomServiceDisplayNameTest.kt
Source:RoomServiceDisplayNameTest.kt
1package net.folivo.trixnity.client.room2import io.kotest.core.spec.style.ShouldSpec3import io.kotest.core.spec.style.scopes.ShouldSpecContainerScope4import io.kotest.matchers.shouldBe5import kotlinx.coroutines.CoroutineScope6import kotlinx.coroutines.Dispatchers7import kotlinx.coroutines.cancel8import kotlinx.coroutines.flow.MutableStateFlow9import net.folivo.trixnity.client.MatrixClientConfiguration10import net.folivo.trixnity.client.mockMatrixClientServerApiClient11import net.folivo.trixnity.client.mocks.KeyBackupServiceMock12import net.folivo.trixnity.client.mocks.MediaServiceMock13import net.folivo.trixnity.client.mocks.OlmEventServiceMock14import net.folivo.trixnity.client.mocks.UserServiceMock15import net.folivo.trixnity.client.simpleRoom16import net.folivo.trixnity.client.store.InMemoryStore17import net.folivo.trixnity.client.store.RoomDisplayName18import net.folivo.trixnity.client.store.Store19import net.folivo.trixnity.clientserverapi.client.SyncState20import net.folivo.trixnity.clientserverapi.model.sync.Sync.Response.Rooms.JoinedRoom.RoomSummary21import net.folivo.trixnity.core.model.EventId22import net.folivo.trixnity.core.model.RoomAliasId23import net.folivo.trixnity.core.model.RoomId24import net.folivo.trixnity.core.model.UserId25import net.folivo.trixnity.core.model.events.Event26import net.folivo.trixnity.core.model.events.m.room.CanonicalAliasEventContent27import net.folivo.trixnity.core.model.events.m.room.MemberEventContent28import net.folivo.trixnity.core.model.events.m.room.Membership29import net.folivo.trixnity.core.model.events.m.room.Membership.*30import net.folivo.trixnity.core.model.events.m.room.NameEventContent31import net.folivo.trixnity.core.serialization.createMatrixEventJson32class RoomServiceDisplayNameTest : ShouldSpec({33 val roomId = RoomId("room", "server")34 lateinit var store: Store35 lateinit var storeScope: CoroutineScope36 val currentSyncState = MutableStateFlow(SyncState.STOPPED)37 lateinit var cut: RoomService38 val user1 = UserId("user1", "server")39 val user2 = UserId("user2", "server")40 val user3 = UserId("user3", "server")41 val user4 = UserId("user4", "server")42 val user5 = UserId("user5", "server")43 val json = createMatrixEventJson()44 beforeTest {45 storeScope = CoroutineScope(Dispatchers.Default)46 store = InMemoryStore(storeScope).apply { init() }47 cut = RoomService(48 UserId("alice", "server"),49 store,50 mockMatrixClientServerApiClient(json).first,51 OlmEventServiceMock(),52 KeyBackupServiceMock(),53 UserServiceMock(),54 MediaServiceMock(),55 currentSyncState,56 MatrixClientConfiguration()57 )58 }59 afterTest {60 storeScope.cancel()61 }62 fun memberEvent(63 i: Long,64 userId: UserId,65 displayName: String,66 membership: Membership67 ): Event.StateEvent<MemberEventContent> {68 return Event.StateEvent(69 MemberEventContent(70 displayName = displayName,71 membership = membership72 ),73 EventId("\$event$i"),74 userId,75 roomId,76 i,77 stateKey = userId.full78 )79 }80 fun nameEvent(81 i: Long,82 userId: UserId,83 name: String84 ): Event.StateEvent<NameEventContent> {85 return Event.StateEvent(86 NameEventContent(name),87 EventId("\$event$i"),88 userId,89 roomId,90 i,91 stateKey = ""92 )93 }94 fun canonicalAliasEvent(95 i: Long,96 userId: UserId, roomAliasId: RoomAliasId97 ): Event.StateEvent<CanonicalAliasEventContent> {98 return Event.StateEvent(99 CanonicalAliasEventContent(roomAliasId),100 EventId("\$event$i"),101 userId,102 roomId,103 1,104 stateKey = ""105 )106 }107 context(RoomService::setRoomDisplayName.name) {108 beforeTest {109 store.room.update(roomId) { simpleRoom.copy(roomId = roomId) }110 }111 suspend fun ShouldSpecContainerScope.testWithoutNameFromNameEvent() {112 context("with an existent Canonical Alias Event") {113 should("set room name to the alias field value") {114 listOf(115 canonicalAliasEvent(2, user2, RoomAliasId("somewhere", "localhost")),116 memberEvent(3, user1, "User1-Display", JOIN),117 memberEvent(4, user2, "User2-Display", INVITE),118 memberEvent(5, user3, "User3-Display", BAN),119 memberEvent(6, user4, "User4-Display", LEAVE)120 ).forEach { store.roomState.update(it) }121 val roomSummary = RoomSummary(122 heroes = listOf(user1, user2),123 joinedMemberCount = 1,124 invitedMemberCount = 1,125 )...
ActiveSasVerificationMethodTest.kt
Source:ActiveSasVerificationMethodTest.kt
1package net.folivo.trixnity.client.verification2import io.kotest.assertions.assertSoftly3import io.kotest.core.spec.style.ShouldSpec4import io.kotest.core.spec.style.scopes.ShouldSpecContainerScope5import io.kotest.matchers.collections.shouldBeEmpty6import io.kotest.matchers.collections.shouldContain7import io.kotest.matchers.collections.shouldHaveSize8import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual9import io.kotest.matchers.ints.shouldBeLessThanOrEqual10import io.kotest.matchers.shouldBe11import io.kotest.matchers.string.shouldNotBeBlank12import io.kotest.matchers.types.shouldBeInstanceOf13import kotlinx.coroutines.CoroutineScope14import kotlinx.coroutines.Dispatchers15import kotlinx.coroutines.cancel16import kotlinx.coroutines.flow.MutableSharedFlow17import kotlinx.coroutines.flow.filterIsInstance18import kotlinx.coroutines.flow.first19import net.folivo.trixnity.client.crypto.KeySignatureTrustLevel.Valid20import net.folivo.trixnity.client.mocks.KeyTrustServiceMock21import net.folivo.trixnity.client.store.InMemoryStore22import net.folivo.trixnity.client.store.Store23import net.folivo.trixnity.client.store.StoredCrossSigningKeys24import net.folivo.trixnity.client.store.StoredDeviceKeys25import net.folivo.trixnity.client.verification.ActiveSasVerificationState.*26import net.folivo.trixnity.core.model.UserId27import net.folivo.trixnity.core.model.events.m.key.verification.*28import net.folivo.trixnity.core.model.events.m.key.verification.VerificationCancelEventContent.Code.*29import net.folivo.trixnity.core.model.events.m.key.verification.VerificationStartEventContent.SasStartEventContent30import net.folivo.trixnity.core.model.keys.*31import net.folivo.trixnity.core.model.keys.EncryptionAlgorithm.Megolm32import net.folivo.trixnity.core.model.keys.Key.Ed25519Key33import net.folivo.trixnity.core.serialization.createMatrixEventJson34import net.folivo.trixnity.olm.OlmSAS35import net.folivo.trixnity.olm.freeAfter36import kotlin.test.assertNotNull37class ActiveSasVerificationMethodTest : ShouldSpec({38 timeout = 30_00039 val alice = UserId("alice", "server")40 val aliceDevice = "AAAAAA"41 val bob = UserId("bob", "server")42 val bobDevice = "BBBBBB"43 lateinit var store: Store44 lateinit var storeScope: CoroutineScope45 lateinit var keyTrustService: KeyTrustServiceMock46 val json = createMatrixEventJson()47 lateinit var sendVerificationStepFlow: MutableSharedFlow<VerificationStep>48 lateinit var cut: ActiveSasVerificationMethod49 beforeTest {50 sendVerificationStepFlow = MutableSharedFlow(replay = 10)51 storeScope = CoroutineScope(Dispatchers.Default)52 store = InMemoryStore(storeScope).apply { init() }53 keyTrustService = KeyTrustServiceMock()54 val method = ActiveSasVerificationMethod.create(55 startEventContent = SasStartEventContent(aliceDevice, relatesTo = null, transactionId = "t"),56 weStartedVerification = true,57 ownUserId = alice,58 ownDeviceId = aliceDevice,59 theirUserId = bob,60 theirDeviceId = bobDevice,61 relatesTo = null,62 transactionId = "t",63 sendVerificationStep = { sendVerificationStepFlow.emit(it) },64 store = store,65 keyTrustService = keyTrustService,66 json = json,67 )68 assertNotNull(method)69 cut = method70 }71 afterTest {72 storeScope.cancel()73 }74 context("create") {75 should("not cancel when key agreement protocol is not supported") {76 val method = ActiveSasVerificationMethod.create(77 startEventContent = SasStartEventContent(78 aliceDevice,79 keyAgreementProtocols = setOf(),80 relatesTo = null,81 transactionId = "t"82 ),83 weStartedVerification = true,84 ownUserId = alice,85 ownDeviceId = aliceDevice,86 theirUserId = bob,87 theirDeviceId = bobDevice,88 relatesTo = null,89 transactionId = "t",90 sendVerificationStep = { sendVerificationStepFlow.emit(it) },91 store = store,92 keyTrustService = keyTrustService,93 json = json,94 )95 method shouldBe null96 val result = sendVerificationStepFlow.first()97 result.shouldBeInstanceOf<VerificationCancelEventContent>()98 result.code shouldBe UnknownMethod99 }100 }101 suspend fun ShouldSpecContainerScope.checkNotAllowedStateChange(vararg steps: VerificationStep) {102 steps.forEach {103 should("cancel unexpected message ${it::class.simpleName}") {104 cut.handleVerificationStep(it, false)105 val result =106 sendVerificationStepFlow.replayCache.filterIsInstance<VerificationCancelEventContent>().first()107 result.code shouldBe UnexpectedMessage108 }109 }110 }111 context("handleVerificationStep") {112 context("current state is ${OwnSasStart::class.simpleName} or ${TheirSasStart::class.simpleName}") {113 checkNotAllowedStateChange(114 SasKeyEventContent("key", null, "t"),115 SasMacEventContent("keys", keysOf(), null, "t")...
ActiveVerificationTest.kt
Source:ActiveVerificationTest.kt
1package net.folivo.trixnity.client.verification2import io.kotest.core.spec.style.ShouldSpec3import io.kotest.core.spec.style.scopes.ShouldSpecContainerScope4import io.kotest.matchers.collections.shouldBeEmpty5import io.kotest.matchers.shouldBe6import io.kotest.matchers.types.shouldBeInstanceOf7import kotlinx.coroutines.CoroutineScope8import kotlinx.coroutines.flow.MutableSharedFlow9import kotlinx.coroutines.flow.first10import net.folivo.trixnity.client.mocks.KeyTrustServiceMock11import net.folivo.trixnity.client.store.InMemoryStore12import net.folivo.trixnity.client.verification.ActiveSasVerificationState.OwnSasStart13import net.folivo.trixnity.client.verification.ActiveSasVerificationState.TheirSasStart14import net.folivo.trixnity.client.verification.ActiveVerificationState.*15import net.folivo.trixnity.core.model.UserId16import net.folivo.trixnity.core.model.events.m.key.verification.*17import net.folivo.trixnity.core.model.events.m.key.verification.VerificationCancelEventContent.Code18import net.folivo.trixnity.core.model.events.m.key.verification.VerificationMethod.Sas19import net.folivo.trixnity.core.model.events.m.key.verification.VerificationMethod.Unknown20import net.folivo.trixnity.core.model.events.m.key.verification.VerificationStartEventContent.SasStartEventContent21import net.folivo.trixnity.core.serialization.createMatrixEventJson22import kotlin.coroutines.EmptyCoroutineContext23class ActiveVerificationTest : ShouldSpec({24 timeout = 30_00025 val alice = UserId("alice", "server")26 val aliceDevice = "AAAAAA"27 val bob = UserId("bob", "server")28 val bobDevice = "BBBBBB"29 var lifecycleCalled = 030 lateinit var sendVerificationStepFlow: MutableSharedFlow<VerificationStep>31 class TestActiveVerification(request: VerificationRequestEventContent) : ActiveVerification(32 request = request,33 requestIsFromOurOwn = request.fromDevice == aliceDevice,34 ownUserId = alice,35 ownDeviceId = aliceDevice,36 theirUserId = bob,37 theirInitialDeviceId = null,38 timestamp = 1234,39 setOf(Sas),40 null,41 "t",42 InMemoryStore(CoroutineScope(EmptyCoroutineContext)),43 KeyTrustServiceMock(),44 createMatrixEventJson(),45 ) {46 override suspend fun lifecycle(scope: CoroutineScope) {47 lifecycleCalled++48 }49 override suspend fun sendVerificationStep(step: VerificationStep) {50 sendVerificationStepFlow.emit(step)51 }52 suspend fun handleStep(step: VerificationStep, sender: UserId, isOurOwn: Boolean) {53 super.handleIncomingVerificationStep(step, sender, isOurOwn)54 }55 fun setState(state: ActiveVerificationState) {56 mutableState.value = state57 }58 }59 lateinit var cut: TestActiveVerification60 beforeTest {61 lifecycleCalled = 062 sendVerificationStepFlow = MutableSharedFlow(replay = 10)63 cut = TestActiveVerification(VerificationRequestEventContent(bobDevice, setOf(Sas), 1234, "t"))64 }65 context(ActiveVerification::startLifecycle.name) {66 should("start lifecycle once") {67 cut.startLifecycle(this)68 cut.startLifecycle(this)69 lifecycleCalled shouldBe 170 }71 }72 context(ActiveVerification::cancel.name) {73 should("send user cancel event content") {74 val expectedCancelEvent =75 VerificationCancelEventContent(Code.User, "user cancelled verification", null, "t")76 cut.cancel()77 sendVerificationStepFlow.first() shouldBe expectedCancelEvent78 cut.state.value shouldBe Cancel(expectedCancelEvent, true)79 }80 }81 context("handleVerificationStep") {82 context("step is from foreign user") {83 should("cancel") {84 cut.handleStep(85 VerificationReadyEventContent("FFFFFF", setOf(), null, "t"),86 UserId("f", "server"),87 false88 )89 sendVerificationStepFlow.first().shouldBeInstanceOf<VerificationCancelEventContent>()90 }91 }92 context("step has no matching transaction") {93 should("cancel") {94 cut.handleStep(VerificationReadyEventContent(aliceDevice, setOf(), null, null), alice, true)95 sendVerificationStepFlow.first().shouldBeInstanceOf<VerificationCancelEventContent>()96 }97 }98 context("current state is ${AcceptedByOtherDevice::class.simpleName}") {99 beforeTest {100 cut.setState(AcceptedByOtherDevice)101 }102 should("set state to ${Done::class.simpleName} when done") {103 cut.handleStep(VerificationDoneEventContent(null, "t"), alice, false)104 cut.state.value shouldBe Done105 }106 should("set state to ${Cancel::class.simpleName} when cancel") {107 val cancelEvent = VerificationCancelEventContent(Code.User, "user", null, "t")108 cut.handleStep(cancelEvent, alice, false)109 cut.state.value shouldBe Cancel(cancelEvent, false)110 }111 }112 suspend fun ShouldSpecContainerScope.checkNotAllowedStateChange(vararg steps: VerificationStep) {113 steps.forEach {114 should("cancel unexpected message ${it::class.simpleName}") {115 val stateBefore = cut.state.value116 cut.handleStep(it, bob, false)117 val state = cut.state.value118 state.shouldBeInstanceOf<Cancel>()119 state.content.code shouldBe Code.UnexpectedMessage120 if (stateBefore !is Cancel) {121 val result = sendVerificationStepFlow.first()122 result.shouldBeInstanceOf<VerificationCancelEventContent>()123 result.code shouldBe Code.UnexpectedMessage124 }125 }126 }...
ShouldSpecRootScope.kt
Source:ShouldSpecRootScope.kt
...22interface ShouldSpecRootScope : RootScope {23 /**24 * Adds a top level context scope to the spec.25 */26 fun context(name: String, test: suspend ShouldSpecContainerScope.() -> Unit) {27 addContainer(TestName("context ", name, false), false, null) {28 ShouldSpecContainerScope(this).test()29 }30 }31 /**32 * Adds a top level context scope to the spec.33 */34 fun xcontext(name: String, test: suspend ShouldSpecContainerScope.() -> Unit) {35 addContainer(TestName("context ", name, false), true, null) { ShouldSpecContainerScope(this).test() }36 }37 /**38 * Adds a top level context scope accepting config to the spec.39 */40 @ExperimentalKotest41 fun context(name: String): RootContainerWithConfigBuilder<ShouldSpecContainerScope> =42 RootContainerWithConfigBuilder(TestName("context ", name, false), false, this) { ShouldSpecContainerScope(it) }43 /**44 * Adds a disabled top level context scope accepting config to the spec.45 */46 @ExperimentalKotest47 fun xcontext(name: String): RootContainerWithConfigBuilder<ShouldSpecContainerScope> =48 RootContainerWithConfigBuilder(TestName("context ", name, false), true, this) { ShouldSpecContainerScope(it) }49 /**50 * Adds a top level test, with the given name and test function, with test config supplied51 * by invoking .config on the return of this function.52 */53 fun should(name: String): RootTestWithConfigBuilder =54 RootTestWithConfigBuilder(this, TestName("should ", name, true), false)55 fun xshould(name: String): RootTestWithConfigBuilder =56 RootTestWithConfigBuilder(this, TestName("should ", name, true), true)57 /**58 * Adds a top level test, with the given name and test function, with default test config.59 */60 fun should(name: String, test: suspend TestScope.() -> Unit) {61 addTest(TestName("should ", name, false), false, null, test)62 }...
ShouldSpecContainerScope.kt
Source:ShouldSpecContainerScope.kt
...3import io.kotest.core.descriptors.append4import io.kotest.core.names.TestName5import io.kotest.core.spec.KotestTestScope6import io.kotest.core.test.TestScope7@Deprecated("This interface has been renamed to ShouldSpecContainerScope. Deprecated since 4.5")8typealias ShouldSpecContextScope = ShouldSpecContainerScope9@Deprecated("This interface has been renamed to ShouldSpecContainerScope. Deprecated since 5.0")10typealias ShouldSpecContainerContext = ShouldSpecContainerScope11/**12 * A scope that allows tests to be registered using the syntax:13 *14 * context("some context")15 * should("some test")16 * should("some test").config(...)17 *18 */19@KotestTestScope20class ShouldSpecContainerScope(21 val testScope: TestScope,22) : AbstractContainerScope(testScope) {23 /**24 * Adds a nested context scope to this scope.25 */26 suspend fun context(name: String, test: suspend ShouldSpecContainerScope.() -> Unit) {27 registerContainer(TestName(name), false, null) { ShouldSpecContainerScope(this).test() }28 }29 /**30 * Adds a disabled nested context scope to this scope.31 */32 suspend fun xcontext(name: String, test: suspend ShouldSpecContainerScope.() -> Unit) {33 registerContainer(TestName(name), true, null) { ShouldSpecContainerScope(this).test() }34 }35 @ExperimentalKotest36 fun context(name: String): ContainerWithConfigBuilder<ShouldSpecContainerScope> {37 return ContainerWithConfigBuilder(TestName(name), this, false) { ShouldSpecContainerScope(it) }38 }39 @ExperimentalKotest40 fun xcontext(name: String): ContainerWithConfigBuilder<ShouldSpecContainerScope> {41 return ContainerWithConfigBuilder(TestName(name), this, true) { ShouldSpecContainerScope(it) }42 }43 suspend fun should(name: String): TestWithConfigBuilder {44 TestDslState.startTest(testScope.testCase.descriptor.append(name))45 return TestWithConfigBuilder(TestName("should ", name, false), this, false)46 }47 suspend fun xshould(name: String): TestWithConfigBuilder {48 TestDslState.startTest(testScope.testCase.descriptor.append(name))49 return TestWithConfigBuilder(TestName("should ", name, false), this, true)50 }51 suspend fun should(name: String, test: suspend TestScope.() -> Unit) {52 registerTest(TestName("should ", name, false), false, null, test)53 }54 suspend fun xshould(name: String, test: suspend TestScope.() -> Unit) {55 registerTest(TestName("should ", name, true), true, null, test)...
ProfileDataShouldSpec.kt
Source:ProfileDataShouldSpec.kt
1package de.jlnstrk.transit.util.testing2import io.kotest.core.spec.style.ShouldSpec3import io.kotest.core.spec.style.scopes.ShouldSpecContainerScope4import io.kotest.core.test.TestContext5import kotlinx.coroutines.delay6public abstract class ProfileDataShouldSpec<Endpoint : Any, Profile : ServiceTestProfile<Endpoint, *>>(7 allProfiles: List<Profile>,8 test: suspend ShouldSpecContainerScope.(Endpoint, Profile) -> Unit9) :10 ShouldSpec(spec@{11 for (profile in allProfiles) {12 val client = profile.makeTestEndpoint()13 context("For profile ${profile.name}") {14 test(client, profile)15 }16 }17 }) {18}19public suspend inline fun <reified DataSet : Any> ShouldSpecContainerScope.givenAll(20 from: ServiceTestProfile<*, *>,21 require: Boolean = true,22 crossinline test: suspend TestContext.(DataSet) -> Unit23) {24 val dataSets = from.testData[DataSet::class].orEmpty() as List<DataSet>25 if (dataSets.isEmpty()) {26 if (require) {27 throw IllegalStateException("No ${DataSet::class.simpleName} specified for ${from.name}")28 } else {29 println("${this::class.simpleName} did not run for ${from.name}")30 }31 }32 for (dataSet in dataSets) {33 context("Given dataSet $dataSet") {34 test(dataSet)35 }36 delay(500)37 }38}39public suspend inline fun <Profile : ServiceTestProfile<*, DataSet>, reified DataSet : Any> ShouldSpecContainerScope.givenOne(40 from: Profile,41 require: Boolean = true,42 crossinline test: suspend TestContext.(DataSet) -> Unit43) {44 val dataSet = from.testData[DataSet::class].orEmpty().firstOrNull()45 if (dataSet == null) {46 if (require) {47 throw IllegalStateException("No ${DataSet::class.simpleName} specified for ${from.name}")48 } else {49 println("${this::class.simpleName} did not run for ${from.name}")50 }51 } else {52 context("Given dataSet $dataSet") {53 test(dataSet)...
ShouldSpecContainerScope
Using AI Code Generation
1import io.kotest.core.spec.style.ShouldSpec2import io.kotest.core.spec.style.scopes.ShouldSpecContainerScope3import io.kotest.matchers.shouldBe4class ShouldSpecContainerScopeTest : ShouldSpec() {5 init {6 should("test should container scope") {7 }8 should("test should container scope with config") {9 }.config(invocations = 2)10 }11}12import io.kotest.core.spec.style.ShouldSpec13import io.kotest.core.spec.style.scopes.ShouldSpecRootScope14import io.kotest.matchers.shouldBe15class ShouldSpecRootScopeTest : ShouldSpec() {16 init {17 should("test should root scope") {18 }19 should("test should root scope with config") {20 }.config(invocations = 2)21 }22}23import io.kotest.core.spec.style.ShouldSpec24import io.kotest.core.spec.style.scopes.ShouldSpecTestScope25import io.kotest.matchers.shouldBe26class ShouldSpecTestScopeTest : ShouldSpec() {27 init {28 should("test should test scope") {29 }30 should("test should test scope with config") {31 }.config(invocations = 2)32 }33}
ShouldSpecContainerScope
Using AI Code Generation
1fun should(name: String, test: suspend TestContext.() -> Unit)2fun should(name: String): TestWithConfig3fun should(name: String, test: suspend TestContext.() -> Unit, config: TestCaseConfig)4fun should(name: String, config: TestCaseConfig, test: suspend TestContext.() -> Unit)5fun should(name: String, test: suspend TestContext.() -> Unit)6fun should(name: String): TestWithConfig7fun should(name: String, test: suspend TestContext.() -> Unit, config: TestCaseConfig)8fun should(name: String, config: TestCaseConfig, test: suspend TestContext.() -> Unit)
ShouldSpecContainerScope
Using AI Code Generation
1 import io.kotest.core.spec.style.ShouldSpecContainerScope2 import io.kotest.matchers.shouldBe3 class ShouldSpecExample2 : ShouldSpecContainerScope ( {4 should ( "test 1" ) {5 }6 should ( "test 2" ) {7 }8 })9 import io.kotest.core.spec.style.ShouldSpecContainerScope10 import io.kotest.matchers.shouldBe11 class ShouldSpecExample3 : ShouldSpecContainerScope ( {12 should ( "test 1" ) {13 }14 should ( "test 2" ) {15 }16 context ( "context 1" ) {17 should ( "test 3" ) {18 }19 }20 })
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!