diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce9f907f..17e22584 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,13 @@ jobs: # uses: japgolly/setup-scalajs@v1 - name: Cache sbt uses: coursier/cache-action@v6.3 + - name: Cache embedmongo + uses: actions/cache@v2 + with: + path: ~/.embedmongo + key: ${{ runner.os }}-embedmongo-4.7.0 + restore-keys: | + ${{ runner.os }}-embedmongo-4.7.0 ### Compile and TESTS ### - run: sbt -mem 2048 -J-Xmx5120m "test" # env: diff --git a/build.sbt b/build.sbt index 1fde9e7d..9f893856 100644 --- a/build.sbt +++ b/build.sbt @@ -27,6 +27,12 @@ lazy val V = new { val zioConfig = "4.0.0-RC16" val zioSl4j = "2.1.13" val mongo = "1.1.0-RC10" + val embedMongo = "4.7.0" + val munitZio = "0.1.1" + val zioTest = "2.0.5" + val zioTestSbt = "2.0.5" + val zioTestMagnolia = "2.0.5" + } /** Dependencies */ @@ -57,6 +63,12 @@ lazy val D = new { val mongo = Def.setting("org.reactivemongo" %% "reactivemongo" % V.mongo) // // For munit https://scalameta.org/munit/docs/getting-started.html#scalajs-setup val munit = Def.setting("org.scalameta" %%% "munit" % V.munit % Test) + val embedMongo = Def.setting("de.flapdoodle.embed" % "de.flapdoodle.embed.mongo" % V.embedMongo % Test) + // For munit zio https://github.com/poslegm/munit-zio + val munitZio = Def.setting("com.github.poslegm" %% "munit-zio" % V.munitZio % Test) + val zioTest = Def.setting("dev.zio" %% "zio-test" % V.zioTest % Test) + val zioTestSbt = Def.setting("dev.zio" %% "zio-test-sbt" % V.zioTestSbt % Test) + val zioTestMagnolia = Def.setting("dev.zio" %% "zio-test-magnolia" % V.zioTestMagnolia % Test) } inThisBuild( @@ -163,6 +175,14 @@ lazy val mediator = project D.zioLoggingSl4j.value ), libraryDependencies += D.mongo.value, + libraryDependencies ++= Seq( + D.munit.value, + D.embedMongo.value, + D.zioTest.value, + D.zioTestSbt.value, + D.zioTestMagnolia.value, + ), + testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework") ) .settings( Compile / mainClass := Some("io.iohk.atala.mediator.app.MediatorStandalone"), @@ -172,6 +192,7 @@ lazy val mediator = project dockerExposedPorts := Seq(8080), dockerBaseImage := "openjdk:11", ) + .settings(Test / parallelExecution := false) .dependsOn(httpUtils.jvm) // did, didExample, .enablePlugins(JavaAppPackaging, DockerPlugin) diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/app/MediatorAgent.scala b/mediator/src/main/scala/io/iohk/atala/mediator/app/MediatorAgent.scala index 0f0f53a4..76c4e810 100644 --- a/mediator/src/main/scala/io/iohk/atala/mediator/app/MediatorAgent.scala +++ b/mediator/src/main/scala/io/iohk/atala/mediator/app/MediatorAgent.scala @@ -32,8 +32,8 @@ case class MediatorAgent( // val resolverLayer: ULayer[DynamicResolver] = // DynamicResolver.resolverLayer(didSocketManager) - type Services = Resolver & Agent & Operations & MessageDispatcher & DidAccountRepo & MessageItemRepo - val protocolHandlerLayer: URLayer[DidAccountRepo & MessageItemRepo, ProtocolExecuter[Services]] = + type Services = Resolver & Agent & Operations & MessageDispatcher & UserAccountRepo & MessageItemRepo + val protocolHandlerLayer: URLayer[UserAccountRepo & MessageItemRepo, ProtocolExecuter[Services]] = ZLayer.succeed( ProtocolExecuterCollection[Services]( BasicMessageExecuter, @@ -89,7 +89,7 @@ case class MediatorAgent( data: String, mSocketID: Option[SocketID], ): ZIO[ - Operations & Resolver & MessageDispatcher & MediatorAgent & MessageItemRepo & DidAccountRepo, + Operations & Resolver & MessageDispatcher & MediatorAgent & MessageItemRepo & UserAccountRepo, MediatorError, Option[EncryptedMessage] ] = @@ -110,7 +110,7 @@ case class MediatorAgent( msg: EncryptedMessage, mSocketID: Option[SocketID] ): ZIO[ - Operations & Resolver & MessageDispatcher & MediatorAgent & MessageItemRepo & DidAccountRepo, + Operations & Resolver & MessageDispatcher & MediatorAgent & MessageItemRepo & UserAccountRepo, MediatorError, Option[EncryptedMessage] ] = @@ -150,7 +150,7 @@ case class MediatorAgent( def createSocketApp( annotationMap: Seq[LogAnnotation] ): ZIO[ - MediatorAgent & Resolver & Operations & MessageDispatcher & MessageItemRepo & DidAccountRepo, + MediatorAgent & Resolver & Operations & MessageDispatcher & MessageItemRepo & UserAccountRepo, Nothing, zio.http.Response ] = { @@ -255,7 +255,7 @@ object MediatorAgent { .copy(status = Status.BadRequest) ) }: Http[ - Operations & Resolver & MessageDispatcher & MediatorAgent & MessageItemRepo & DidAccountRepo, + Operations & Resolver & MessageDispatcher & MediatorAgent & MessageItemRepo & UserAccountRepo, Throwable, Request, Response diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/app/MediatorStandalone.scala b/mediator/src/main/scala/io/iohk/atala/mediator/app/MediatorStandalone.scala index 00488a1d..86fbb348 100644 --- a/mediator/src/main/scala/io/iohk/atala/mediator/app/MediatorStandalone.scala +++ b/mediator/src/main/scala/io/iohk/atala/mediator/app/MediatorStandalone.scala @@ -47,7 +47,7 @@ case class DataBaseConfig( object MediatorStandalone extends ZIOAppDefault { val app: HttpApp[ // type HttpApp[-R, +Err] = Http[R, Err, Request, Response] - Hub[String] & Operations & MessageDispatcher & MediatorAgent & Resolver & MessageItemRepo & DidAccountRepo, + Hub[String] & Operations & MessageDispatcher & MediatorAgent & Resolver & MessageItemRepo & UserAccountRepo, Throwable ] = MediatorAgent.didCommApp ++ Http @@ -104,7 +104,7 @@ object MediatorStandalone extends ZIOAppDefault { .provideSomeLayer( AsyncDriverResource.layer >>> ReactiveMongoApi.layer(mediatorDbConfig.connectionString) - >>> MessageItemRepo.layer.and(DidAccountRepo.layer) + >>> MessageItemRepo.layer.and(UserAccountRepo.layer) ) .provideSomeLayer(Operations.layerDefault) .provideSomeLayer(client >>> MessageDispatcherJVM.layer) diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/db/DidAccountRepo.scala b/mediator/src/main/scala/io/iohk/atala/mediator/db/UserAccountRepo.scala similarity index 87% rename from mediator/src/main/scala/io/iohk/atala/mediator/db/DidAccountRepo.scala rename to mediator/src/main/scala/io/iohk/atala/mediator/db/UserAccountRepo.scala index 1e40ca27..5e4cb9ae 100644 --- a/mediator/src/main/scala/io/iohk/atala/mediator/db/DidAccountRepo.scala +++ b/mediator/src/main/scala/io/iohk/atala/mediator/db/UserAccountRepo.scala @@ -12,23 +12,23 @@ import zio.* import scala.concurrent.ExecutionContext -object DidAccountRepo { - def layer: ZLayer[ReactiveMongoApi, Throwable, DidAccountRepo] = +object UserAccountRepo { + def layer: ZLayer[ReactiveMongoApi, Throwable, UserAccountRepo] = ZLayer { for { ref <- ZIO.service[ReactiveMongoApi] - } yield DidAccountRepo(ref)(using scala.concurrent.ExecutionContext.global) + } yield UserAccountRepo(ref)(using scala.concurrent.ExecutionContext.global) } } -class DidAccountRepo(reactiveMongoApi: ReactiveMongoApi)(using ec: ExecutionContext) { +class UserAccountRepo(reactiveMongoApi: ReactiveMongoApi)(using ec: ExecutionContext) { def collectionName: String = "user.account" def collection: IO[StorageCollection, BSONCollection] = reactiveMongoApi.database .map(_.collection(collectionName)) .mapError(ex => StorageCollection(ex)) - def newDidAccount(did: DIDSubject /*, alias: Seq[DID] = Seq.empty*/ ): IO[StorageError, WriteResult] = { + def newDidAccount(did: DIDSubject): IO[StorageError, WriteResult] = { val value = DidAccount( did = did, alias = Seq(did), @@ -96,7 +96,7 @@ class DidAccountRepo(reactiveMongoApi: ReactiveMongoApi)(using ec: ExecutionCont } /** @return - * numbre of documents updated in DB + * number of documents updated in DB */ def addToInboxes(recipients: Set[DIDSubject], msg: EncryptedMessage): ZIO[Any, StorageError, Int] = { def selector = @@ -135,8 +135,8 @@ class DidAccountRepo(reactiveMongoApi: ReactiveMongoApi)(using ec: ExecutionCont } yield result.nModified } - def makeAsDelivered(didAccount: DIDSubject, hashs: Seq[HASH]): ZIO[Any, StorageError, Int] = { - def selector = BSONDocument("did" -> didAccount.did, "messagesRef.hash" -> BSONDocument("$in" -> hashs)) + def markAsDelivered(didAccount: DIDSubject, hashes: Seq[HASH]): ZIO[Any, StorageError, Int] = { + def selector = BSONDocument("did" -> didAccount.did, "messagesRef.hash" -> BSONDocument("$in" -> hashes)) def update: BSONDocument = BSONDocument("$set" -> BSONDocument("messagesRef.$.state" -> true)) for { diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/protocols/ForwardMessageExecuter.scala b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/ForwardMessageExecuter.scala index b6e51e31..e692c157 100644 --- a/mediator/src/main/scala/io/iohk/atala/mediator/protocols/ForwardMessageExecuter.scala +++ b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/ForwardMessageExecuter.scala @@ -13,12 +13,12 @@ import zio.json.* object ForwardMessageExecuter extends ProtocolExecuterWithServices[ - ProtocolExecuter.Services & DidAccountRepo & MessageItemRepo + ProtocolExecuter.Services & UserAccountRepo & MessageItemRepo ] { override def suportedPIURI: Seq[PIURI] = Seq(ForwardMessage.piuri) - override def program[R1 <: DidAccountRepo & MessageItemRepo]( + override def program[R1 <: UserAccountRepo & MessageItemRepo]( plaintextMessage: PlaintextMessage ): ZIO[R1, MediatorError, Action] = { // the val is from the match to be definitely stable @@ -30,7 +30,7 @@ object ForwardMessageExecuter for { _ <- ZIO.logInfo("ForwardMessage") repoMessageItem <- ZIO.service[MessageItemRepo] - repoDidAccount <- ZIO.service[DidAccountRepo] + repoDidAccount <- ZIO.service[UserAccountRepo] recipientsSubject = Set(m.next) // m.msg.recipientsSubject numbreOfUpdated <- repoDidAccount.addToInboxes(recipientsSubject, m.msg) msg <- diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/protocols/MediatorCoordinationExecuter.scala b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/MediatorCoordinationExecuter.scala index 774943d9..c107e1c6 100644 --- a/mediator/src/main/scala/io/iohk/atala/mediator/protocols/MediatorCoordinationExecuter.scala +++ b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/MediatorCoordinationExecuter.scala @@ -8,10 +8,10 @@ import fmgp.did.comm.protocol.* import fmgp.did.comm.protocol.mediatorcoordination2.* import io.iohk.atala.mediator.* import io.iohk.atala.mediator.actions.* -import io.iohk.atala.mediator.db.DidAccountRepo +import io.iohk.atala.mediator.db.UserAccountRepo import zio.* import zio.json.* -object MediatorCoordinationExecuter extends ProtocolExecuterWithServices[ProtocolExecuter.Services & DidAccountRepo] { +object MediatorCoordinationExecuter extends ProtocolExecuterWithServices[ProtocolExecuter.Services & UserAccountRepo] { override def suportedPIURI: Seq[PIURI] = Seq( MediateRequest.piuri, @@ -23,7 +23,7 @@ object MediatorCoordinationExecuter extends ProtocolExecuterWithServices[Protoco Keylist.piuri, ) - override def program[R1 <: (DidAccountRepo)]( + override def program[R1 <: (UserAccountRepo)]( plaintextMessage: PlaintextMessage ): ZIO[R1, MediatorError, Action] = { // the val is from the match to be definitely stable @@ -49,7 +49,7 @@ object MediatorCoordinationExecuter extends ProtocolExecuterWithServices[Protoco case m: MediateRequest => for { _ <- ZIO.logInfo("MediateRequest") - repo <- ZIO.service[DidAccountRepo] + repo <- ZIO.service[UserAccountRepo] result <- repo.newDidAccount(m.from.asDIDURL.toDID) reply = result.n match case 1 => m.makeRespondMediateGrant.toPlaintextMessage @@ -58,7 +58,7 @@ object MediatorCoordinationExecuter extends ProtocolExecuterWithServices[Protoco case m: KeylistUpdate => for { _ <- ZIO.logInfo("KeylistUpdate") - repo <- ZIO.service[DidAccountRepo] + repo <- ZIO.service[UserAccountRepo] updateResponse <- ZIO.foreach(m.updates) { case (fromto, KeylistAction.add) => repo.addAlias(m.from.toDID, fromto.toDID).map { diff --git a/mediator/src/main/scala/io/iohk/atala/mediator/protocols/PickupExecuter.scala b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/PickupExecuter.scala index 6037f4a8..5f5ac84b 100644 --- a/mediator/src/main/scala/io/iohk/atala/mediator/protocols/PickupExecuter.scala +++ b/mediator/src/main/scala/io/iohk/atala/mediator/protocols/PickupExecuter.scala @@ -12,7 +12,7 @@ import io.iohk.atala.mediator.db.* import zio.* import zio.json.* object PickupExecuter - extends ProtocolExecuterWithServices[ProtocolExecuter.Services & DidAccountRepo & MessageItemRepo] { + extends ProtocolExecuterWithServices[ProtocolExecuter.Services & UserAccountRepo & MessageItemRepo] { override def suportedPIURI: Seq[PIURI] = Seq( StatusRequest.piuri, @@ -23,7 +23,7 @@ object PickupExecuter LiveModeChange.piuri, ) - override def program[R1 <: DidAccountRepo & MessageItemRepo]( + override def program[R1 <: UserAccountRepo & MessageItemRepo]( plaintextMessage: PlaintextMessage ): ZIO[R1, MediatorError, Action] = { // the val is from the match to be definitely stable @@ -48,7 +48,7 @@ object PickupExecuter for { _ <- ZIO.logInfo("DeliveryRequest") repoMessageItem <- ZIO.service[MessageItemRepo] - repoDidAccount <- ZIO.service[DidAccountRepo] + repoDidAccount <- ZIO.service[UserAccountRepo] didRequestingMessages = m.from.asFROMTO mDidAccount <- repoDidAccount.getDidAccount(didRequestingMessages.toDID) msgHash = mDidAccount match @@ -87,9 +87,9 @@ object PickupExecuter case m: MessagesReceived => for { _ <- ZIO.logInfo("MessagesReceived") - repoDidAccount <- ZIO.service[DidAccountRepo] + repoDidAccount <- ZIO.service[UserAccountRepo] didRequestingMessages = m.from.asFROMTO - mDidAccount <- repoDidAccount.makeAsDelivered( + mDidAccount <- repoDidAccount.markAsDelivered( didRequestingMessages.toDID, m.message_id_list.map(e => e.toInt) // TODO have it safe 'toInt' ) diff --git a/mediator/src/test/scala/io/iohk/atala/mediator/db/AccountStubRepo.scala b/mediator/src/test/scala/io/iohk/atala/mediator/db/AccountStubRepo.scala new file mode 100644 index 00000000..1f20bbab --- /dev/null +++ b/mediator/src/test/scala/io/iohk/atala/mediator/db/AccountStubRepo.scala @@ -0,0 +1,34 @@ +package io.iohk.atala.mediator.db +import fmgp.did.comm.EncryptedMessage +import zio.json.* +trait AccountStubSetup { + val alice = + "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9hbGljZS5kaWQuZm1ncC5hcHAvIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfQ" + val bob = + "did:peer:2.Ez6LSkGy3e2z54uP4U9HyXJXRpaF2ytsnTuVgh6SNNmCyGZQZ.Vz6Mkjdwvf9hWc6ibZndW9B97si92DSk9hWAhGYBgP9kUFk8Z.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9ib2IuZGlkLmZtZ3AuYXBwLyIsInIiOltdLCJhIjpbImRpZGNvbW0vdjIiXX0" + + val encryptedMessageAlice: Either[String, EncryptedMessage] = """{ + | "ciphertext": "L8wzawffzL7kbVyDcMiHgQMwlqLUqWDYOwH4f0VOFK7sJL8vO-H_JBDCADu7gvH1MDHdVJDXQmSv5dDXp6JYubagcSfBqyZGKGOQDQz-1Ir6-sqZ9K5xRPMt76dYbSswrxhaROVJvtAXyrTmN3KEv8SgP_vt8za5QU6B8cM6gp2CycJKoIhETnhVEVjHnrX0YcyzBsLhd-ZKb8e1Nlz_XSh-3cw1oIQMFjXHnU3PSjrqXcrC-4qSGpVVpHvMSOF3o2EbLGEpP-8UbI0psgSLd9a2VUF9xV55r7Bvn_zJp6tC3-KZKNLO0Rc2K-6JRgyyZjo7O_8aj7Ns6Lx0OjUXoKLiA6B79goHiv8qxJ04k5EWD_h8gt4M3_oByH_cVXH35RDK5SwNV6cVl6zRqiHUR9Ilivh6kfOLGqM2sXCKpXE78qRgHOBeOtl08JHFQO5oFkt1ob8iDqczX0nu-qwlckiibnPK1VvhFnmgLyc9lIUsi_xlCNKqIBZCKoi03xrjNobcM1dWFG7yE04nT-sSiakRNwVHBmNCyA5JhEFQ92d1xpXFGM1ojtiHCPkN5nqe7lMYVM2r7QFnN1xTHwaDWddKprW3vkz_RP7tpoPlWk6X8rLoYUvYc3MqNdbj91QMlho5rU472EX3gprIDeNV7VQiKWoAksFe1hdt62zLH8mJJjUZ3lyq4YjOmrqg7g6RArUWC6KbPgmnuJCqwigpjlwRUCBTPISzaZETAisyluIyuMW8QlRCSQdWnfPZAU2fpLBcviMODMzZTULECvRBef05Fvtd_xRCCbpKpDkGxAY", + | "protected": "eyJlcGsiOnsia3R5IjoiT0tQIiwiY3J2IjoiWDI1NTE5IiwieCI6Ikp2TXhvYXZJaUxaUWVrNDA2eXNtWThQOGpkZGFHSjVIaVNRR0ltWHZWQ2MifSwiYXB2IjoiLWNOQ3l0eFVrSHpSRE5SckV2Vm05S0VmZzhZcUtQVnVVcVg1a0VLbU9yMCIsInNraWQiOiJkaWQ6cGVlcjoyLkV6NkxTa0d5M2UyejU0dVA0VTlIeVhKWFJwYUYyeXRzblR1VmdoNlNOTm1DeUdaUVouVno2TWtqZHd2ZjloV2M2aWJabmRXOUI5N3NpOTJEU2s5aFdBaEdZQmdQOWtVRms4Wi5TZXlKMElqb2laRzBpTENKeklqb2lhSFIwY0hNNkx5OWliMkl1Wkdsa0xtWnRaM0F1WVhCd0x5SXNJbklpT2x0ZExDSmhJanBiSW1ScFpHTnZiVzB2ZGpJaVhYMCM2TFNrR3kzZTJ6NTR1UDRVOUh5WEpYUnBhRjJ5dHNuVHVWZ2g2U05ObUN5R1pRWiIsImFwdSI6IlpHbGtPbkJsWlhJNk1pNUZlalpNVTJ0SGVUTmxNbm8xTkhWUU5GVTVTSGxZU2xoU2NHRkdNbmwwYzI1VWRWWm5hRFpUVGs1dFEzbEhXbEZhTGxaNk5rMXJhbVIzZG1ZNWFGZGpObWxpV201a1Z6bENPVGR6YVRreVJGTnJPV2hYUVdoSFdVSm5VRGxyVlVack9Gb3VVMlY1U2pCSmFtOXBXa2N3YVV4RFNucEphbTlwWVVoU01HTklUVFpNZVRscFlqSkpkVnBIYkd0TWJWcDBXak5CZFZsWVFuZE1lVWx6U1c1SmFVOXNkR1JNUTBwb1NXcHdZa2x0VW5CYVIwNTJZbGN3ZG1ScVNXbFlXREFqTmt4VGEwZDVNMlV5ZWpVMGRWQTBWVGxJZVZoS1dGSndZVVl5ZVhSemJsUjFWbWRvTmxOT1RtMURlVWRhVVZvIiwidHlwIjoiYXBwbGljYXRpb24vZGlkY29tbS1lbmNyeXB0ZWQranNvbiIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJhbGciOiJFQ0RILTFQVStBMjU2S1cifQ", + | "recipients": [{ + | "encrypted_key": "eFHcJkoUle7ZkksvZjvhJHLagF7y5B5gDQX11tc1kus2xa3Vn_QmzyVwFzScOHTCEjWRUe7r63rHBFBw0El2ukZW2tcitxr8", + | "header": { + | "kid": "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9hbGljZS5kaWQuZm1ncC5hcHAvIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfQ#6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y" + | } + | }], + | "tag": "6MUE-lIZFVRId9qX-0If18EsPcfld_a3r2gQXXq4ms4", + | "iv": "plEK7sqstAIkIxVKyfuuYg" + |}""".stripMargin.fromJson[EncryptedMessage] + val encryptedMessageBob: Either[String, EncryptedMessage] = """{ + | "ciphertext": "-dyjeU0aRQ8F87yAJFbOmmuon0UtEhQpmDQhYclMiTAqi06wH2bLTjtJNIOFG9sJ12uIyaQahPzCFBUyR2X22Oyl-1-Un0LOCAoRP584JuN19NdgKIytmSm4pg4VeSJ56SKO5D3Qe3G8XQa_7vO8O9-b_6V9mQXXZDkknNbphuiD7S53ss_VJzbgQVLRxkBrbnU1PL7yC6AhkAZNkUNCS8GGYbdEKFeRsdWjoROEnuQ1zzPN28eO3T4Wmt2APQ10vHum3Bl1RTiPRGCx3C-fSi05F-521jyP0rwyLeMZdBLruDz2IrUudtimFI3CKQ8_BcmuXTlLa1vw2hKlhk1_ppAmggW-qrjyZPiC9-XIN02OxZox5Xzba-cmg05Dj2zmEXryrxqFfyJlrV5FKSvWDfqeNAA9QNE0aUsgpKK9gOmu5pJRCuYNL96lbxdSCR4BiuX1G_ma2gagdIIY-70d22UPGcrZTw7Blet574i5U_bIDpZw4Az0UxoKNDbCDPOpn-Shojo5n_5FFbaPky0H0N99-9cx-Ns-3NMQecpupPQ9QJ-A2KWBTLYrJofLCBWv_ysqJ3Cde9fQ3xi5DTB13dhbxKVCgzdHKlwnqPC0C3mjFs_eZ1jnzBiUAKLvD_aWMwqx7y8GUhSfXlX3hImqtXaKBu2Koc1esUOvQwadHUj5pSmsNKJJcQLzsOcuYz8U9peRevrft3sy7fm9Ne2L3SSsoCFJP1N8U8bRLmyhY0CDuCNlvXxy_VFoSdPms40d1BNZObhgwO9Ca8kp1r0ITl22repvpMQYcQ83m9fkTXg", + | "protected": "eyJlcGsiOnsia3R5IjoiT0tQIiwiY3J2IjoiWDI1NTE5IiwieCI6ImNKRzZVeVJRbnhOM2RCa3ZQcndmZXZabFBwRzA0SzFyTzZHTjdnQkNyVFUifSwiYXB2IjoiLWNOQ3l0eFVrSHpSRE5SckV2Vm05S0VmZzhZcUtQVnVVcVg1a0VLbU9yMCIsInNraWQiOiJkaWQ6cGVlcjoyLkV6NkxTa0d5M2UyejU0dVA0VTlIeVhKWFJwYUYyeXRzblR1VmdoNlNOTm1DeUdaUVouVno2TWtqZHd2ZjloV2M2aWJabmRXOUI5N3NpOTJEU2s5aFdBaEdZQmdQOWtVRms4Wi5TZXlKMElqb2laRzBpTENKeklqb2lhSFIwY0hNNkx5OWliMkl1Wkdsa0xtWnRaM0F1WVhCd0x5SXNJbklpT2x0ZExDSmhJanBiSW1ScFpHTnZiVzB2ZGpJaVhYMCM2TFNrR3kzZTJ6NTR1UDRVOUh5WEpYUnBhRjJ5dHNuVHVWZ2g2U05ObUN5R1pRWiIsImFwdSI6IlpHbGtPbkJsWlhJNk1pNUZlalpNVTJ0SGVUTmxNbm8xTkhWUU5GVTVTSGxZU2xoU2NHRkdNbmwwYzI1VWRWWm5hRFpUVGs1dFEzbEhXbEZhTGxaNk5rMXJhbVIzZG1ZNWFGZGpObWxpV201a1Z6bENPVGR6YVRreVJGTnJPV2hYUVdoSFdVSm5VRGxyVlVack9Gb3VVMlY1U2pCSmFtOXBXa2N3YVV4RFNucEphbTlwWVVoU01HTklUVFpNZVRscFlqSkpkVnBIYkd0TWJWcDBXak5CZFZsWVFuZE1lVWx6U1c1SmFVOXNkR1JNUTBwb1NXcHdZa2x0VW5CYVIwNTJZbGN3ZG1ScVNXbFlXREFqTmt4VGEwZDVNMlV5ZWpVMGRWQTBWVGxJZVZoS1dGSndZVVl5ZVhSemJsUjFWbWRvTmxOT1RtMURlVWRhVVZvIiwidHlwIjoiYXBwbGljYXRpb24vZGlkY29tbS1lbmNyeXB0ZWQranNvbiIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJhbGciOiJFQ0RILTFQVStBMjU2S1cifQ", + | "recipients": [{ + | "encrypted_key": "KCjv83os8FnpVaIUgcuYV-5TIw4VYuGIoHozQ0JVlIwK2sEkxwWg10yh0UlbbcnNVfF_OBr4OkJUczj7pB0spRhvjoDHBM_s", + | "header": { + | "kid": "did:peer:2.Ez6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y.Vz6Mkhh1e5CEYYq6JBUcTZ6Cp2ranCWRrv7Yax3Le4N59R6dd.SeyJ0IjoiZG0iLCJzIjoiaHR0cHM6Ly9hbGljZS5kaWQuZm1ncC5hcHAvIiwiciI6W10sImEiOlsiZGlkY29tbS92MiJdfQ#6LSghwSE437wnDE1pt3X6hVDUQzSjsHzinpX3XFvMjRAm7y" + | } + | }], + | "tag": "iR0meUd8R3X5dleMywNHq5NaGJ4g0h2tok414SJ7UGI", + | "iv": "T-I0b4fIktFXVhAIFoSmQg" + |}""".stripMargin.fromJson[EncryptedMessage] +} diff --git a/mediator/src/test/scala/io/iohk/atala/mediator/db/EmbeddedMongoDBInstance.scala b/mediator/src/test/scala/io/iohk/atala/mediator/db/EmbeddedMongoDBInstance.scala new file mode 100644 index 00000000..3bc88431 --- /dev/null +++ b/mediator/src/test/scala/io/iohk/atala/mediator/db/EmbeddedMongoDBInstance.scala @@ -0,0 +1,30 @@ +package io.iohk.atala.mediator.db +import de.flapdoodle.embed.mongo.config.Net +import de.flapdoodle.embed.mongo.distribution.Version +import de.flapdoodle.embed.mongo.transitions.{ImmutableMongod, Mongod, RunningMongodProcess} +import de.flapdoodle.embed.process.io.ProcessOutput +import de.flapdoodle.reverse.TransitionWalker +import de.flapdoodle.reverse.transitions.Start +import zio.{Task, ZIO, ZLayer} + +object EmbeddedMongoDBInstance { + + def layer( + port: Int = 27077, + hostIp: String = "localhost" + ): ZLayer[Any, Throwable, TransitionWalker.ReachedState[RunningMongodProcess]] = { + ZLayer.scoped( + ZIO.acquireRelease( + ZIO.attemptBlocking { + Mongod + .builder() + .processOutput(Start.to(classOf[ProcessOutput]).initializedWith(ProcessOutput.silent())) + .net(Start.to(classOf[Net]).initializedWith(Net.of(hostIp, port, false))) + .build() + .start(Version.Main.V6_0) + } + )(process => ZIO.succeed(process.close())) + ) + } + +} diff --git a/mediator/src/test/scala/io/iohk/atala/mediator/db/MessageItemRepoSpec.scala b/mediator/src/test/scala/io/iohk/atala/mediator/db/MessageItemRepoSpec.scala new file mode 100644 index 00000000..cc8bd171 --- /dev/null +++ b/mediator/src/test/scala/io/iohk/atala/mediator/db/MessageItemRepoSpec.scala @@ -0,0 +1,55 @@ +package io.iohk.atala.mediator.db + +import fmgp.did.comm.EncryptedMessage +import zio.* +import zio.ExecutionStrategy.Sequential +import zio.json.* +import zio.test.* +import zio.test.Assertion.* + +object MessageItemRepoSpec extends ZIOSpecDefault with AccountStubSetup { + val port = 27777 + val hostIp = "localhost" + val connectionString = s"mongodb://$hostIp:$port/messages" + + override def spec = suite("MessageItemSpec")( + test("insert message") { + for { + messageItem <- ZIO.service[MessageItemRepo] + msg <- ZIO.fromEither(encryptedMessageAlice) + result <- messageItem.insert(MessageItem(msg)) + } yield { + println(result) + assertTrue(result.writeErrors == Nil) + assertTrue(result.n == 1) + } + }, + test("findById message") { + for { + messageItem <- ZIO.service[MessageItemRepo] + msg <- ZIO.fromEither(encryptedMessageAlice) + result <- messageItem.findById(msg.hashCode()) + } yield { + assertTrue(result.contains(MessageItem(msg))) + } + }, + test("findByIds messages") { + for { + messageItem <- ZIO.service[MessageItemRepo] + msg <- ZIO.fromEither(encryptedMessageAlice) + msg2 <- ZIO.fromEither(encryptedMessageBob) + msg2Added <- messageItem.insert(MessageItem(msg2)) + result <- messageItem.findByIds(Seq(msg.hashCode(), msg2.hashCode())) + } yield { + assertTrue(result.contains(MessageItem(msg))) + assertTrue(result.contains(MessageItem(msg2))) + } + } + ).provideLayerShared( + EmbeddedMongoDBInstance.layer(port, hostIp) + >>> AsyncDriverResource.layer + >>> ReactiveMongoApi.layer(connectionString) + >>> MessageItemRepo.layer + ) @@ TestAspect.sequential + +} diff --git a/mediator/src/test/scala/io/iohk/atala/mediator/db/UserAccountRepoSpec.scala b/mediator/src/test/scala/io/iohk/atala/mediator/db/UserAccountRepoSpec.scala new file mode 100644 index 00000000..d04a116b --- /dev/null +++ b/mediator/src/test/scala/io/iohk/atala/mediator/db/UserAccountRepoSpec.scala @@ -0,0 +1,137 @@ +package io.iohk.atala.mediator.db + +import fmgp.did.DIDSubject +import fmgp.did.comm.EncryptedMessage +import io.iohk.atala.mediator.StorageError +import reactivemongo.api.indexes.{Index, IndexType} +import zio.* +import zio.ExecutionStrategy.Sequential +import zio.json.* +import zio.test.* +import zio.test.Assertion.* + +import scala.concurrent.ExecutionContext.Implicits.global +object UserAccountRepoSpec extends ZIOSpecDefault with AccountStubSetup { + val port = 27778 + val hostIp = "localhost" + + val connectionString = s"mongodb://$hostIp:$port/messages" + // Define the index + val index = Index( + key = Seq("alias" -> IndexType.Ascending), + name = Some("alias_did"), + unique = true, + background = true + ) + + override def spec = suite("UserAccountRepoSpec")( + test("insert new Did Account") { + for { + userAccount <- ZIO.service[UserAccountRepo] + col <- userAccount.collection + _ = col.indexesManager.create(index) + result <- userAccount.newDidAccount(DIDSubject(alice)) + } yield { + assertTrue(result.writeErrors == Nil) + assertTrue(result.n == 1) + } + }, + test("insert same Did should fail") { + for { + userAccount <- ZIO.service[UserAccountRepo] + result <- userAccount.newDidAccount(DIDSubject(alice)).exit + } yield { + assert(result)(fails(isSubtype[StorageError](anything))) + } + }, + test("Get Did Account") { + for { + userAccount <- ZIO.service[UserAccountRepo] + result <- userAccount.getDidAccount(DIDSubject(alice)) + } yield { + assertTrue(result.isDefined) + assertTrue(result.exists(_.did == DIDSubject(alice))) + } + }, + test("Get Did Account return for unknown did") { + for { + userAccount <- ZIO.service[UserAccountRepo] + result <- userAccount.getDidAccount(DIDSubject(bob)) + } yield { + assertTrue(result.isEmpty) + } + }, + test("Add alias to existing Did Account return right") { + for { + userAccount <- ZIO.service[UserAccountRepo] + result <- userAccount.addAlias(DIDSubject(alice), DIDSubject(bob)) + didAccount <- userAccount.getDidAccount(DIDSubject(alice)) + } yield { + assertTrue(result.isRight) + assertTrue(result == Right(())) + assertTrue(didAccount.isDefined) + val alias: Seq[String] = didAccount.map(_.alias.map(_.did)).getOrElse(Seq.empty) + assertTrue(alias == Seq(alice, bob)) + } + }, + test("Remove alias to existing Did Account should return right") { + for { + userAccount <- ZIO.service[UserAccountRepo] + result <- userAccount.removeAlias(DIDSubject(alice), DIDSubject(bob)) + didAccount <- userAccount.getDidAccount(DIDSubject(alice)) + } yield { + assertTrue(result.isRight) + assertTrue(result == Right(())) + assertTrue(didAccount.isDefined) + val alias: Seq[String] = didAccount.map(_.alias.map(_.did)).getOrElse(Seq.empty) + assertTrue(alias == Seq(alice)) + } + }, + test("addMessage to inbox for given Account") { + for { + userAccount <- ZIO.service[UserAccountRepo] + messageItem <- ZIO.service[MessageItemRepo] + result <- userAccount.addAlias(DIDSubject(alice), DIDSubject(bob)) + msg <- ZIO.fromEither(encryptedMessageAlice) + msgAdded <- messageItem.insert(MessageItem(msg)) + addedToInbox <- userAccount.addToInboxes(Set(DIDSubject(bob)), msg) + didAccount <- userAccount.getDidAccount(DIDSubject(alice)) + } yield { + assertTrue(result.isRight) + assertTrue(result == Right(())) + assertTrue(msgAdded.writeErrors == Nil) + assertTrue(msgAdded.n == 1) + assertTrue(addedToInbox == 1) + val messageMetaData: Seq[MessageMetaData] = didAccount.map(_.messagesRef).getOrElse(Seq.empty) + assert(messageMetaData)( + forall( + hasField("hash", (m: MessageMetaData) => m.hash, equalTo(msg.hashCode())) + && hasField("recipient", (m: MessageMetaData) => m.recipient, equalTo(DIDSubject(bob))) + ) + ) + } + }, + test("mark message as delivered given did") { + for { + userAccount <- ZIO.service[UserAccountRepo] + msg <- ZIO.fromEither(encryptedMessageAlice) + markedDelivered <- userAccount.markAsDelivered(DIDSubject(alice), Seq(msg.hashCode())) + didAccount <- userAccount.getDidAccount(DIDSubject(alice)) + } yield { + assertTrue(markedDelivered == 1) + val messageMetaData: Seq[MessageMetaData] = didAccount.map(_.messagesRef).getOrElse(Seq.empty) + assert(messageMetaData)( + forall( + hasField("state", (m: MessageMetaData) => m.state, equalTo(true)) + ) + ) + } + }, + ).provideLayerShared( + EmbeddedMongoDBInstance.layer(port, hostIp) + >>> AsyncDriverResource.layer + >>> ReactiveMongoApi.layer(connectionString) + >>> (UserAccountRepo.layer ++ MessageItemRepo.layer) + ) @@ TestAspect.sequential + +}