diff --git a/Dockerfile b/Dockerfile index a20dc4f..fedbdc9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ -FROM alpine +FROM alpine:3 #For alpine versions need to create a group before adding a user to the image WORKDIR /transferservice RUN addgroup --system transferservicegroup && adduser --system transferserviceuser -G transferservicegroup && \ chown -R transferserviceuser /transferservice && \ + apk update && apk upgrade p11-kit busybox expat libretls zlib openssl libcrypto3 libssl3 && \ apk add openjdk17 --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community COPY target/scala-2.13/transferservice.jar /transferservice diff --git a/build.sbt b/build.sbt index 8977c04..f11c4d8 100644 --- a/build.sbt +++ b/build.sbt @@ -1,3 +1,4 @@ +import Dependencies.* ThisBuild / version := "0.0.1-SNAPSHOT" ThisBuild / scalaVersion := "2.13.14" @@ -5,18 +6,38 @@ ThisBuild / organization := "uk.gov.nationalarchives" lazy val root = (project in file(".")) .settings( - name := "tdr-transfer-service" + name := "tdr-transfer-service", + libraryDependencies ++= Seq( + authUtils, + catsEffect, + http4sCirce, + http4sDsl, + http4sEmberServer, + keycloakMock % Test, + logbackClassic, + logBackEncoder, + mockito % Test, + pekkoTestKitHttp % Test, + pureConfig, + pureConfigCatsEffect, + scalaTest % Test, + tapirHttp4sServer, + tapirJsonCirce, + tapirSwaggerUI + ) ) -(Compile / run / mainClass) := Some("Main") +(Compile / run / mainClass) := Some("uk.gov.nationalarchives.tdr.transfer.service.api.TransferServiceServer") (assembly / assemblyJarName) := "transferservice.jar" (assembly / assemblyMergeStrategy) := { - case PathList("META-INF", x, xs @ _*) if x.toLowerCase == "services" => MergeStrategy.filterDistinctLines - case PathList("META-INF", xs @ _*) => MergeStrategy.discard - case PathList("reference.conf") => MergeStrategy.concat - case _ => MergeStrategy.first + case PathList("META-INF", "maven", "org.webjars", "swagger-ui", "pom.properties") => MergeStrategy.singleOrError + case PathList("META-INF", "resources", "webjars", "swagger-ui", _*) => MergeStrategy.singleOrError + case PathList("META-INF", x, xs @ _*) if x.toLowerCase == "services" => MergeStrategy.filterDistinctLines + case PathList("META-INF", xs @ _*) => MergeStrategy.discard + case PathList("reference.conf") => MergeStrategy.concat + case _ => MergeStrategy.first } -(assembly / mainClass) := Some("Main") +(assembly / mainClass) := Some("uk.gov.nationalarchives.tdr.transfer.service.api.TransferServiceServer") diff --git a/project/Dependencies.scala b/project/Dependencies.scala new file mode 100644 index 0000000..8a9f238 --- /dev/null +++ b/project/Dependencies.scala @@ -0,0 +1,33 @@ +import sbt.* + +object Dependencies { + private val http4sVersion = "0.23.26" + private val pureConfigVersion = "0.17.6" + private val tapirVersion = "1.10.7" + + lazy val authUtils = "uk.gov.nationalarchives" %% "tdr-auth-utils" % "0.0.196" + + lazy val catsEffect = "org.typelevel" %% "cats-effect" % "3.5.4" + + lazy val http4sCirce = "org.http4s" %% "http4s-circe" % http4sVersion + lazy val http4sEmberServer = "org.http4s" %% "http4s-ember-server" % http4sVersion + lazy val http4sDsl = "org.http4s" %% "http4s-dsl" % http4sVersion + + lazy val keycloakMock = "com.tngtech.keycloakmock" % "mock" % "0.16.0" + + lazy val logBackEncoder = "net.logstash.logback" % "logstash-logback-encoder" % "7.4" + lazy val logbackClassic = "ch.qos.logback" % "logback-classic" % "1.5.6" + + lazy val mockito = "org.mockito" %% "mockito-scala" % "1.17.31" + lazy val mockitoScalaTest = "org.mockito" %% "mockito-scala-scalatest" % "1.17.31" + + lazy val pekkoTestKitHttp = "org.apache.pekko" %% "pekko-http-testkit" % "1.0.1" + lazy val pureConfig = "com.github.pureconfig" %% "pureconfig" % pureConfigVersion + lazy val pureConfigCatsEffect = "com.github.pureconfig" %% "pureconfig-cats-effect" % pureConfigVersion + + lazy val tapirHttp4sServer = "com.softwaremill.sttp.tapir" %% "tapir-http4s-server" % tapirVersion + lazy val tapirJsonCirce = "com.softwaremill.sttp.tapir" %% "tapir-json-circe" % tapirVersion + lazy val tapirSwaggerUI = "com.softwaremill.sttp.tapir" %% "tapir-swagger-ui-bundle" % tapirVersion + + lazy val scalaTest = "org.scalatest" %% "scalatest" % "3.2.18" +} diff --git a/src/main/resources/application.conf b/src/main/resources/application.conf new file mode 100644 index 0000000..42177c6 --- /dev/null +++ b/src/main/resources/application.conf @@ -0,0 +1,10 @@ +api { + port = "8080" + port = ${?API_PORT} +} + +auth { + url = "https://auth.tdr-integration.nationalarchives.gov.uk" + url = ${?AUTH_URL} + realm = "tdr" +} diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..8b44e1a --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + + {"application":"transfer-service"} + + + + + + + diff --git a/src/main/scala/Main.scala b/src/main/scala/Main.scala deleted file mode 100644 index a35d2e7..0000000 --- a/src/main/scala/Main.scala +++ /dev/null @@ -1,5 +0,0 @@ -object Main { - def main(args: Array[String]): Unit = { - println("Hello world!") - } -} diff --git a/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/ApplicationConfig.scala b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/ApplicationConfig.scala new file mode 100644 index 0000000..a2e5767 --- /dev/null +++ b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/ApplicationConfig.scala @@ -0,0 +1,16 @@ +package uk.gov.nationalarchives.tdr.transfer.service + +import pureconfig.ConfigSource +import pureconfig.generic.auto._ + +object ApplicationConfig { + case class Api(port: Int) + case class Auth(url: String, realm: String) + + case class Configuration(auth: Auth, api: Api) + + val appConfig: Configuration = ConfigSource.default.load[Configuration] match { + case Left(value) => throw new RuntimeException(s"Failed to load transfer service application configuration ${value.prettyPrint()}") + case Right(value) => value + } +} diff --git a/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/TransferServiceServer.scala b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/TransferServiceServer.scala new file mode 100644 index 0000000..5f70f69 --- /dev/null +++ b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/TransferServiceServer.scala @@ -0,0 +1,51 @@ +package uk.gov.nationalarchives.tdr.transfer.service.api + +import cats.data.Kleisli +import cats.effect.{ExitCode, IO, IOApp} +import cats.implicits.toSemigroupKOps +import com.comcast.ip4s.{IpLiteralSyntax, Port} +import org.http4s.dsl.io._ +import org.http4s.ember.server.EmberServerBuilder +import org.http4s.server.middleware.Logger +import org.http4s.{HttpRoutes, Request, Response} +import sttp.apispec.openapi.Info +import sttp.tapir.server.http4s.Http4sServerInterpreter +import sttp.tapir.swagger.bundle.SwaggerInterpreter +import uk.gov.nationalarchives.tdr.transfer.service.ApplicationConfig +import uk.gov.nationalarchives.tdr.transfer.service.api.controllers.LoadController + +object TransferServiceServer extends IOApp { + private val apiPort: Port = Port.fromInt(ApplicationConfig.appConfig.api.port).getOrElse(port"8080") + + private val infoTitle = "TDR Transfer Service API" + private val infoVersion = "0.0.1" + private val infoDescription = Some("APIs to allow client services to transfer records to TDR") + + private val openApiInfo: Info = Info(infoTitle, infoVersion, description = infoDescription) + private val loadController = LoadController.apply() + + private val documentationEndpoints = + SwaggerInterpreter().fromEndpoints[IO](loadController.endpoints, openApiInfo) + + val healthCheckRoute: HttpRoutes[IO] = HttpRoutes.of[IO] { case GET -> Root / "healthcheck" => + Ok("Healthy") + } + + private val allRoutes = + Http4sServerInterpreter[IO]().toRoutes(documentationEndpoints) <+> loadController.routes <+> healthCheckRoute + + private val app: Kleisli[IO, Request[IO], Response[IO]] = allRoutes.orNotFound + + private val finalApp = Logger.httpApp(logHeaders = true, logBody = false)(app) + + private val transferServiceServer = EmberServerBuilder + .default[IO] + .withHost(ipv4"0.0.0.0") + .withPort(apiPort) + .withHttpApp(finalApp) + .build + + override def run(args: List[String]): IO[ExitCode] = { + transferServiceServer.use(_ => IO.never).as(ExitCode.Success) + } +} diff --git a/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/auth/TokenAuthenticator.scala b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/auth/TokenAuthenticator.scala new file mode 100644 index 0000000..7e1b7fe --- /dev/null +++ b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/auth/TokenAuthenticator.scala @@ -0,0 +1,38 @@ +package uk.gov.nationalarchives.tdr.transfer.service.api.auth + +import cats.effect.IO +import uk.gov.nationalarchives.tdr.keycloak.{KeycloakUtils, TdrKeycloakDeployment, Token} +import uk.gov.nationalarchives.tdr.transfer.service.ApplicationConfig +import uk.gov.nationalarchives.tdr.transfer.service.api.errors.BackendException.AuthenticationError + +import scala.concurrent.ExecutionContext + +case class AuthenticatedContext(token: Token) + +class TokenAuthenticator { + implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global + private val appConfig = ApplicationConfig.appConfig + + private val authUrl = appConfig.auth.url + private val realm = appConfig.auth.realm + + implicit val tdrKeycloakDeployment: TdrKeycloakDeployment = + TdrKeycloakDeployment(s"$authUrl", realm, 8080) + + def authenticateUserToken(bearer: String): IO[Either[AuthenticationError, AuthenticatedContext]] = { + IO { + KeycloakUtils().token(bearer) match { + case Right(t) => Right(AuthenticatedContext(t)) + case Left(e) => + Left { + println(e.getMessage) + AuthenticationError(e.getMessage) + } + } + } + } +} + +object TokenAuthenticator { + def apply() = new TokenAuthenticator +} diff --git a/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/controllers/BaseController.scala b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/controllers/BaseController.scala new file mode 100644 index 0000000..d932bd5 --- /dev/null +++ b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/controllers/BaseController.scala @@ -0,0 +1,25 @@ +package uk.gov.nationalarchives.tdr.transfer.service.api.controllers + +import cats.effect.IO +import sttp.model.StatusCode +import sttp.tapir.json.circe.jsonBody +import sttp.tapir.server.PartialServerEndpoint +import sttp.tapir.{Endpoint, auth, endpoint, statusCode} +import uk.gov.nationalarchives.tdr.transfer.service.api.auth.{AuthenticatedContext, TokenAuthenticator} +import uk.gov.nationalarchives.tdr.transfer.service.api.errors.BackendException.AuthenticationError +import uk.gov.nationalarchives.tdr.transfer.service.api.model.Serializers._ + +trait BaseController { + + private val tokenAuthenticator = TokenAuthenticator() + + private val securedWithBearerEndpoint: Endpoint[String, Unit, AuthenticationError, Unit, Any] = endpoint + .securityIn(auth.bearer[String]()) + .errorOut(statusCode(StatusCode.Unauthorized)) + .errorOut(jsonBody[AuthenticationError]) + + val securedWithBearer: PartialServerEndpoint[String, AuthenticatedContext, Unit, AuthenticationError, Unit, Any, IO] = securedWithBearerEndpoint + .serverSecurityLogic( + tokenAuthenticator.authenticateUserToken + ) +} diff --git a/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/controllers/LoadController.scala b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/controllers/LoadController.scala new file mode 100644 index 0000000..7704bdb --- /dev/null +++ b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/controllers/LoadController.scala @@ -0,0 +1,32 @@ +package uk.gov.nationalarchives.tdr.transfer.service.api.controllers + +import cats.effect.IO +import org.http4s.HttpRoutes +import sttp.tapir.{Endpoint, _} +import sttp.tapir.json.circe.jsonBody +import sttp.tapir.server.PartialServerEndpoint +import sttp.tapir.server.http4s.Http4sServerInterpreter +import uk.gov.nationalarchives.tdr.transfer.service.api.auth.AuthenticatedContext +import uk.gov.nationalarchives.tdr.transfer.service.api.errors.BackendException +import uk.gov.nationalarchives.tdr.transfer.service.api.model.Consignment.ConsignmentDetails +import uk.gov.nationalarchives.tdr.transfer.service.api.model.Serializers._ +import uk.gov.nationalarchives.tdr.transfer.service.services.ConsignmentService + +class LoadController(consignmentService: ConsignmentService) extends BaseController { + def endpoints: List[Endpoint[String, Unit, BackendException.AuthenticationError, ConsignmentDetails, Any]] = List(initiateLoadEndpoint.endpoint) + + def routes: HttpRoutes[IO] = initiateLoadRoute + + private val initiateLoadEndpoint: PartialServerEndpoint[String, AuthenticatedContext, Unit, BackendException.AuthenticationError, ConsignmentDetails, Any, IO] = securedWithBearer + .summary("Initiate the load of records and metadata") + .post + .in("load" / "sharepoint" / "initiate") + .out(jsonBody[ConsignmentDetails]) + + val initiateLoadRoute: HttpRoutes[IO] = + Http4sServerInterpreter[IO]().toRoutes(initiateLoadEndpoint.serverLogicSuccess(ac => _ => consignmentService.createConsignment(ac.token))) +} + +object LoadController { + def apply() = new LoadController(ConsignmentService()) +} diff --git a/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/errors/BackendException.scala b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/errors/BackendException.scala new file mode 100644 index 0000000..c571837 --- /dev/null +++ b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/errors/BackendException.scala @@ -0,0 +1,7 @@ +package uk.gov.nationalarchives.tdr.transfer.service.api.errors + +sealed trait BackendException extends Exception + +object BackendException { + case class AuthenticationError(message: String) extends BackendException +} diff --git a/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/model/Consignment.scala b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/model/Consignment.scala new file mode 100644 index 0000000..462a6c4 --- /dev/null +++ b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/model/Consignment.scala @@ -0,0 +1,9 @@ +package uk.gov.nationalarchives.tdr.transfer.service.api.model + +import java.util.UUID + +sealed trait ConsignmentModel + +object Consignment { + case class ConsignmentDetails(consignmentId: UUID) extends ConsignmentModel +} diff --git a/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/model/Serializers.scala b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/model/Serializers.scala new file mode 100644 index 0000000..94f5f87 --- /dev/null +++ b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/api/model/Serializers.scala @@ -0,0 +1,9 @@ +package uk.gov.nationalarchives.tdr.transfer.service.api.model + +import io.circe.generic.AutoDerivation +import sttp.tapir.generic.auto.SchemaDerivation +import sttp.tapir.generic.{Configuration => TapirConfiguration} + +object Serializers extends AutoDerivation with SchemaDerivation { + implicit val schemaConfiguration: TapirConfiguration = TapirConfiguration.default.withDiscriminator("type") +} diff --git a/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/services/ConsignmentService.scala b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/services/ConsignmentService.scala new file mode 100644 index 0000000..965b497 --- /dev/null +++ b/src/main/scala/uk/gov/nationalarchives/tdr/transfer/service/services/ConsignmentService.scala @@ -0,0 +1,19 @@ +package uk.gov.nationalarchives.tdr.transfer.service.services + +import cats.effect.IO +import cats.implicits.catsSyntaxApplicativeId +import uk.gov.nationalarchives.tdr.keycloak.Token +import uk.gov.nationalarchives.tdr.transfer.service.api.model.Consignment.ConsignmentDetails + +import java.util.UUID + +class ConsignmentService { + def createConsignment(token: Token): IO[ConsignmentDetails] = { + // For now just return dummy response + ConsignmentDetails(UUID.fromString("ae4b7cad-ee83-46bd-b952-80bc8263c6c2")).pure[IO] + } +} + +object ConsignmentService { + def apply() = new ConsignmentService +} diff --git a/src/test/resources/application.conf b/src/test/resources/application.conf new file mode 100644 index 0000000..9ab2156 --- /dev/null +++ b/src/test/resources/application.conf @@ -0,0 +1,8 @@ +api { + port = "8080" +} + +auth { + url = "http://localhost:8000/auth" + realm = "tdr" +} diff --git a/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/TestUtils.scala b/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/TestUtils.scala new file mode 100644 index 0000000..dd3acc4 --- /dev/null +++ b/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/TestUtils.scala @@ -0,0 +1,38 @@ +package uk.gov.nationalarchives.tdr.transfer.service + +import com.tngtech.keycloakmock.api.TokenConfig.aTokenConfig +import com.tngtech.keycloakmock.api.{KeycloakMock, ServerConfig} +import org.apache.pekko.http.scaladsl.model.headers.OAuth2BearerToken +import org.scalatest.BeforeAndAfterEach +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +import java.util.UUID + +object TestUtils extends AnyFlatSpec with Matchers with BeforeAndAfterEach { + private val tdrKeycloakMock: KeycloakMock = createServer("tdr", 8000) + private val testKeycloakMock: KeycloakMock = createServer("test", 8001) + + val userId: UUID = UUID.fromString("4ab14990-ed63-4615-8336-56fbb9960300") + + def validUserToken(userId: UUID = userId, body: String = "Code", standardUser: String = "true"): OAuth2BearerToken = + OAuth2BearerToken( + tdrKeycloakMock.getAccessToken( + aTokenConfig() + .withResourceRole("tdr", "tdr_user") + .withClaim("body", body) + .withClaim("user_id", userId) + .withClaim("standard_user", standardUser) + .build + ) + ) + + def invalidToken: OAuth2BearerToken = OAuth2BearerToken(testKeycloakMock.getAccessToken(aTokenConfig().build)) + + private def createServer(realm: String, port: Int): KeycloakMock = { + val config = ServerConfig.aServerConfig().withPort(port).withDefaultRealm(realm).build() + val mock: KeycloakMock = new KeycloakMock(config) + mock.start() + mock + } +} diff --git a/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/api/TransferServiceServerSpec.scala b/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/api/TransferServiceServerSpec.scala new file mode 100644 index 0000000..164b326 --- /dev/null +++ b/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/api/TransferServiceServerSpec.scala @@ -0,0 +1,69 @@ +package uk.gov.nationalarchives.tdr.transfer.service.api + +import cats.effect.IO +import cats.effect.unsafe.implicits.global +import io.circe.Json +import io.circe.syntax.KeyOps +import org.http4s.circe.jsonDecoder +import org.http4s.implicits.http4sLiteralsSyntax +import org.http4s.{Header, Headers, Method, Request, Status} +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import org.typelevel.ci.CIString +import uk.gov.nationalarchives.tdr.transfer.service.TestUtils.{invalidToken, validUserToken} +import uk.gov.nationalarchives.tdr.transfer.service.api.controllers.LoadController + +class TransferServiceServerSpec extends AnyFlatSpec with Matchers { + + "'healthcheck' endpoint" should "return 200 if server running" in { + val getHealthCheck = Request[IO](Method.GET, uri"/healthcheck") + val response = TransferServiceServer.healthCheckRoute.orNotFound(getHealthCheck).unsafeRunSync() + + response.status shouldBe Status.Ok + response.as[String].unsafeRunSync() shouldEqual "Healthy" + } + + "'load/sharepoint/initiate' endpoint" should "return 200 with correct authorisation header" in { + val validToken = validUserToken() + val bearer = CIString("Authorization") + val authHeader = Header.Raw.apply(bearer, s"$validToken") + val fakeHeaders = Headers.apply(authHeader) + val response = LoadController + .apply() + .initiateLoadRoute + .orNotFound + .run( + Request(method = Method.POST, uri = uri"/load/sharepoint/initiate", headers = fakeHeaders) + ) + .unsafeRunSync() + + val expectedResponse = Json.obj( + "consignmentId" := "ae4b7cad-ee83-46bd-b952-80bc8263c6c2" + ) + + response.status shouldBe Status.Ok + response.as[Json].unsafeRunSync() shouldEqual expectedResponse + } + + "'load/sharepoint/initiate' endpoint" should "return 401 response with incorrect authorisation header" in { + val token = invalidToken + val bearer = CIString("Authorization") + val authHeader = Header.Raw.apply(bearer, s"$token") + val fakeHeaders = Headers.apply(authHeader) + val response = LoadController + .apply() + .initiateLoadRoute + .orNotFound + .run( + Request(method = Method.POST, uri = uri"/load/sharepoint/initiate", headers = fakeHeaders) + ) + .unsafeRunSync() + + val expectedResponse = Json.obj( + "message" := "Invalid token issuer. Expected 'http://localhost:8000/auth/realms/tdr'" + ) + + response.status shouldBe Status.Unauthorized + response.as[Json].unsafeRunSync() shouldEqual expectedResponse + } +} diff --git a/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/api/auth/TokenAuthenticatorSpec.scala b/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/api/auth/TokenAuthenticatorSpec.scala new file mode 100644 index 0000000..75d2bb7 --- /dev/null +++ b/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/api/auth/TokenAuthenticatorSpec.scala @@ -0,0 +1,22 @@ +package uk.gov.nationalarchives.tdr.transfer.service.api.auth + +import cats.effect.unsafe.implicits.global +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import uk.gov.nationalarchives.tdr.transfer.service.TestUtils.{invalidToken, userId, validUserToken} + +class TokenAuthenticatorSpec extends AnyFlatSpec with Matchers { + "'authenticateUserToken'" should "return authenticated token when token valid" in { + val validToken = validUserToken() + val response = TokenAuthenticator().authenticateUserToken(validToken.token).unsafeRunSync() + + response.toOption.get.token.userId shouldBe userId + } + + "'authenticateUserToken'" should "return authentication error when token invalid" in { + val token = invalidToken + val response = TokenAuthenticator().authenticateUserToken(token.token).unsafeRunSync() + + response.left.toOption.get.message shouldBe "Invalid token issuer. Expected 'http://localhost:8000/auth/realms/tdr'" + } +} diff --git a/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/services/ConsignmentServiceSpec.scala b/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/services/ConsignmentServiceSpec.scala new file mode 100644 index 0000000..6677a48 --- /dev/null +++ b/src/test/scala/uk/gov/nationalarchives/tdr/transfer/service/services/ConsignmentServiceSpec.scala @@ -0,0 +1,19 @@ +package uk.gov.nationalarchives.tdr.transfer.service.services + +import cats.effect.unsafe.implicits.global +import org.mockito.MockitoSugar.mock +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers +import uk.gov.nationalarchives.tdr.keycloak.Token + +import java.util.UUID + +class ConsignmentServiceSpec extends AnyFlatSpec with Matchers { + val mockKeycloakToken: Token = mock[Token] + + "'createConsignment'" should "return the consignment id of the created consignment" in { + val response = ConsignmentService().createConsignment(mockKeycloakToken).unsafeRunSync() + + response.consignmentId shouldBe UUID.fromString("ae4b7cad-ee83-46bd-b952-80bc8263c6c2") + } +}