Merge pull request #150 from ouchadam/release-candidate

[Auto] Release Candidate
This commit is contained in:
Adam Brown 2022-09-19 22:19:02 +01:00 committed by GitHub
commit b3d4f79d8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 181 additions and 91 deletions

View File

@ -25,7 +25,7 @@ jobs:
- name: Create pip requirements
run: |
echo "matrix-synapse==v1.60.0" > requirements.txt
echo "matrix-synapse" > requirements.txt
- name: Set up Python 3.8
uses: actions/setup-python@v2

View File

@ -68,7 +68,7 @@ import java.time.Clock
internal class AppModule(context: Application, logger: MatrixLogger) {
private val buildMeta = BuildMeta(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE)
private val buildMeta = BuildMeta(BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, isDebug = BuildConfig.DEBUG)
private val deviceMeta = DeviceMeta(Build.VERSION.SDK_INT)
private val trackingModule by unsafeLazy {
TrackingModule(
@ -94,7 +94,7 @@ internal class AppModule(context: Application, logger: MatrixLogger) {
private val workModule = WorkModule(context)
private val imageLoaderModule = ImageLoaderModule(context)
private val matrixModules = MatrixModules(storeModule, trackingModule, workModule, logger, coroutineDispatchers, context.contentResolver)
private val matrixModules = MatrixModules(storeModule, trackingModule, workModule, logger, coroutineDispatchers, context.contentResolver, buildMeta)
val domainModules = DomainModules(matrixModules, trackingModule.errorTracker, workModule, storeModule, context, coroutineDispatchers)
val coreAndroidModule = CoreAndroidModule(
@ -232,6 +232,7 @@ internal class MatrixModules(
private val logger: MatrixLogger,
private val coroutineDispatchers: CoroutineDispatchers,
private val contentResolver: ContentResolver,
private val buildMeta: BuildMeta,
) {
val matrix by unsafeLazy {
@ -240,7 +241,7 @@ internal class MatrixModules(
MatrixClient(
KtorMatrixHttpClientFactory(
credentialsStore,
includeLogging = true
includeLogging = buildMeta.isDebug,
),
logger
).also {

View File

@ -132,7 +132,7 @@ ext.kotlinTest = { dependencies ->
dependencies.testImplementation Dependencies.mavenCentral.kluent
dependencies.testImplementation Dependencies.mavenCentral.kotlinTest
dependencies.testImplementation "org.jetbrains.kotlin:kotlin-test-junit:1.6.10"
dependencies.testImplementation 'io.mockk:mockk:1.12.7'
dependencies.testImplementation 'io.mockk:mockk:1.12.8'
dependencies.testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4'
dependencies.testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'

View File

@ -3,4 +3,5 @@ package app.dapk.st.core
data class BuildMeta(
val versionName: String,
val versionCode: Int,
val isDebug: Boolean,
)

View File

@ -143,7 +143,7 @@ ext.Dependencies.with {
junit = "junit:junit:4.13.2"
kluent = "org.amshove.kluent:kluent:1.68"
mockk = 'io.mockk:mockk:1.12.7'
mockk = 'io.mockk:mockk:1.12.8'
matrixOlm = "org.matrix.android:olm-sdk:3.2.12"
}

View File

@ -8,3 +8,7 @@ fun OlmAccount.readIdentityKeys(): Pair<Ed25519, Curve25519> {
val identityKeys = this.identityKeys()
return Ed25519(identityKeys["ed25519"]!!) to Curve25519(identityKeys["curve25519"]!!)
}
fun OlmAccount.oneTimeCurveKeys(): List<Pair<String, Curve25519>> {
return this.oneTimeKeys()["curve25519"]?.map { it.key to Curve25519(it.value) } ?: emptyList()
}

View File

@ -67,7 +67,7 @@ class OlmWrapper(
private suspend fun accountCrypto(deviceCredentials: DeviceCredentials): AccountCryptoSession? {
return olmStore.read()?.let { olmAccount ->
createAccountCryptoSession(deviceCredentials, olmAccount)
createAccountCryptoSession(deviceCredentials, olmAccount, isNew = false)
}
}
@ -80,12 +80,12 @@ class OlmWrapper(
val olmAccount = this.olmAccount as OlmAccount
olmAccount.generateOneTimeKeys(count)
val oneTimeKeys = DeviceService.OneTimeKeys(olmAccount.oneTimeKeys()["curve25519"]!!.map {
val oneTimeKeys = DeviceService.OneTimeKeys(olmAccount.oneTimeCurveKeys().map { (key, value) ->
DeviceService.OneTimeKeys.Key.SignedCurve(
keyId = it.key,
value = it.value,
keyId = key,
value = value.value,
signature = DeviceService.OneTimeKeys.Key.SignedCurve.Ed25519Signature(
value = it.value.toSignedJson(olmAccount),
value = value.value.toSignedJson(olmAccount),
deviceId = credentials.deviceId,
userId = credentials.userId,
)
@ -98,20 +98,21 @@ class OlmWrapper(
private suspend fun createAccountCrypto(deviceCredentials: DeviceCredentials, action: suspend (AccountCryptoSession) -> Unit): AccountCryptoSession {
val olmAccount = OlmAccount()
return createAccountCryptoSession(deviceCredentials, olmAccount).also {
return createAccountCryptoSession(deviceCredentials, olmAccount, isNew = true).also {
action(it)
olmStore.persist(olmAccount)
}
}
private fun createAccountCryptoSession(credentials: DeviceCredentials, olmAccount: OlmAccount): AccountCryptoSession {
private fun createAccountCryptoSession(credentials: DeviceCredentials, olmAccount: OlmAccount, isNew: Boolean): AccountCryptoSession {
val (identityKey, senderKey) = olmAccount.readIdentityKeys()
return AccountCryptoSession(
fingerprint = identityKey,
senderKey = senderKey,
deviceKeys = deviceKeyFactory.create(credentials.userId, credentials.deviceId, identityKey, senderKey, olmAccount),
olmAccount = olmAccount,
maxKeys = olmAccount.maxOneTimeKeys().toInt()
maxKeys = olmAccount.maxOneTimeKeys().toInt(),
hasKeys = !isNew,
)
}
@ -136,6 +137,7 @@ class OlmWrapper(
singletonFlows.update("room-${roomId.value}", rotatedSession)
}
}
else -> this
}
}
@ -277,10 +279,12 @@ class OlmWrapper(
}
}
}
OlmMessage.MESSAGE_TYPE_MESSAGE -> {
logger.crypto("decrypting olm message type")
session.decryptMessage(olmMessage)?.let { JsonString(it) }
}
else -> throw IllegalArgumentException("Unknown message type: $type")
}
}.onFailure {
@ -297,7 +301,7 @@ class OlmWrapper(
}
private suspend fun AccountCryptoSession.updateAccountInstance(olmAccount: OlmAccount) {
singletonFlows.update("account-crypto", this.copy(olmAccount = olmAccount))
singletonFlows.update("account-crypto", this.copy(olmAccount = olmAccount, hasKeys = true))
olmStore.persist(olmAccount)
}

View File

@ -100,19 +100,27 @@ class HomeViewModel(
Loading -> current
is SignedIn -> {
when (page) {
Page.Directory -> {
// do nothing
current.page -> current
else -> current.copy(page = page).also {
pageChangeSideEffects(page)
}
Page.Profile -> profileViewModel.reset()
}
current.copy(page = page)
}
SignedOut -> current
}
}
private fun pageChangeSideEffects(page: Page) {
when (page) {
Page.Directory -> {
// do nothing
}
Page.Profile -> profileViewModel.reset()
}
}
fun stop() {
viewModelScope.cancel()
}

View File

@ -18,7 +18,7 @@ private const val ENABLED_MATERIAL_YOU = true
class SettingsItemFactoryTest {
private val buildMeta = BuildMeta(versionName = "a-version-name", versionCode = 100)
private val buildMeta = BuildMeta(versionName = "a-version-name", versionCode = 100, isDebug = false)
private val deviceMeta = DeviceMeta(apiVersion = 31)
private val fakePushTokenRegistrars = FakePushRegistrars()
private val fakeThemeStore = FakeThemeStore()

View File

@ -72,6 +72,7 @@ interface Olm {
val fingerprint: Ed25519,
val senderKey: Curve25519,
val deviceKeys: DeviceKeys,
val hasKeys: Boolean,
val maxKeys: Int,
val olmAccount: Any,
)

View File

@ -15,16 +15,28 @@ internal class MaybeCreateAndUploadOneTimeKeysUseCaseImpl(
private val credentialsStore: CredentialsStore,
private val deviceService: DeviceService,
private val logger: MatrixLogger,
): MaybeCreateAndUploadOneTimeKeysUseCase {
) : MaybeCreateAndUploadOneTimeKeysUseCase {
override suspend fun invoke(currentServerKeyCount: ServerKeyCount) {
val ensureCryptoAccount = fetchAccountCryptoUseCase.invoke()
val keysDiff = (ensureCryptoAccount.maxKeys / 2) - currentServerKeyCount.value
if (keysDiff > 0) {
logger.crypto("current otk: $currentServerKeyCount, creating: $keysDiff")
ensureCryptoAccount.createAndUploadOneTimeKeys(countToCreate = keysDiff + (ensureCryptoAccount.maxKeys / 4))
} else {
logger.crypto("current otk: $currentServerKeyCount, not creating new keys")
val cryptoAccount = fetchAccountCryptoUseCase.invoke()
when {
currentServerKeyCount.value == 0 && cryptoAccount.hasKeys -> {
logger.crypto("Server has no keys but a crypto instance exists, waiting for next update")
}
else -> {
val keysDiff = (cryptoAccount.maxKeys / 2) - currentServerKeyCount.value
when {
keysDiff > 0 -> {
logger.crypto("current otk: $currentServerKeyCount, creating: $keysDiff")
cryptoAccount.createAndUploadOneTimeKeys(countToCreate = keysDiff + (cryptoAccount.maxKeys / 4))
}
else -> {
logger.crypto("current otk: $currentServerKeyCount, not creating new keys")
}
}
}
}
}

View File

@ -22,12 +22,11 @@ class MaybeCreateAndUploadOneTimeKeysUseCaseTest {
private val fakeDeviceService = FakeDeviceService()
private val fakeOlm = FakeOlm()
private val fakeCredentialsStore = FakeCredentialsStore().also {
it.givenCredentials().returns(A_USER_CREDENTIALS)
}
private val fakeCredentialsStore = FakeCredentialsStore().also { it.givenCredentials().returns(A_USER_CREDENTIALS) }
private val fakeFetchAccountCryptoUseCase = FakeFetchAccountCryptoUseCase()
private val maybeCreateAndUploadOneTimeKeysUseCase = MaybeCreateAndUploadOneTimeKeysUseCaseImpl(
FakeFetchAccountCryptoUseCase().also { it.givenFetch().returns(AN_ACCOUNT_CRYPTO_SESSION) },
fakeFetchAccountCryptoUseCase.also { it.givenFetch().returns(AN_ACCOUNT_CRYPTO_SESSION) },
fakeOlm,
fakeCredentialsStore,
fakeDeviceService,
@ -43,6 +42,16 @@ class MaybeCreateAndUploadOneTimeKeysUseCaseTest {
fakeDeviceService.verifyDidntUploadOneTimeKeys()
}
@Test
fun `given account has keys and server count is 0 then does nothing`() = runTest {
fakeFetchAccountCryptoUseCase.givenFetch().returns(AN_ACCOUNT_CRYPTO_SESSION.copy(hasKeys = true))
val zeroServiceKeys = ServerKeyCount(0)
maybeCreateAndUploadOneTimeKeysUseCase.invoke(zeroServiceKeys)
fakeDeviceService.verifyDidntUploadOneTimeKeys()
}
@Test
fun `given 0 current keys than generates and uploads 75 percent of the max key capacity`() = runTest {
fakeDeviceService.expect { it.uploadOneTimeKeys(GENERATED_ONE_TIME_KEYS) }

View File

@ -10,8 +10,9 @@ fun anAccountCryptoSession(
senderKey: Curve25519 = aCurve25519(),
deviceKeys: DeviceKeys = aDeviceKeys(),
maxKeys: Int = 5,
hasKeys: Boolean = false,
olmAccount: Any = mockk(),
) = Olm.AccountCryptoSession(fingerprint, senderKey, deviceKeys, maxKeys, olmAccount)
) = Olm.AccountCryptoSession(fingerprint, senderKey, deviceKeys, hasKeys, maxKeys, olmAccount)
fun aRoomCryptoSession(
creationTimestampUtc: Long = 0L,

View File

@ -56,6 +56,12 @@ internal data class ApiSyncRoom(
@SerialName("state") val state: ApiSyncRoomState,
@SerialName("account_data") val accountData: ApiAccountData? = null,
@SerialName("ephemeral") val ephemeral: ApiEphemeral? = null,
@SerialName("summary") val summary: ApiRoomSummary? = null,
)
@Serializable
internal data class ApiRoomSummary(
@SerialName("m.heroes") val heroes: List<UserId>? = null
)
@Serializable

View File

@ -21,7 +21,13 @@ internal sealed class ApiTimelineEvent {
@Serializable
internal data class Content(
@SerialName("type") val type: String? = null
)
) {
object Type {
const val SPACE = "m.space"
}
}
}
@Serializable
@ -50,6 +56,18 @@ internal sealed class ApiTimelineEvent {
)
}
@Serializable
@SerialName("m.room.canonical_alias")
internal data class CanonicalAlias(
@SerialName("event_id") val id: EventId,
@SerialName("content") val content: Content,
) : ApiTimelineEvent() {
@Serializable
internal data class Content(
@SerialName("alias") val alias: String
)
}
@Serializable
@SerialName("m.room.avatar")

View File

@ -27,6 +27,7 @@ internal class SyncSideEffects(
response.deviceLists?.changed?.ifEmpty { null }?.let {
notifyDevicesUpdated.notifyChanges(it, requestToken)
}
oneTimeKeyProducer.onServerKeyCount(response.oneTimeKeysCount["signed_curve25519"] ?: ServerKeyCount(0))
val decryptedToDeviceEvents = decryptedToDeviceEvents(response)

View File

@ -12,37 +12,43 @@ internal class RoomOverviewProcessor(
private val roomMembersService: RoomMembersService,
) {
suspend fun process(roomToProcess: RoomToProcess, previousState: RoomOverview?, lastMessage: LastMessage?): RoomOverview {
suspend fun process(roomToProcess: RoomToProcess, previousState: RoomOverview?, lastMessage: LastMessage?): RoomOverview? {
val combinedEvents = roomToProcess.apiSyncRoom.state.stateEvents + roomToProcess.apiSyncRoom.timeline.apiTimelineEvents
val isEncrypted = combinedEvents.any { it is ApiTimelineEvent.Encryption }
val readMarker = roomToProcess.apiSyncRoom.accountData?.events?.filterIsInstance<ApiAccountEvent.FullyRead>()?.firstOrNull()?.content?.eventId
return when (previousState) {
null -> combinedEvents.filterIsInstance<ApiTimelineEvent.RoomCreate>().first().let { roomCreate ->
val roomName = roomDisplayName(combinedEvents)
val isGroup = roomToProcess.directMessage == null
RoomOverview(
roomName = roomName ?: roomToProcess.directMessage?.let {
roomMembersService.find(roomToProcess.roomId, it)?.let { it.displayName ?: it.id.value }
},
roomCreationUtc = roomCreate.utcTimestamp,
lastMessage = lastMessage,
roomId = roomToProcess.roomId,
isGroup = isGroup,
roomAvatarUrl = roomAvatar(
roomToProcess.roomId,
roomMembersService,
roomToProcess.directMessage,
combinedEvents,
roomToProcess.userCredentials.homeServer
),
readMarker = readMarker,
isEncrypted = isEncrypted,
)
when (roomCreate.content.type) {
ApiTimelineEvent.RoomCreate.Content.Type.SPACE -> null
else -> {
val roomName = roomDisplayName(roomToProcess, combinedEvents)
val isGroup = roomToProcess.directMessage == null
val processedName = roomName ?: roomToProcess.directMessage?.let {
roomMembersService.find(roomToProcess.roomId, it)?.let { it.displayName ?: it.id.value }
}
RoomOverview(
roomName = processedName,
roomCreationUtc = roomCreate.utcTimestamp,
lastMessage = lastMessage,
roomId = roomToProcess.roomId,
isGroup = isGroup,
roomAvatarUrl = roomAvatar(
roomToProcess.roomId,
roomMembersService,
roomToProcess.directMessage,
combinedEvents,
roomToProcess.userCredentials.homeServer
),
readMarker = readMarker,
isEncrypted = isEncrypted,
)
}
}
}
else -> {
previousState.copy(
roomName = previousState.roomName ?: roomDisplayName(combinedEvents),
roomName = previousState.roomName ?: roomDisplayName(roomToProcess, combinedEvents),
lastMessage = lastMessage ?: previousState.lastMessage,
roomAvatarUrl = previousState.roomAvatarUrl ?: roomAvatar(
roomToProcess.roomId,
@ -58,9 +64,13 @@ internal class RoomOverviewProcessor(
}
}
private fun roomDisplayName(combinedEvents: List<ApiTimelineEvent>): String? {
val roomName = combinedEvents.filterIsInstance<ApiTimelineEvent.RoomName>().lastOrNull()
return roomName?.content?.name
private suspend fun roomDisplayName(roomToProcess: RoomToProcess, combinedEvents: List<ApiTimelineEvent>): String? {
val roomName = combinedEvents.filterIsInstance<ApiTimelineEvent.RoomName>().lastOrNull()?.content?.name
?: combinedEvents.filterIsInstance<ApiTimelineEvent.CanonicalAlias>().lastOrNull()?.content?.alias
?: roomToProcess.heroes?.let {
roomMembersService.find(roomToProcess.roomId, it).joinToString { it.displayName ?: it.id.value }
}
return roomName?.takeIf { it.isNotEmpty() }
}
private suspend fun roomAvatar(
@ -75,6 +85,7 @@ internal class RoomOverviewProcessor(
val filterIsInstance = combinedEvents.filterIsInstance<ApiTimelineEvent.RoomAvatar>()
filterIsInstance.lastOrNull()?.content?.url?.convertMxUrToUrl(homeServerUrl)?.let { AvatarUrl(it) }
}
else -> membersService.find(roomId, dmUser)?.avatarUrl
}
}

View File

@ -17,7 +17,7 @@ internal class RoomProcessor(
private val ephemeralEventsUseCase: EphemeralEventsUseCase,
) {
suspend fun processRoom(roomToProcess: RoomToProcess, isInitialSync: Boolean): RoomState {
suspend fun processRoom(roomToProcess: RoomToProcess, isInitialSync: Boolean): RoomState? {
val members = roomToProcess.apiSyncRoom.collectMembers(roomToProcess.userCredentials)
roomMembersService.insert(roomToProcess.roomId, members)
@ -28,16 +28,17 @@ internal class RoomProcessor(
previousState?.events ?: emptyList(),
)
val overview = createRoomOverview(distinctEvents, roomToProcess, previousState)
unreadEventsProcessor.processUnreadState(overview, previousState?.roomOverview, newEvents, roomToProcess.userCredentials.userId, isInitialSync)
return createRoomOverview(distinctEvents, roomToProcess, previousState)?.let {
unreadEventsProcessor.processUnreadState(it, previousState?.roomOverview, newEvents, roomToProcess.userCredentials.userId, isInitialSync)
return RoomState(overview, distinctEvents).also {
roomDataSource.persist(roomToProcess.roomId, previousState, it)
ephemeralEventsUseCase.processEvents(roomToProcess)
RoomState(it, distinctEvents).also {
roomDataSource.persist(roomToProcess.roomId, previousState, it)
ephemeralEventsUseCase.processEvents(roomToProcess)
}
}
}
private suspend fun createRoomOverview(distinctEvents: List<RoomEvent>, roomToProcess: RoomToProcess, previousState: RoomState?): RoomOverview {
private suspend fun createRoomOverview(distinctEvents: List<RoomEvent>, roomToProcess: RoomToProcess, previousState: RoomState?): RoomOverview? {
val lastMessage = distinctEvents.sortedByDescending { it.utcTimestamp }.findLastMessage()
return roomOverviewProcessor.process(roomToProcess, previousState?.roomOverview, lastMessage)
}
@ -56,6 +57,7 @@ private fun ApiSyncRoom.collectMembers(userCredentials: UserCredentials): List<R
avatarUrl = it.content.avatarUrl?.convertMxUrToUrl(userCredentials.homeServer)?.let { AvatarUrl(it) },
)
}
else -> null
}
}

View File

@ -10,4 +10,5 @@ internal data class RoomToProcess(
val apiSyncRoom: ApiSyncRoom,
val directMessage: UserId?,
val userCredentials: UserCredentials,
val heroes: List<UserId>?,
)

View File

@ -30,7 +30,6 @@ internal class SyncReducer(
suspend fun reduce(isInitialSync: Boolean, sideEffects: SideEffectResult, response: ApiSyncResponse, userCredentials: UserCredentials): ReducerResult {
val directMessages = response.directMessages()
val invites = response.rooms?.invite?.map { roomInvite(it, userCredentials) } ?: emptyList()
val roomsLeft = findRoomsLeft(response, userCredentials)
val newRooms = response.rooms?.join?.keys?.filterNot { roomDataSource.contains(it) } ?: emptyList()
@ -46,6 +45,7 @@ internal class SyncReducer(
apiSyncRoom = apiRoom,
directMessage = directMessages[roomId],
userCredentials = userCredentials,
heroes = apiRoom.summary?.heroes,
),
isInitialSync = isInitialSync
)

View File

@ -38,6 +38,7 @@ internal class TimelineEventsProcessor(
is ApiTimelineEvent.RoomMember -> null
is ApiTimelineEvent.RoomName -> null
is ApiTimelineEvent.RoomTopic -> null
is ApiTimelineEvent.CanonicalAlias -> null
ApiTimelineEvent.Ignored -> null
}
roomEvent

View File

@ -70,4 +70,5 @@ private fun aRoomToProcess(ephemeral: ApiEphemeral? = null) = RoomToProcess(
anApiSyncRoom(ephemeral = ephemeral),
directMessage = null,
userCredentials = aUserCredentials(),
heroes = null,
)

View File

@ -101,4 +101,4 @@ internal fun aRoomToProcess(
apiSyncRoom: ApiSyncRoom = anApiSyncRoom(),
directMessage: UserId? = null,
userCredentials: UserCredentials = aUserCredentials(),
) = RoomToProcess(roomId, apiSyncRoom, directMessage, userCredentials)
) = RoomToProcess(roomId, apiSyncRoom, directMessage, userCredentials, heroes = null)

View File

@ -9,7 +9,7 @@ test {
dependencies {
kotlinTest(it)
testImplementation 'app.cash.turbine:turbine:0.9.0'
testImplementation 'app.cash.turbine:turbine:0.10.0'
testImplementation Dependencies.mavenCentral.kotlinSerializationJson

View File

@ -20,10 +20,7 @@ import org.junit.jupiter.api.MethodOrderer
import org.junit.jupiter.api.Order
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestMethodOrder
import test.MatrixTestScope
import test.TestMatrix
import test.flowTest
import test.restoreLoginAndInitialSync
import test.*
import java.nio.file.Paths
import java.util.*
@ -35,8 +32,8 @@ class SmokeTest {
@Test
@Order(1)
fun `can register accounts`() = runTest {
SharedState._alice = createAndRegisterAccount()
SharedState._bob = createAndRegisterAccount()
SharedState._alice = createAndRegisterAccount("alice")
SharedState._bob = createAndRegisterAccount("bob")
}
@Test
@ -94,7 +91,7 @@ class SmokeTest {
@Test
fun `can import E2E room keys file`() = runTest {
val ignoredUser = TestUser("ignored", RoomMember(UserId("ignored"), null, null), "ignored")
val ignoredUser = TestUser("ignored", RoomMember(UserId("ignored"), null, null), "ignored", "ignored")
val cryptoService = TestMatrix(ignoredUser, includeLogging = true).client.cryptoService()
val stream = loadResourceStream("element-keys.txt")
@ -133,10 +130,10 @@ class SmokeTest {
}
}
private suspend fun createAndRegisterAccount(): TestUser {
private suspend fun createAndRegisterAccount(testUsername: String): TestUser {
val aUserName = "${UUID.randomUUID()}"
val userId = UserId("@$aUserName:localhost:8080")
val aUser = TestUser("aaaa11111zzzz", RoomMember(userId, aUserName, null), HTTPS_TEST_SERVER_URL)
val aUser = TestUser("aaaa11111zzzz", RoomMember(userId, aUserName, null), HTTPS_TEST_SERVER_URL, testUsername)
val result = TestMatrix(aUser, includeLogging = true, includeHttpLogging = true)
.client
@ -167,26 +164,35 @@ private suspend fun login(user: TestUser) {
}
object SharedState {
val alice: TestUser
get() = _alice!!
var _alice: TestUser? = null
set(value) {
field = value!!
TestUsers.users.add(value)
}
val bob: TestUser
get() = _bob!!
var _bob: TestUser? = null
set(value) {
field = value!!
TestUsers.users.add(value)
}
val sharedRoom: RoomId
get() = _sharedRoom!!
var _sharedRoom: RoomId? = null
}
data class TestUser(val password: String, val roomMember: RoomMember, val homeServer: String)
data class TestUser(val password: String, val roomMember: RoomMember, val homeServer: String, val testName: String)
data class TestMessage(val content: String, val author: RoomMember)
fun String.from(roomMember: RoomMember) = TestMessage("$this - ${UUID.randomUUID()}", roomMember)
fun testAfterInitialSync(block: suspend MatrixTestScope.(TestMatrix, TestMatrix) -> Unit) {
restoreLoginAndInitialSync(TestMatrix(SharedState.alice, includeLogging = false), TestMatrix(SharedState.bob, includeLogging = false), block)
restoreLoginAndInitialSync(TestMatrix(SharedState.alice, includeLogging = true), TestMatrix(SharedState.bob, includeLogging = false), block)
}
private fun Flow<Verification.State>.automaticVerification(testMatrix: TestMatrix) = this.onEach {

View File

@ -5,17 +5,13 @@ package test
import TestMessage
import TestUser
import app.dapk.st.core.extensions.ifNull
import app.dapk.st.matrix.common.MxUrl
import app.dapk.st.matrix.common.RoomId
import app.dapk.st.matrix.common.RoomMember
import app.dapk.st.matrix.common.convertMxUrToUrl
import app.dapk.st.matrix.http.MatrixHttpClient
import app.dapk.st.matrix.message.MessageService
import app.dapk.st.matrix.message.messageService
import app.dapk.st.matrix.sync.RoomEvent
import app.dapk.st.matrix.sync.syncService
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.util.cio.*
@ -28,7 +24,6 @@ import org.amshove.kluent.fail
import org.amshove.kluent.shouldBeEqualTo
import java.io.File
import java.math.BigInteger
import java.net.URL
import java.security.MessageDigest
import java.util.*

View File

@ -46,6 +46,12 @@ import java.io.File
import java.time.Clock
import javax.imageio.ImageIO
object TestUsers {
val users = mutableSetOf<TestUser>()
}
class TestMatrix(
private val user: TestUser,
temporaryDatabase: Boolean = false,
@ -53,10 +59,11 @@ class TestMatrix(
includeLogging: Boolean = false,
) {
private val errorTracker = PrintingErrorTracking(prefix = user.roomMember.id.value.split(":")[0])
private val errorTracker = PrintingErrorTracking(prefix = user.testName)
private val logger: MatrixLogger = { tag, message ->
if (includeLogging) {
println("${user.roomMember.id.value.split(":")[0]} $tag $message")
val messageWithIdReplaceByName = TestUsers.users.fold(message) { acc, user -> acc.replace(user.roomMember.id.value, "*${user.testName}") }
println("${user.testName} $tag $messageWithIdReplaceByName")
}
}

View File

@ -1,4 +1,4 @@
{
"code": 16,
"name": "15/09/2022-V1"
"code": 17,
"name": "19/09/2022-V1"
}