diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/di/DataRegistryModule.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/di/DataRegistryModule.kt index f4a612b8c5..0057f25465 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/di/DataRegistryModule.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/di/DataRegistryModule.kt @@ -19,6 +19,8 @@ import com.github.dedis.popstellar.model.network.method.message.data.election.El import com.github.dedis.popstellar.model.network.method.message.data.election.ElectionResult import com.github.dedis.popstellar.model.network.method.message.data.election.ElectionSetup import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge +import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult +import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange import com.github.dedis.popstellar.model.network.method.message.data.lao.CreateLao import com.github.dedis.popstellar.model.network.method.message.data.lao.GreetLao import com.github.dedis.popstellar.model.network.method.message.data.lao.StateLao @@ -259,6 +261,18 @@ object DataRegistryModule { linkedOrganizationsHandler.handleChallenge(context, challenge) } + builder.add(Objects.FEDERATION, Action.RESULT, FederationResult::class.java) { + context: HandlerContext, + result: FederationResult -> + linkedOrganizationsHandler.handleResult(context, result) + } + + builder.add(Objects.FEDERATION, Action.TOKENS_EXCHANGE, TokensExchange::class.java) { + context: HandlerContext, + tokenExchange: TokensExchange -> + linkedOrganizationsHandler.handleTokensExchange(context, tokenExchange) + } + return builder.build() } @@ -327,6 +341,8 @@ object DataRegistryModule { // Federation builder.add(Objects.FEDERATION, Action.CHALLENGE, Challenge::class.java, null) + builder.add(Objects.FEDERATION, Action.RESULT, FederationResult::class.java, null) + builder.add(Objects.FEDERATION, Action.TOKENS_EXCHANGE, TokensExchange::class.java, null) return builder.build() } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/Action.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/Action.kt index db8b5fde08..a663b6d3c3 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/Action.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/Action.kt @@ -44,6 +44,7 @@ enum class Action CHALLENGE("challenge"), INIT("init"), EXPECT("expect"), + TOKENS_EXCHANGE("tokens_exchange"), RUMOR("rumor"); /** diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt new file mode 100644 index 0000000000..285a97044a --- /dev/null +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResult.kt @@ -0,0 +1,73 @@ +package com.github.dedis.popstellar.model.network.method.message.data.federation + +import com.github.dedis.popstellar.model.network.method.message.MessageGeneral +import com.github.dedis.popstellar.model.network.method.message.data.Action +import com.github.dedis.popstellar.model.network.method.message.data.Data +import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.github.dedis.popstellar.utility.MessageValidator +import com.google.gson.annotations.SerializedName + +/** Informs about the result of the authentication procedure */ +class FederationResult +/** + * Constructor for a data Federation Result + * + * @param status status of the result (either success or failure) + * @param reason reason of the failure + * @param publicKey public key of the other LAO organizer + * @param challenge challenge used to connect the LAOs + */ +( + val status: String, + val reason: String? = null, + @SerializedName("public_key") val publicKey: String? = null, + val challenge: MessageGeneral, +) : Data { + + init { + MessageValidator.verify().isValidFederationResult(status, reason, publicKey, challenge) + } + + override val `object`: String + get() = Objects.FEDERATION.`object` + + override val action: String + get() = Action.RESULT.action + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other == null || javaClass != other.javaClass) { + return false + } + val that = other as FederationResult + return status == that.status && + reason == that.reason && + publicKey == that.publicKey && + challenge == that.challenge + } + + override fun hashCode(): Int { + return java.util.Objects.hash(status, reason, publicKey, challenge) + } + + override fun toString(): String { + if (status == FAILURE) { + return "FederationResult{status='$status', reason='$reason', challenge='$challenge'}" + } else if (status == SUCCESS) { + return "FederationResult{status='$status', public_key='$publicKey', " + + "challenge='$challenge'}" + } + return "FederationResult{ERROR}" + } + + fun isSuccess(): Boolean { + return status == SUCCESS + } + + companion object { + const val SUCCESS = "success" + const val FAILURE = "failure" + } +} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt new file mode 100644 index 0000000000..f9540491ff --- /dev/null +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchange.kt @@ -0,0 +1,62 @@ +package com.github.dedis.popstellar.model.network.method.message.data.federation + +import com.github.dedis.popstellar.model.network.method.message.data.Action +import com.github.dedis.popstellar.model.network.method.message.data.Data +import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.github.dedis.popstellar.utility.MessageValidator +import com.google.gson.annotations.SerializedName + +/** Token exchange to be broadcast in the LAO */ +class TokensExchange +/** + * Constructor for a data TokenExchange + * + * @param laoId ID of the remote LAO + * @param rollCallId ID of the rollCall of the remote LAO + * @param tokens array of tokens contained in the rollCall + * @param timestamp timestamp of the message + */ +( + @SerializedName("lao_id") val laoId: String, + @SerializedName("roll_call_id") val rollCallId: String, + val tokens: Array, + val timestamp: Long +) : Data { + + init { + MessageValidator.verify() + .isNotEmptyBase64(laoId, "lao_id") + .isBase64(rollCallId, "roll_call_id") + .arrayElementsNotEmptyBase64(tokens, field = "tokens") + .validPastTimes(timestamp) + } + + override val `object`: String + get() = Objects.FEDERATION.`object` + + override val action: String + get() = Action.TOKENS_EXCHANGE.action + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + if (other == null || javaClass != other.javaClass) { + return false + } + val that = other as TokensExchange + return laoId == that.laoId && + rollCallId == that.rollCallId && + tokens.contentEquals(that.tokens) && + timestamp == that.timestamp + } + + override fun hashCode(): Int { + return java.util.Objects.hash(laoId, rollCallId, tokens, timestamp) + } + + override fun toString(): String { + return "TokensExchange{lao_id='$laoId', roll_call_id='$rollCallId', " + + "tokens='$tokens', timestamp='$timestamp'}" + } +} diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/objects/Chirp.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/objects/Chirp.kt index 4d96c1c53b..979ececcae 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/objects/Chirp.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/model/objects/Chirp.kt @@ -13,6 +13,7 @@ class Chirp : Copyable { val timestamp: Long val isDeleted: Boolean val parentId: MessageID + val laoId: String constructor( id: MessageID, @@ -20,7 +21,8 @@ class Chirp : Copyable { text: String, timestamp: Long, isDeleted: Boolean, - parentId: MessageID + parentId: MessageID, + laoId: String ) { require(id.encoded.isNotEmpty()) { "The id of the Chirp is empty" } require(timestamp >= 0) { "The timestamp of the Chirp is negative" } @@ -32,6 +34,7 @@ class Chirp : Copyable { this.timestamp = timestamp this.parentId = parentId this.isDeleted = isDeleted + this.laoId = laoId } constructor( @@ -39,8 +42,9 @@ class Chirp : Copyable { sender: PublicKey, text: String, timestamp: Long, - parentId: MessageID - ) : this(id, sender, text, timestamp, false, parentId) + parentId: MessageID, + laoId: String + ) : this(id, sender, text, timestamp, false, parentId, laoId) constructor(chirp: Chirp, deleted: Boolean) { id = chirp.id @@ -49,6 +53,7 @@ class Chirp : Copyable { timestamp = chirp.timestamp parentId = chirp.parentId isDeleted = deleted + laoId = chirp.laoId } constructor(chirp: Chirp) { @@ -58,6 +63,7 @@ class Chirp : Copyable { timestamp = chirp.timestamp isDeleted = chirp.isDeleted parentId = chirp.parentId + laoId = chirp.laoId } override fun copy(): Chirp { diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt index 9b3571169b..b932dc1bb2 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepository.kt @@ -1,6 +1,14 @@ package com.github.dedis.popstellar.repository +import android.app.Activity +import android.app.Application +import androidx.lifecycle.Lifecycle import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge +import com.github.dedis.popstellar.utility.GeneralUtils +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable +import java.util.EnumMap +import java.util.function.Consumer import javax.inject.Inject import javax.inject.Singleton @@ -10,18 +18,25 @@ import javax.inject.Singleton * Its main purpose is to store received messages */ @Singleton -class LinkedOrganizationsRepository @Inject constructor() { +class LinkedOrganizationsRepository @Inject constructor(application: Application) { private var challenge: Challenge? = null private var onChallengeUpdatedCallback: ((Challenge) -> Unit)? = null + private var linkedLaos: MutableMap>> = mutableMapOf() + private var onLinkedLaosUpdatedCallback: ((String, MutableMap>) -> Unit)? = + null + private var newTokensNotifyFunction: ((String, String, String, Array) -> Unit)? = null + private val disposables = CompositeDisposable() var otherLaoId: String? = null var otherServerAddr: String? = null var otherPublicKey: String? = null - /** - * Updates the challenge - * - * @param challenge the new Challenge - */ + init { + val consumerMap: MutableMap> = + EnumMap(Lifecycle.Event::class.java) + consumerMap[Lifecycle.Event.ON_STOP] = Consumer { disposables.clear() } + application.registerActivityLifecycleCallbacks(GeneralUtils.buildLifecycleCallback(consumerMap)) + } + fun updateChallenge(challenge: Challenge) { this.challenge = challenge onChallengeUpdatedCallback?.invoke(challenge) @@ -35,6 +50,40 @@ class LinkedOrganizationsRepository @Inject constructor() { return challenge } + fun addLinkedLao(laoId: String, otherLaoId: String, tokens: Array) { + val laoMap = linkedLaos.getOrPut(laoId) { mutableMapOf() } + laoMap[otherLaoId] = tokens + onLinkedLaosUpdatedCallback?.invoke(laoId, laoMap) + } + + fun updateAndNotifyLinkedLao( + laoId: String, + otherLaoId: String, + tokens: Array, + rollCallId: String + ) { + addLinkedLao(laoId, otherLaoId, tokens) + newTokensNotifyFunction?.invoke(laoId, otherLaoId, rollCallId, tokens) + } + + fun setOnLinkedLaosUpdatedCallback( + callback: (String, MutableMap>) -> Unit + ) { + onLinkedLaosUpdatedCallback = callback + } + + fun setNewTokensNotifyFunction(function: (String, String, String, Array) -> Unit) { + newTokensNotifyFunction = function + } + + fun getLinkedLaos(laoId: String): MutableMap> { + return linkedLaos.getOrDefault(laoId, mutableMapOf()) + } + + fun addDisposable(disposable: Disposable) { + disposables.add(disposable) + } + fun flush() { otherLaoId = null otherServerAddr = null diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt index b772c043c2..9010d88f9b 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragment.kt @@ -17,6 +17,9 @@ import com.github.dedis.popstellar.ui.lao.event.LaoDetailAnimation.showIn import com.github.dedis.popstellar.ui.lao.event.LaoDetailAnimation.showOut import com.github.dedis.popstellar.ui.qrcode.QrScannerFragment import com.github.dedis.popstellar.ui.qrcode.ScanningAction +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch /** * A simple [Fragment] subclass. Use the [LinkedOrganizationsFragment.newInstance] factory method to @@ -41,9 +44,6 @@ class LinkedOrganizationsFragment : Fragment() { linkedOrganizationsViewModel = obtainLinkedOrganizationsViewModel(requireActivity(), laoViewModel.laoId) - // Starts from a clean repository - linkedOrganizationsViewModel.flushRepository() - // Sets the text and the button depending on the user's role laoViewModel.role.observe(viewLifecycleOwner) { role: Role -> if (role == Role.ORGANIZER) { @@ -59,6 +59,19 @@ class LinkedOrganizationsFragment : Fragment() { binding.inviteOtherOrganization.setOnClickListener(invitationPage) binding.joinOtherOrganizationInvitation.setOnClickListener(joinButton) + // Displaying the linked organizations + val laos = linkedOrganizationsViewModel.getLinkedLaosMap().keys + displayLinkedOrganizations(laos) + linkedOrganizationsViewModel.doWhenLinkedLaosIsUpdated { laoId, laoMap -> + if (laoId == laoViewModel.laoId) { + CoroutineScope(Dispatchers.Main).launch { + val currentLaos = laoMap.keys + displayLinkedOrganizations(currentLaos) + } + } + } + + linkedOrganizationsViewModel.setLinkedLaosNotifyFunction() handleBackNav() return binding.root @@ -97,6 +110,7 @@ class LinkedOrganizationsFragment : Fragment() { private var joinButton = View.OnClickListener { + linkedOrganizationsViewModel.flushRepository() laoViewModel.setIsTab(false) linkedOrganizationsViewModel.manager = parentFragmentManager LaoActivity.setCurrentFragment(parentFragmentManager, R.id.fragment_qr_scanner) { @@ -104,6 +118,19 @@ class LinkedOrganizationsFragment : Fragment() { } } + private fun displayLinkedOrganizations(laos: Set) { + if (laos.isNotEmpty()) { + val laosText = laos.joinToString(separator = "\n\n") + val textToDisplay = context?.getString(R.string.list_organizations, laosText) + binding.noOrganizationsText.visibility = View.GONE + binding.listOrganizationsText.visibility = View.VISIBLE + binding.listOrganizationsText.text = textToDisplay + } else { + binding.listOrganizationsText.visibility = View.GONE + binding.noOrganizationsText.visibility = View.VISIBLE + } + } + private fun handleBackNav() { LaoActivity.addBackNavigationCallbackToEvents(requireActivity(), viewLifecycleOwner, TAG) } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsInviteFragment.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsInviteFragment.kt index f44e9664c6..7a9fae574a 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsInviteFragment.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsInviteFragment.kt @@ -106,6 +106,7 @@ class LinkedOrganizationsInviteFragment(createsInvitation: Boolean) : Fragment() } private fun createInvitationSetup(binding: LinkedOrganizationsInviteFragmentBinding) { + // Setup buttons binding.nextStepButton.setText(R.string.next_step) binding.nextStepButton.visibility = View.GONE binding.nextStepButton.setOnClickListener { openScanner() } @@ -113,6 +114,11 @@ class LinkedOrganizationsInviteFragment(createsInvitation: Boolean) : Fragment() binding.loadingText.visibility = View.VISIBLE binding.linkedOrganizationsNameTitle.visibility = View.GONE binding.linkedOrganizationsServerTitle.visibility = View.GONE + + // Starts with a clean repository + linkedOrganizationsViewModel.flushRepository() + + // Sends the challenge request laoViewModel.addDisposable( linkedOrganizationsViewModel .sendChallengeRequest(Instant.now().epochSecond) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt index 7e52183404..213854bac8 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsViewModel.kt @@ -10,6 +10,7 @@ import com.github.dedis.popstellar.model.network.method.message.data.federation. import com.github.dedis.popstellar.model.network.method.message.data.federation.ChallengeRequest import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationExpect import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationInit +import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange import com.github.dedis.popstellar.model.objects.view.LaoView import com.github.dedis.popstellar.model.qrcode.FederationDetails import com.github.dedis.popstellar.repository.LAORepository @@ -23,6 +24,7 @@ import com.google.gson.Gson import dagger.hilt.android.lifecycle.HiltViewModel import io.reactivex.Completable import io.reactivex.disposables.CompositeDisposable +import java.time.Instant import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject @@ -137,10 +139,39 @@ constructor( ) } + /** + * Sends a Token Exchange data + * + * @param remoteLaoId ID of the remote LAO + * @param rollCallId ID of the rollCall of the remote LAO + * @param attendees array with the token of each attendee + */ + fun sendTokensExchange( + remoteLaoId: String, + rollCallId: String, + attendees: Array + ): Completable { + val laoView: LaoView = + try { + laoRepo.getLaoView(laoId) + } catch (e: UnknownLaoException) { + ErrorUtils.logAndShow(getApplication(), TAG, e, R.string.unknown_lao_exception) + return Completable.error(UnknownLaoException()) + } + val timestamp = Instant.now().epochSecond + val tokensExchange = TokensExchange(remoteLaoId, rollCallId, attendees, timestamp) + return networkManager.messageSender.publish( + keyManager.mainKeyPair, laoView.channel.subChannel(FEDERATION), tokensExchange) + } + fun getChallenge(): Challenge? { return linkedOrgRepo.getChallenge() } + fun getLinkedLaosMap(): Map> { + return linkedOrgRepo.getLinkedLaos(laoId) + } + fun isRepositoryValid(): Boolean { return linkedOrgRepo.otherLaoId != null && linkedOrgRepo.otherServerAddr != null && @@ -159,6 +190,26 @@ constructor( linkedOrgRepo.setOnChallengeUpdatedCallback(function) } + fun doWhenLinkedLaosIsUpdated(function: (String, MutableMap>) -> Unit) { + linkedOrgRepo.setOnLinkedLaosUpdatedCallback(function) + } + + fun setLinkedLaosNotifyFunction() { + linkedOrgRepo.setNewTokensNotifyFunction { receivedLaoId, otherLaoId, rollCallId, tokens -> + if (receivedLaoId == laoId) { + disposables.add( + sendTokensExchange(otherLaoId, rollCallId, tokens) + .subscribe( + { ErrorUtils.logAndShow(getApplication(), TAG, R.string.tokens_exchange_sent) }, + { error: Throwable -> + ErrorUtils.logAndShow( + getApplication(), TAG, error, R.string.error_sending_tokens_exchange) + }, + )) + } + } + } + override fun handleData(data: String?) { // Don't process another data from the scanner if I'm already trying to scan if (connecting.get()) { @@ -179,6 +230,11 @@ constructor( return } + // Saving the other organization details to the repository + linkedOrgRepo.otherLaoId = federationDetails.laoId + linkedOrgRepo.otherServerAddr = federationDetails.serverAddress + linkedOrgRepo.otherPublicKey = federationDetails.publicKey + if (federationDetails.challenge == null) { // The federationDetails object is sent by the invitation creator disposables.add( @@ -196,9 +252,6 @@ constructor( )) } else { // The federationDetails object is sent by the one who joins the invitation - linkedOrgRepo.otherLaoId = federationDetails.laoId - linkedOrgRepo.otherServerAddr = federationDetails.serverAddress - linkedOrgRepo.otherPublicKey = federationDetails.publicKey linkedOrgRepo.updateChallenge(federationDetails.challenge) } connecting.set(false) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapter.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapter.kt index d42fff6d47..5b7255dca2 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapter.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapter.kt @@ -101,6 +101,7 @@ class ChirpListAdapter( val reactionDisposable = socialMediaViewModel .getReactions( + chirp.laoId, chirp.id) // Each time the observable changes the counter and the selection is // notified .subscribe( diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt index 784a4c0608..bb70f57ada 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaViewModel.kt @@ -16,6 +16,7 @@ import com.github.dedis.popstellar.model.objects.security.MessageID import com.github.dedis.popstellar.model.objects.security.PoPToken import com.github.dedis.popstellar.model.objects.view.LaoView import com.github.dedis.popstellar.repository.LAORepository +import com.github.dedis.popstellar.repository.LinkedOrganizationsRepository import com.github.dedis.popstellar.repository.RollCallRepository import com.github.dedis.popstellar.repository.SocialMediaRepository import com.github.dedis.popstellar.repository.remote.GlobalNetworkManager @@ -47,6 +48,7 @@ constructor( */ private val laoRepo: LAORepository, private val rollCallRepo: RollCallRepository, + private val linkedOrgRepo: LinkedOrganizationsRepository, private val schedulerProvider: SchedulerProvider, private val socialMediaRepository: SocialMediaRepository, private val networkManager: GlobalNetworkManager, @@ -225,36 +227,50 @@ constructor( } val chirps: Observable> - get() = - socialMediaRepository - .getChirpsOfLao(laoId) - // Retrieve chirp subjects per id - .map { ids: Set -> - val chirps: MutableList> = ArrayList(ids.size) - for (id in ids) { - chirps.add(socialMediaRepository.getChirp(laoId, id)) - } - chirps - } - // Zip the subjects together to a sorted list - .flatMap { observables: List> -> - Observable.combineLatest(observables) { chirps: Array -> - Arrays.stream(chirps) - .map { obj: Any? -> Chirp::class.java.cast(obj) } - .sorted( - Comparator.comparing { chirp: Chirp? -> - if (chirp != null) -chirp.timestamp else 0 - }) - .collect(Collectors.toList()) - } - } - // We want to observe these changes on the main thread such that any modification done - // to - // the view are done on the thread. Otherwise, the app might crash - .observeOn(schedulerProvider.mainThread()) + get() { + val laoIds = listOf(laoId) + linkedOrgRepo.getLinkedLaos(laoId).keys + val laoObservables: MutableList>> = ArrayList() + for (lao in laoIds) { + val observable = + socialMediaRepository + .getChirpsOfLao(lao) + .map { ids: Set -> + val chirps: MutableList> = ArrayList(ids.size) + for (id in ids) { + chirps.add(socialMediaRepository.getChirp(lao, id)) + } + chirps + } + .flatMap { observables: List> -> + Observable.combineLatest(observables) { chirps: Array -> + Arrays.stream(chirps) + .map { obj: Any? -> Chirp::class.java.cast(obj) } + .sorted( + Comparator.comparing { chirp: Chirp? -> + if (chirp != null) -chirp.timestamp else 0 + }) + .collect(Collectors.toList()) + } + } + laoObservables.add(observable) + } + val combinedObservables = + Observable.combineLatest(laoObservables) { chirpsLists: Array -> + Arrays.stream(chirpsLists) + .flatMap { chirpsList: Any? -> (chirpsList as List).stream() } + .sorted( + Comparator.comparing { chirp: Chirp? -> + if (chirp != null) -chirp.timestamp else 0 + }) + .collect(Collectors.toList()) + } + // We want to observe these changes on the main thread such that any modification done + // to the view are done on the thread. Otherwise, the app might crash + return combinedObservables.observeOn(schedulerProvider.mainThread()) + } @Throws(UnknownChirpException::class) - fun getReactions(chirpId: MessageID): Observable> { + fun getReactions(laoId: String, chirpId: MessageID): Observable> { return socialMediaRepository .getReactions(laoId, chirpId) .observeOn(schedulerProvider.mainThread()) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt index 537599f83e..4df33745fc 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/MessageValidator.kt @@ -3,6 +3,7 @@ package com.github.dedis.popstellar.utility import android.net.Uri import com.github.dedis.popstellar.model.network.method.message.MessageGeneral import com.github.dedis.popstellar.model.network.method.message.data.election.Vote +import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult import com.github.dedis.popstellar.model.objects.Lao import com.github.dedis.popstellar.model.objects.Meeting import com.github.dedis.popstellar.model.objects.Reaction @@ -161,6 +162,19 @@ object MessageValidator { return this } + /** + * Helper method to ensure all strings in an Array are valid, not-empty URL-safe base64 + * encodings. + * + * @param inputs the strings to check + * @param field the name of the field (to print in case of error) + * @throws IllegalArgumentException if any string is empty or not a URL-safe base64 encoding + */ + fun arrayElementsNotEmptyBase64(inputs: Array, field: String): MessageValidatorBuilder { + inputs.forEach { input -> isNotEmptyBase64(input, field) } + return this + } + fun isNotNull(input: Any?, field: String): MessageValidatorBuilder { requireNotNull(input) { "$field cannot be null" } return this @@ -369,6 +383,30 @@ object MessageValidator { return this } + fun isValidFederationResult( + status: String, + reason: String? = null, + publicKey: String? = null, + challenge: MessageGeneral + ): MessageValidatorBuilder { + val success = FederationResult.SUCCESS + val failure = FederationResult.FAILURE + when (status) { + failure -> { + require(reason != null) { "Reason must be provided for $failure status." } + require(publicKey == null) { "Public key must be null for $failure status." } + } + success -> { + require(publicKey != null) { "Public key must be provided for $success status." } + require(reason == null) { "Reason must be null for $success status." } + verify().isNotEmptyBase64(publicKey, "public_key") + } + else -> throw IllegalArgumentException("Status must be either '$failure' or '$success'.") + } + verify().validMessage(challenge) + return this + } + companion object { // Defines how old messages can be to be considered valid, keeping it non-restrictive here for // now diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt index 11b5de5e30..758b6ce8b7 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ChirpHandler.kt @@ -37,16 +37,16 @@ constructor( channel, addChirp.getParentId().orElse(MessageID(""))) - val laoView = laoRepo.getLaoViewByChannel(channel) val chirp = Chirp( messageId, senderPk, addChirp.text, addChirp.timestamp, - addChirp.getParentId().orElse(MessageID(""))) + addChirp.getParentId().orElse(MessageID("")), + channel.extractLaoId()) - socialMediaRepo.addChirp(laoView.id, chirp) + socialMediaRepo.addChirp(channel.extractLaoId(), chirp) } /** @@ -60,8 +60,7 @@ constructor( val channel = context.channel Timber.tag(TAG).d("handleDeleteChirp: channel: %s, id: %s", channel, deleteChirp.chirpId) - val laoView = laoRepo.getLaoViewByChannel(channel) - val chirpExist = socialMediaRepo.deleteChirp(laoView.id, deleteChirp.chirpId) + val chirpExist = socialMediaRepo.deleteChirp(channel.extractLaoId(), deleteChirp.chirpId) if (!chirpExist) { throw InvalidMessageIdException(deleteChirp, deleteChirp.chirpId) diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt index c329833a7a..77ffbd442d 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/LinkedOrganizationsHandler.kt @@ -1,17 +1,24 @@ package com.github.dedis.popstellar.utility.handler.data import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge +import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult +import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange +import com.github.dedis.popstellar.model.objects.Channel import com.github.dedis.popstellar.repository.LAORepository import com.github.dedis.popstellar.repository.LinkedOrganizationsRepository +import com.github.dedis.popstellar.repository.RollCallRepository import com.github.dedis.popstellar.utility.error.UnknownLaoException +import com.github.dedis.popstellar.utility.error.keys.NoRollCallException import javax.inject.Inject +import timber.log.Timber /** Federation messages handler class */ class LinkedOrganizationsHandler @Inject constructor( private val laoRepo: LAORepository, - private val linkedOrgRepo: LinkedOrganizationsRepository + private val linkedOrgRepo: LinkedOrganizationsRepository, + private val rollCallRepo: RollCallRepository ) { /** @@ -24,4 +31,84 @@ constructor( fun handleChallenge(context: HandlerContext, challenge: Challenge) { linkedOrgRepo.updateChallenge(challenge) } + + /** + * Process a Federation Result message + * + * @param context the HandlerContext of the message + * @param result the message that was received + */ + @Throws(UnknownLaoException::class) + fun handleResult(context: HandlerContext, result: FederationResult) { + if (result.isSuccess()) { + if (result.challenge.data == linkedOrgRepo.getChallenge() && + linkedOrgRepo.otherLaoId != null) { + val laoId = context.channel.extractLaoId() + linkedOrgRepo.addLinkedLao(laoId, linkedOrgRepo.otherLaoId!!, arrayOf()) + linkedOrgRepo.addDisposable( + context.messageSender + .subscribe(Channel.getLaoChannel(linkedOrgRepo.otherLaoId!!)) + .subscribe( + { putRemoteLaoTokensInRepository(laoId) }, + { error: Throwable -> Timber.tag(TAG).e(error, ERROR) })) + } else { + Timber.tag(TAG).d("Invalid FederationResult success") + } + } else { + Timber.tag(TAG).d("FederationResult failure : %s", result.reason) + } + } + + /** + * Process a Token Exchange message + * + * @param context the HandlerContext of the message + * @param tokenExchange the message that was received + */ + @Throws(UnknownLaoException::class) + fun handleTokensExchange(context: HandlerContext, tokenExchange: TokensExchange) { + // Adds the tokens in the repository + linkedOrgRepo.addLinkedLao( + context.channel.extractLaoId(), tokenExchange.laoId, tokenExchange.tokens) + + // Subscribes to social of the linked organization automatically + // Note that for now the participants of an LAO automatically subscribe to social of the other + // LAO. This might be changed in the future (making a pop-up asking the user if he/she wants + // to subscribe to that) + tokenExchange.tokens.forEach { t -> + linkedOrgRepo.addDisposable( + context.messageSender + .subscribe( + Channel.getLaoChannel(tokenExchange.laoId).subChannel(SOCIAL).subChannel(t)) + .subscribe( + { Timber.tag(TAG).d(SUCCESS) }, + { error: Throwable -> Timber.tag(TAG).e(error, ERROR) })) + } + linkedOrgRepo.addDisposable( + context.messageSender + .subscribe( + Channel.getLaoChannel(tokenExchange.laoId).subChannel(SOCIAL).subChannel(REACTIONS)) + .subscribe( + { Timber.tag(TAG).d(SUCCESS) }, + { error: Throwable -> Timber.tag(TAG).e(error, ERROR) })) + } + + private fun putRemoteLaoTokensInRepository(myLaoId: String) { + try { + val rollCall = rollCallRepo.getLastClosedRollCall(linkedOrgRepo.otherLaoId!!) + val attendees = rollCall.attendees.map { e -> e.encoded }.toTypedArray() + linkedOrgRepo.updateAndNotifyLinkedLao( + myLaoId, linkedOrgRepo.otherLaoId!!, attendees, rollCall.persistentId) + } catch (e: NoRollCallException) { + Timber.tag(TAG).d("No RollCall was found on the linked LAO") + } + } + + companion object { + private val TAG = LinkedOrganizationsHandler::class.java.simpleName + private const val SOCIAL = "social" + private const val REACTIONS = "reactions" + private const val SUCCESS = "subscription is a success" + private const val ERROR = "subscription error" + } } diff --git a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ReactionHandler.kt b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ReactionHandler.kt index fd2f8044da..05280bd263 100644 --- a/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ReactionHandler.kt +++ b/fe2-android/app/src/main/java/com/github/dedis/popstellar/utility/handler/data/ReactionHandler.kt @@ -31,12 +31,11 @@ constructor( val senderPk = context.senderPk Timber.tag(TAG).d("handleAddReaction: channel: %s, chirp id: %s", channel, addReaction.chirpId) - val laoView = laoRepo.getLaoViewByChannel(channel) val reaction = Reaction( messageId, senderPk, addReaction.codepoint, addReaction.chirpId, addReaction.timestamp) - if (!socialMediaRepo.addReaction(laoView.id, reaction)) { + if (!socialMediaRepo.addReaction(channel.extractLaoId(), reaction)) { throw InvalidMessageIdException(addReaction, addReaction.chirpId) } } @@ -53,8 +52,7 @@ constructor( Timber.tag(TAG) .d("handleDeleteReaction: channel: %s, reaction id: %s", channel, deleteReaction.reactionID) - val laoView = laoRepo.getLaoViewByChannel(channel) - if (!socialMediaRepo.deleteReaction(laoView.id, deleteReaction.reactionID)) { + if (!socialMediaRepo.deleteReaction(channel.extractLaoId(), deleteReaction.reactionID)) { throw InvalidMessageIdException(deleteReaction, deleteReaction.reactionID) } } diff --git a/fe2-android/app/src/main/res/layout/linked_organizations_fragment.xml b/fe2-android/app/src/main/res/layout/linked_organizations_fragment.xml index 295fbdfad5..d59188556a 100644 --- a/fe2-android/app/src/main/res/layout/linked_organizations_fragment.xml +++ b/fe2-android/app/src/main/res/layout/linked_organizations_fragment.xml @@ -102,7 +102,8 @@ android:id="@+id/empty_events_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_margin="@dimen/main_horizontal_margin"> + android:layout_margin="@dimen/main_horizontal_margin" + android:orientation="horizontal"> + + diff --git a/fe2-android/app/src/main/res/values/strings.xml b/fe2-android/app/src/main/res/values/strings.xml index 047b5fc3bd..ea5a2741a0 100644 --- a/fe2-android/app/src/main/res/values/strings.xml +++ b/fe2-android/app/src/main/res/values/strings.xml @@ -334,6 +334,8 @@ Loading… Federation expect sent successfully Federation init sent successfully + List of linked organizations :\n\n%s + Tokens exchange sent successfully No stored data found @@ -404,6 +406,7 @@ Could not send the federation expect message Could not send the federation init message Invalid federation information + Could not send the tokens exchange message An error response was replied by the server\nError %1$d - %2$s diff --git a/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/lao/federation/LinkedOrganizationsFragmentPageObject.java b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/lao/federation/LinkedOrganizationsFragmentPageObject.java index 493d93aa41..9be9a3749a 100644 --- a/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/lao/federation/LinkedOrganizationsFragmentPageObject.java +++ b/fe2-android/app/src/test/framework/common/java/com/github/dedis/popstellar/testutils/pages/lao/federation/LinkedOrganizationsFragmentPageObject.java @@ -32,4 +32,12 @@ public static ViewInteraction nextInvitationFragment() { public static ViewInteraction nextQrScannerFragment() { return onView(withId(R.id.fragment_qr_scanner)); } + + public static ViewInteraction noOrganizationsText() { + return onView(withId(R.id.no_organizations_text)); + } + + public static ViewInteraction listOrganizationsText() { + return onView(withId(R.id.list_organizations_text)); + } } diff --git a/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt b/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt index 9266b70757..ca3d4c7972 100644 --- a/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt +++ b/fe2-android/app/src/test/framework/robolectric/java/com/github/dedis/popstellar/di/DataRegistryModuleHelper.kt @@ -64,7 +64,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), consensusRepository, - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -86,7 +86,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -109,7 +109,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -132,7 +132,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -163,7 +163,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -187,7 +187,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -219,7 +219,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -243,7 +243,7 @@ object DataRegistryModuleHelper { keyManager, serverRepo, ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -271,7 +271,7 @@ object DataRegistryModuleHelper { keyManager, ServerRepository(), ConsensusRepository(), - LinkedOrganizationsRepository() + LinkedOrganizationsRepository(application) ) } @@ -299,7 +299,7 @@ object DataRegistryModuleHelper { val reactionHandler = ReactionHandler(laoRepo, socialMediaRepo) val transactionCoinHandler = TransactionCoinHandler(digitalCashRepo) val witnessingHandler = WitnessingHandler(laoRepo, witnessingRepo) - val linkedOrganizationsHandler = LinkedOrganizationsHandler(laoRepo, linkedOrgRepo) + val linkedOrganizationsHandler = LinkedOrganizationsHandler(laoRepo, linkedOrgRepo, rollCallRepo) return DataRegistryModule.provideDataRegistry( laoHandler, rollCallHandler, diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentNonOrganizerTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentNonOrganizerTest.kt index 8bf4cd2b6d..3861adeb69 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentNonOrganizerTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentNonOrganizerTest.kt @@ -35,6 +35,9 @@ class LinkedOrganizationsFragmentNonOrganizerTest { @Inject lateinit var laoRepository: LAORepository + @BindValue @Mock + lateinit var linkedOrganizationsViewModel: LinkedOrganizationsViewModel + @BindValue @Mock lateinit var keyManager: KeyManager @@ -54,6 +57,7 @@ class LinkedOrganizationsFragmentNonOrganizerTest { laoRepository.updateLao(LAO) Mockito.`when`(keyManager.mainKeyPair).thenReturn(KEY_PAIR) Mockito.`when`(keyManager.mainPublicKey).thenReturn(POP_TOKEN.publicKey) + Mockito.`when`(linkedOrganizationsViewModel.getLinkedLaosMap()).thenReturn(mutableMapOf(LAO_ID to arrayOf())) } } @@ -75,6 +79,18 @@ class LinkedOrganizationsFragmentNonOrganizerTest { .check(matches(withEffectiveVisibility(Visibility.GONE))) } + @Test + fun testLAOTextDisplayed() { + LinkedOrganizationsFragmentPageObject.listOrganizationsText().check(matches(isDisplayed())) + LinkedOrganizationsFragmentPageObject.noOrganizationsText().check(matches(withEffectiveVisibility(Visibility.GONE))) + } + + @Test + fun testLAOTextIsCorrect() { + val validText = "List of linked organizations :\n\n$LAO_ID" + LinkedOrganizationsFragmentPageObject.listOrganizationsText().check(matches(withText(validText))) + } + companion object { private val KEY_PAIR = Base64DataUtils.generateKeyPair() private val POP_TOKEN = Base64DataUtils.generatePoPToken() diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt index 6a73fea5eb..5908e64d8f 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/federation/LinkedOrganizationsFragmentOrganizerTest.kt @@ -105,6 +105,12 @@ class LinkedOrganizationsFragmentOrganizerTest { LinkedOrganizationsFragmentPageObject.nextQrScannerFragment().check(matches(isDisplayed())) } + @Test + fun testNoLAOTextDisplayed() { + LinkedOrganizationsFragmentPageObject.noOrganizationsText().check(matches(isDisplayed())) + LinkedOrganizationsFragmentPageObject.listOrganizationsText().check(matches(withEffectiveVisibility(Visibility.GONE))) + } + companion object { private val KEY_PAIR = Base64DataUtils.generateKeyPair() private const val LAO_NAME = "LAO" diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapterTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapterTest.kt index 23bbdbc216..0b70b28371 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapterTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListAdapterTest.kt @@ -315,9 +315,10 @@ class ChirpListAdapterTest { private const val TEXT_2 = "text2" private const val TIMESTAMP_1: Long = 1632204910 private const val TIMESTAMP_2: Long = 1632204900 - private val CHIRP_1 = Chirp(MESSAGE_ID_1, SENDER_1, TEXT_1, TIMESTAMP_1, MessageID("")) + private val CHIRP_1 = + Chirp(MESSAGE_ID_1, SENDER_1, TEXT_1, TIMESTAMP_1, MessageID(""), LAO_ID) private val CHIRP_2 = - Chirp(MESSAGE_ID_2, SENDER_2, TEXT_2, TIMESTAMP_2, MessageID("")).deleted() + Chirp(MESSAGE_ID_2, SENDER_2, TEXT_2, TIMESTAMP_2, MessageID(""), LAO_ID).deleted() private val TIMESTAMP = Instant.now().epochSecond private fun createChirpList(): List { diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListFragmentTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListFragmentTest.kt index 8ae049821c..29420f234b 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListFragmentTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/ChirpListFragmentTest.kt @@ -338,9 +338,10 @@ class ChirpListFragmentTest { private const val TEXT_2 = "text2" private const val TIMESTAMP_1: Long = 1632204910 private const val TIMESTAMP_2: Long = 1632204900 - private val CHIRP_1 = Chirp(MESSAGE_ID_1, SENDER_1, TEXT_1, TIMESTAMP_1, MessageID("")) + private val CHIRP_1 = + Chirp(MESSAGE_ID_1, SENDER_1, TEXT_1, TIMESTAMP_1, MessageID(""), LAO_ID) private val CHIRP_2 = - Chirp(MESSAGE_ID_2, SENDER_2, TEXT_2, TIMESTAMP_2, MessageID("")).deleted() + Chirp(MESSAGE_ID_2, SENDER_2, TEXT_2, TIMESTAMP_2, MessageID(""), LAO_ID).deleted() private val TIMESTAMP = Instant.now().epochSecond private fun createChirpList(): List { diff --git a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaHomeFragmentTest.kt b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaHomeFragmentTest.kt index 3d4f78cfd0..8143361ed5 100644 --- a/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaHomeFragmentTest.kt +++ b/fe2-android/app/src/test/ui/robolectric/com/github/dedis/popstellar/ui/lao/socialmedia/SocialMediaHomeFragmentTest.kt @@ -196,7 +196,14 @@ class SocialMediaHomeFragmentTest { val socialMediaViewModel = LaoActivity.obtainSocialMediaViewModel(activity, LAO_ID) - val chirp = Chirp(Base64DataUtils.generateMessageID(), SENDER_1, "text", Instant.now().epochSecond, MessageID("")) + val chirp = Chirp( + Base64DataUtils.generateMessageID(), + SENDER_1, + "text", + Instant.now().epochSecond, + MessageID(""), + LAO_ID + ) socialMediaViewModel.sendChirp(chirp.text, chirp.parentId, chirp.timestamp) socialMediaViewModel.sendReaction(Reaction.ReactionEmoji.UPVOTE.code, chirp.id, Instant.now().epochSecond) diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt new file mode 100644 index 0000000000..bb728b12b0 --- /dev/null +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/FederationResultTest.kt @@ -0,0 +1,137 @@ +package com.github.dedis.popstellar.model.network.method.message.data.federation + +import com.github.dedis.popstellar.model.network.method.message.MessageGeneral +import com.github.dedis.popstellar.model.network.method.message.data.Action +import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.github.dedis.popstellar.testutils.Base64DataUtils +import com.google.gson.Gson +import org.junit.Assert +import org.junit.Assert.assertThrows +import org.junit.Test +import java.time.Instant + +class FederationResultTest { + + @Test + fun resultStatusTest() { + Assert.assertEquals(SUCCESS, RESULT_SUCCESS.status) + Assert.assertEquals(FAILURE, RESULT_FAILURE.status) + } + + @Test + fun resultReasonTest() { + Assert.assertNull(RESULT_SUCCESS.reason) + Assert.assertEquals(REASON, RESULT_FAILURE.reason) + } + + @Test + fun resultPublicKeyTest() { + Assert.assertEquals(PK.encoded, RESULT_SUCCESS.publicKey) + Assert.assertNull(RESULT_FAILURE.publicKey) + } + + @Test + fun resultChallengeTest() { + Assert.assertEquals(MG_CHALLENGE, RESULT_SUCCESS.challenge) + Assert.assertEquals(MG_CHALLENGE, RESULT_FAILURE.challenge) + } + + @Test + fun resultObjectTest() { + Assert.assertEquals(Objects.FEDERATION.`object`, RESULT_SUCCESS.`object`) + Assert.assertEquals(Objects.FEDERATION.`object`, RESULT_FAILURE.`object`) + } + + @Test + fun resultActionTest() { + Assert.assertEquals(Action.RESULT.action, RESULT_SUCCESS.action) + Assert.assertEquals(Action.RESULT.action, RESULT_FAILURE.action) + } + + @Test + fun resultEqualsTest() { + val result2 = FederationResult(SUCCESS, publicKey = PK.encoded, challenge = MG_CHALLENGE) + val result3 = FederationResult(FAILURE, reason = REASON, challenge = MG_CHALLENGE) + val result4 = FederationResult(FAILURE, reason = "reason2", challenge = MG_CHALLENGE) + Assert.assertEquals(RESULT_SUCCESS, result2) + Assert.assertEquals(RESULT_SUCCESS, RESULT_SUCCESS) + Assert.assertEquals(RESULT_SUCCESS.hashCode().toLong(), result2.hashCode().toLong()) + Assert.assertEquals(RESULT_FAILURE, result3) + Assert.assertEquals(RESULT_FAILURE, RESULT_FAILURE) + Assert.assertEquals(RESULT_FAILURE.hashCode().toLong(), result3.hashCode().toLong()) + + Assert.assertNotEquals(RESULT_SUCCESS, RESULT_FAILURE) + Assert.assertNotEquals(RESULT_SUCCESS, result3) + Assert.assertNotEquals(RESULT_SUCCESS, result4) + Assert.assertNotEquals(RESULT_FAILURE, result2) + Assert.assertNotEquals(RESULT_FAILURE, result4) + Assert.assertNotEquals(RESULT_SUCCESS, null) + Assert.assertNotEquals(RESULT_FAILURE, null) + } + + @Test + fun resultToStringTest() { + Assert.assertEquals( + "FederationResult{status='$SUCCESS', public_key='${PK.encoded}', challenge='$MG_CHALLENGE'}", + RESULT_SUCCESS.toString() + ) + Assert.assertEquals( + "FederationResult{status='$FAILURE', reason='$REASON', challenge='$MG_CHALLENGE'}", + RESULT_FAILURE.toString() + ) + } + + @Test + fun invalidMessageTypeTest() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult("invalid", challenge = MG_CHALLENGE) + } + assert(exception.message == "Status must be either 'failure' or 'success'.") + } + + @Test + fun invalidSuccessTest1() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult(SUCCESS, challenge = MG_CHALLENGE) + } + assert(exception.message == "Public key must be provided for success status.") + } + + @Test + fun invalidSuccessTest2() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult(SUCCESS, publicKey = PK.encoded, reason = "reason", challenge = MG_CHALLENGE) + } + assert(exception.message == "Reason must be null for success status.") + } + + @Test + fun invalidFailureTest1() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult(FAILURE, challenge = MG_CHALLENGE) + } + assert(exception.message == "Reason must be provided for failure status.") + } + + @Test + fun invalidFailureTest2() { + val exception = assertThrows(IllegalArgumentException::class.java) { + FederationResult(FAILURE, publicKey = PK.encoded, reason = "reason", challenge = MG_CHALLENGE) + } + assert(exception.message == "Public key must be null for failure status.") + } + + companion object { + private val KEY_PAIR = Base64DataUtils.generateKeyPair() + private val PK = KEY_PAIR.publicKey + private val TIMESTAMP = Instant.now().epochSecond + private const val CHALLENGE_VALUE = "1feb2a2c7c739ea25f2568d056cc82d11be65d361511872cd35e4abd1a20f3d4" + private val CHALLENGE = Challenge(CHALLENGE_VALUE, TIMESTAMP) + private val MG_CHALLENGE = MessageGeneral(Base64DataUtils.generateKeyPair(), CHALLENGE, Gson()) + private val SUCCESS = "success" + private val FAILURE = "failure" + private val REASON = "reason" + private val RESULT_SUCCESS = FederationResult(SUCCESS, publicKey = PK.encoded, challenge = MG_CHALLENGE) + private val RESULT_FAILURE = FederationResult(FAILURE, reason = REASON, challenge = MG_CHALLENGE) + } +} diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt new file mode 100644 index 0000000000..20bb0dacc2 --- /dev/null +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/network/method/message/data/federation/TokensExchangeTest.kt @@ -0,0 +1,95 @@ +package com.github.dedis.popstellar.model.network.method.message.data.federation + +import com.github.dedis.popstellar.model.network.method.message.data.Action +import com.github.dedis.popstellar.model.network.method.message.data.Objects +import com.github.dedis.popstellar.model.objects.Lao +import com.github.dedis.popstellar.testutils.Base64DataUtils +import org.junit.Assert +import org.junit.Test +import java.time.Instant + +class TokensExchangeTest { + + @Test + fun laoIDTest() { + Assert.assertEquals(LAO_ID, TK_EXCHANGE.laoId) + } + + @Test + fun rollCallIDTest() { + Assert.assertEquals(ROLL_CALL_ID, TK_EXCHANGE.rollCallId) + } + + @Test + fun tokensArrayTest() { + Assert.assertEquals(TOKENS, TK_EXCHANGE.tokens) + val emptyArray = arrayOf() + Assert.assertEquals(emptyArray, TokensExchange(LAO_ID, ROLL_CALL_ID, emptyArray, TIMESTAMP).tokens) + } + + @Test + fun timestampTest() { + Assert.assertEquals(TIMESTAMP, TK_EXCHANGE.timestamp) + } + + @Test + fun tokensExchangeObjectTest() { + Assert.assertEquals(Objects.FEDERATION.`object`, TK_EXCHANGE.`object`) + } + + @Test + fun tokensExchangeActionTest() { + Assert.assertEquals(Action.TOKENS_EXCHANGE.action, TK_EXCHANGE.action) + } + + @Test + fun equalsTest() { + val tokensExchange2 = TokensExchange(LAO_ID, ROLL_CALL_ID, TOKENS, TIMESTAMP) + Assert.assertEquals(TK_EXCHANGE, tokensExchange2) + Assert.assertEquals(TK_EXCHANGE, TK_EXCHANGE) + Assert.assertEquals(TK_EXCHANGE.hashCode().toLong(), tokensExchange2.hashCode().toLong()) + + val tokensExchange3 = TokensExchange(Lao.generateLaoId(ORGANIZER, CREATION, "LAO2"), ROLL_CALL_ID, TOKENS, TIMESTAMP) + val tokensExchange4 = TokensExchange(LAO_ID, "UkMy", TOKENS, TIMESTAMP) + val tokensExchange5 = TokensExchange(LAO_ID, ROLL_CALL_ID, arrayOf("dG9rZW4x"), TIMESTAMP) + Assert.assertNotEquals(TK_EXCHANGE, tokensExchange3) + Assert.assertNotEquals(TK_EXCHANGE, tokensExchange4) + Assert.assertNotEquals(TK_EXCHANGE, tokensExchange5) + Assert.assertNotEquals(TK_EXCHANGE, null) + } + + @Test + fun toStringTest() { + Assert.assertEquals( + "TokensExchange{lao_id='$LAO_ID', roll_call_id='$ROLL_CALL_ID', tokens='$TOKENS', timestamp='$TIMESTAMP'}", + TK_EXCHANGE.toString() + ) + } + + @Test + fun invalidTokensExchangeTest() { + Assert.assertThrows(IllegalArgumentException::class.java) { + TokensExchange("LAOID", ROLL_CALL_ID, TOKENS, TIMESTAMP) + } + Assert.assertThrows(IllegalArgumentException::class.java) { + TokensExchange(LAO_ID, "RollCallId", TOKENS, TIMESTAMP) + } + Assert.assertThrows(IllegalArgumentException::class.java) { + TokensExchange(LAO_ID, ROLL_CALL_ID, arrayOf("dG9rZW4x", "token2"), TIMESTAMP) + } + Assert.assertThrows(IllegalArgumentException::class.java) { + TokensExchange(LAO_ID, ROLL_CALL_ID, TOKENS, TIMESTAMP + 200) + } + } + + companion object { + private val ORGANIZER = Base64DataUtils.generatePublicKey() + private val CREATION = Instant.now().epochSecond + private const val NAME = "Lao name" + private val LAO_ID = Lao.generateLaoId(ORGANIZER, CREATION, NAME) + private val ROLL_CALL_ID = "UkMx" + private val TOKENS = arrayOf("dG9rZW4x", "dG9rZW4y", "dG9rZW4z") + private val TIMESTAMP = Instant.now().epochSecond + private val TK_EXCHANGE = TokensExchange(LAO_ID, ROLL_CALL_ID, TOKENS, TIMESTAMP) + } +} diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/objects/ChirpTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/objects/ChirpTest.kt index f7d1610381..1eb4c240c3 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/objects/ChirpTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/model/objects/ChirpTest.kt @@ -10,14 +10,14 @@ class ChirpTest { fun createChirpWithEmptyIdFails() { Assert.assertThrows( IllegalArgumentException::class.java - ) { Chirp(EMPTY_MESSAGE_ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID) } + ) { Chirp(EMPTY_MESSAGE_ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID, LAO_ID) } } @Test fun createChirpWithNegativeTimestampFails() { Assert.assertThrows( IllegalArgumentException::class.java - ) { Chirp(ID, SENDER, TEXT, -5, EMPTY_MESSAGE_ID) } + ) { Chirp(ID, SENDER, TEXT, -5, EMPTY_MESSAGE_ID, LAO_ID) } } @Test @@ -30,12 +30,12 @@ class ChirpTest { + " the threshold.") Assert.assertThrows( IllegalArgumentException::class.java - ) { Chirp(ID, SENDER, textTooLong, TIMESTAMP, EMPTY_MESSAGE_ID) } + ) { Chirp(ID, SENDER, textTooLong, TIMESTAMP, EMPTY_MESSAGE_ID, LAO_ID) } } @Test fun deletedChirpProducesASimilarChirpWithEmptyTextAndDeletedProperty() { - val chirp = Chirp(ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID) + val chirp = Chirp(ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID, LAO_ID) val deleted = chirp.deleted() Assert.assertEquals(chirp.id, deleted.id) Assert.assertEquals(chirp.sender, deleted.sender) @@ -43,6 +43,13 @@ class ChirpTest { Assert.assertEquals(chirp.timestamp, deleted.timestamp) Assert.assertEquals(chirp.parentId, deleted.parentId) Assert.assertTrue(deleted.isDeleted) + Assert.assertEquals(chirp.laoId, deleted.laoId) + } + + @Test + fun laoIdIsStoredCorrectlyInChirp() { + val chirp = Chirp(ID, SENDER, TEXT, TIMESTAMP, EMPTY_MESSAGE_ID, LAO_ID) + Assert.assertEquals(LAO_ID, chirp.laoId) } companion object { @@ -52,5 +59,6 @@ class ChirpTest { private const val TEXT = "This is a Chirp !" private const val TIMESTAMP: Long = 10000 private val EMPTY_MESSAGE_ID = MessageID("") + private val LAO_ID = Lao.generateLaoId(Base64DataUtils.generatePublicKey(), 1000, "LAO") } } \ No newline at end of file diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt index ce2460710e..b4469d60ff 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/LinkedOrganizationsRepositoryTest.kt @@ -1,16 +1,23 @@ package com.github.dedis.popstellar.repository +import android.app.Application +import com.github.dedis.popstellar.model.network.method.message.MessageGeneral import com.github.dedis.popstellar.model.network.method.message.data.federation.Challenge +import com.github.dedis.popstellar.model.network.method.message.data.federation.FederationResult +import com.github.dedis.popstellar.model.network.method.message.data.federation.TokensExchange +import com.github.dedis.popstellar.model.objects.Channel import com.github.dedis.popstellar.model.objects.Lao import com.github.dedis.popstellar.testutils.Base64DataUtils import com.github.dedis.popstellar.utility.handler.data.HandlerContext import com.github.dedis.popstellar.utility.handler.data.LinkedOrganizationsHandler +import com.google.gson.Gson import org.junit.Assert import org.junit.Test import org.mockito.Mockito import java.time.Instant class LinkedOrganizationsRepositoryTest { + @Test fun repoFlushTest() { REPO.flush() @@ -60,24 +67,123 @@ class LinkedOrganizationsRepositoryTest { } @Test - fun handlerAndRepoTest() { + fun handleChallengeAndRepoTest() { val mockLaoRepo = Mockito.mock(LAORepository::class.java) val mockContext = Mockito.mock(HandlerContext::class.java) + val mockRollCallRepo = Mockito.mock(RollCallRepository::class.java) REPO.flush() - val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO) + val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) handler.handleChallenge(mockContext, CHALLENGE) Assert.assertEquals(CHALLENGE, REPO.getChallenge()) } + @Test + fun repoLinkedLaoTest() { + Assert.assertEquals(mutableMapOf>(), REPO.getLinkedLaos(LAO_ID)) + + REPO.addLinkedLao(LAO_ID, LAO_ID_2, TOKENS_ARRAY_1) + REPO.addLinkedLao(LAO_ID_3, LAO_ID_2, TOKENS_ARRAY_2) + REPO.updateAndNotifyLinkedLao(LAO_ID_4, LAO_ID_3, TOKENS_ARRAY_2, "rollcall") + REPO.addLinkedLao(LAO_ID, LAO_ID_3, TOKENS_ARRAY_2) + + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID).keys.contains(LAO_ID_2)) + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID).keys.contains(LAO_ID_3)) + Assert.assertFalse(REPO.getLinkedLaos(LAO_ID).keys.contains(LAO_ID_4)) + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID_3).keys.contains(LAO_ID_2)) + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID_4).keys.contains(LAO_ID_3)) + Assert.assertFalse(REPO.getLinkedLaos(LAO_ID_4).keys.contains(LAO_ID)) + Assert.assertFalse(REPO.getLinkedLaos(LAO_ID_4).keys.contains(LAO_ID_2)) + Assert.assertFalse(REPO.getLinkedLaos(LAO_ID_4).keys.contains(LAO_ID_4)) + + Assert.assertEquals(TOKENS_ARRAY_1, REPO.getLinkedLaos(LAO_ID)[LAO_ID_2]) + Assert.assertEquals(TOKENS_ARRAY_2, REPO.getLinkedLaos(LAO_ID)[LAO_ID_3]) + Assert.assertEquals(TOKENS_ARRAY_2, REPO.getLinkedLaos(LAO_ID_4)[LAO_ID_3]) + } + + @Test + fun handleResultAndRepoTest() { + val mockLaoRepo = Mockito.mock(LAORepository::class.java) + val mockContext = Mockito.mock(HandlerContext::class.java) + val mockChannel = Mockito.mock(Channel::class.java) + Mockito.`when`(mockContext.channel).thenReturn(mockChannel) + Mockito.`when`(mockChannel.extractLaoId()).thenReturn(LAO_ID_5) + val mockRollCallRepo = Mockito.mock(RollCallRepository::class.java) + REPO.flush() + REPO.otherLaoId = LAO_ID_4 + REPO.updateChallenge(CHALLENGE) + val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) + Assert.assertThrows(NullPointerException::class.java) { + handler.handleResult(mockContext, RESULT_SUCCESS) + } + Assert.assertEquals(mutableSetOf(LAO_ID_4), REPO.getLinkedLaos(LAO_ID_5).keys) + Assert.assertTrue(REPO.getLinkedLaos(LAO_ID_5)[LAO_ID_4]!!.isEmpty()) + } + + @Test + fun handleResultAndRepoTest2() { + val mockLaoRepo = Mockito.mock(LAORepository::class.java) + val mockContext = Mockito.mock(HandlerContext::class.java) + val mockChannel = Mockito.mock(Channel::class.java) + Mockito.`when`(mockContext.channel).thenReturn(mockChannel) + Mockito.`when`(mockChannel.extractLaoId()).thenReturn(LAO_ID_7) + val mockRollCallRepo = Mockito.mock(RollCallRepository::class.java) + REPO.flush() + REPO.otherLaoId = LAO_ID_4 + REPO.updateChallenge(CHALLENGE) + val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) + handler.handleResult(mockContext, RESULT_FAILURE) + Assert.assertEquals(mutableSetOf(), REPO.getLinkedLaos(LAO_ID_7).keys) + + REPO.otherLaoId = null + handler.handleResult(mockContext, RESULT_SUCCESS) + Assert.assertEquals(mutableSetOf(), REPO.getLinkedLaos(LAO_ID_7).keys) + REPO.flush() + REPO.otherLaoId = LAO_ID_4 + handler.handleResult(mockContext, RESULT_SUCCESS) + Assert.assertEquals(mutableSetOf(), REPO.getLinkedLaos(LAO_ID_7).keys) + } + + @Test + fun handleTokensExchangeAndRepoTest() { + val mockLaoRepo = Mockito.mock(LAORepository::class.java) + val mockContext = Mockito.mock(HandlerContext::class.java) + val mockChannel = Mockito.mock(Channel::class.java) + Mockito.`when`(mockContext.channel).thenReturn(mockChannel) + Mockito.`when`(mockChannel.extractLaoId()).thenReturn(LAO_ID_6) + val mockRollCallRepo = Mockito.mock(RollCallRepository::class.java) + REPO.flush() + REPO.otherLaoId = LAO_ID_4 + REPO.updateChallenge(CHALLENGE) + val handler = LinkedOrganizationsHandler(mockLaoRepo, REPO, mockRollCallRepo) + Assert.assertThrows(NullPointerException::class.java) { + handler.handleTokensExchange(mockContext, TOKENS_EXCHANGE) + } + Assert.assertEquals(mutableSetOf(LAO_ID_3), REPO.getLinkedLaos(LAO_ID_6).keys) + Assert.assertEquals(TOKENS_ARRAY_1, REPO.getLinkedLaos(LAO_ID_6)[LAO_ID_3]) + } + companion object { private val ORGANIZER = Base64DataUtils.generatePublicKey() private val CREATION = Instant.now().epochSecond private const val NAME = "Lao name" private val LAO_ID = Lao.generateLaoId(ORGANIZER, CREATION, NAME) + private val LAO_ID_2 = "aWQy" + private val LAO_ID_3 = "aWQz" + private val LAO_ID_4 = "aWQ0" + private val LAO_ID_5 = "aWQ1" + private val LAO_ID_6 = "aWQ2" + private val LAO_ID_7 = "aWQ3" private val TIMESTAMP = Instant.now().epochSecond private const val SERVER_ADDRESS = "wss://1.1.1.1:9000/client" private const val CHALLENGE_VALUE = "1feb2a2c7c739ea25f2568d056cc82d11be65d361511872cd35e4abd1a20f3d4" + private val TOKENS_ARRAY_1 = arrayOf("dG9rZW4x", "dG9rZW4y") + private val TOKENS_ARRAY_2 = arrayOf("dG9rZW43", "dG9rZW44", "dG9rZW45") private val CHALLENGE = Challenge(CHALLENGE_VALUE, TIMESTAMP) - private val REPO = LinkedOrganizationsRepository() + private val MG_CHALLENGE = MessageGeneral(Base64DataUtils.generateKeyPair(), CHALLENGE, Gson()) + private val RESULT_SUCCESS = FederationResult("success", publicKey = "UEs=", challenge = MG_CHALLENGE) + private val RESULT_FAILURE = FederationResult("failure", reason = "fail", challenge = MG_CHALLENGE) + private val TOKENS_EXCHANGE = TokensExchange(LAO_ID_3, "rollCall", TOKENS_ARRAY_1, TIMESTAMP) + private val application = Application() + private val REPO = LinkedOrganizationsRepository(application) } -} \ No newline at end of file +} diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/SocialMediaRepositoryTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/SocialMediaRepositoryTest.kt index 10fa527935..ebd3646768 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/SocialMediaRepositoryTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/SocialMediaRepositoryTest.kt @@ -111,7 +111,8 @@ class SocialMediaRepositoryTest { Base64DataUtils.generatePublicKey(), "This is another chirp !", 1003, - MessageID("") + MessageID(""), + LAO_ID ) repo.addChirp(LAO_ID, invalidChirp) @@ -214,8 +215,10 @@ class SocialMediaRepositoryTest { private val CHIRP1_ID = Base64DataUtils.generateMessageID() private val CHIRP2_ID = Base64DataUtils.generateMessageID() private const val EMOJI = "\uD83D\uDC4D" - private val CHIRP_1 = Chirp(CHIRP1_ID, SENDER, "This is a chirp !", 1001, MessageID("")) - private val CHIRP_2 = Chirp(CHIRP2_ID, SENDER, "This is another chirp !", 1003, MessageID("")) + private val CHIRP_1 = + Chirp(CHIRP1_ID, SENDER, "This is a chirp !", 1001, MessageID(""), LAO_ID) + private val CHIRP_2 = + Chirp(CHIRP2_ID, SENDER, "This is another chirp !", 1003, MessageID(""), LAO_ID) private val REACTION_1 = Reaction( Base64DataUtils.generateMessageID(), diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/database/SocialMediaDatabaseTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/database/SocialMediaDatabaseTest.kt index db1a92ad8a..92b854d7f2 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/database/SocialMediaDatabaseTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/repository/database/SocialMediaDatabaseTest.kt @@ -84,8 +84,10 @@ class SocialMediaDatabaseTest { private val CHIRP1_ID = Base64DataUtils.generateMessageID() private val CHIRP2_ID = Base64DataUtils.generateMessageID() private const val EMOJI = "\uD83D\uDC4D" - private val CHIRP_1 = Chirp(CHIRP1_ID, SENDER, "This is a chirp !", 1001, MessageID("")) - private val CHIRP_2 = Chirp(CHIRP2_ID, SENDER, "This is another chirp !", 1003, MessageID("")) + private val CHIRP_1 = + Chirp(CHIRP1_ID, SENDER, "This is a chirp !", 1001, MessageID(""), LAO_ID) + private val CHIRP_2 = + Chirp(CHIRP2_ID, SENDER, "This is another chirp !", 1003, MessageID(""), LAO_ID) private val REACTION_1 = Reaction( Base64DataUtils.generateMessageID(), diff --git a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/utility/handler/ChirpHandlerTest.kt b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/utility/handler/ChirpHandlerTest.kt index 03a8fc68d9..f8f281715b 100644 --- a/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/utility/handler/ChirpHandlerTest.kt +++ b/fe2-android/app/src/test/unit/java/com/github/dedis/popstellar/utility/handler/ChirpHandlerTest.kt @@ -122,7 +122,7 @@ class ChirpHandlerTest { private const val TEXT = "textOfTheChirp" private val PARENT_ID = Base64DataUtils.generateMessageID() private val CHIRP_ID = Base64DataUtils.generateMessageID() - private val CHIRP = Chirp(CHIRP_ID, SENDER, TEXT, CREATION_TIME, PARENT_ID) + private val CHIRP = Chirp(CHIRP_ID, SENDER, TEXT, CREATION_TIME, PARENT_ID, LAO_ID) private val ADD_CHIRP = AddChirp(TEXT, PARENT_ID, CREATION_TIME) private val DELETE_CHIRP = DeleteChirp(CHIRP_ID, DELETION_TIME) }