From 7f7fdd19fd64bed19fbed255575b313a26a9df14 Mon Sep 17 00:00:00 2001 From: VimleshGupta Date: Tue, 23 Jul 2024 11:37:56 +0100 Subject: [PATCH 1/4] Revert "fix type script formatting issue" This reverts commit 4097ea598f93cf0451eef2986054cc5c22d8e3b4. --- npm/src/checks/index.ts | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/npm/src/checks/index.ts b/npm/src/checks/index.ts index 3c96d297d..5302c5056 100644 --- a/npm/src/checks/index.ts +++ b/npm/src/checks/index.ts @@ -22,22 +22,19 @@ export class Checks { if (isJudgmentUser) { goToNextPage("#continue-transfer") } else { - const intervalId: ReturnType = setInterval( - async () => { - const fileChecksProgress: IFileCheckProgress | Error = + const intervalId: ReturnType = setInterval(async () => { + const fileChecksProgress: IFileCheckProgress | Error = await getFileChecksProgress() - if (!isError(fileChecksProgress)) { - const checksCompleted = haveFileChecksCompleted(fileChecksProgress) - if (checksCompleted) { - clearInterval(intervalId) - displayChecksCompletedBanner("file-checks") - } - } else { - return fileChecksProgress + if (!isError(fileChecksProgress)) { + const checksCompleted = haveFileChecksCompleted(fileChecksProgress) + if (checksCompleted) { + clearInterval(intervalId) + displayChecksCompletedBanner("file-checks") } - }, - 5000 - ) + } else { + return fileChecksProgress + } + }, 5000) } } From 0c9bf77df53b616a1877d10a702801b0957bf8d0 Mon Sep 17 00:00:00 2001 From: VimleshGupta Date: Tue, 23 Jul 2024 11:40:03 +0100 Subject: [PATCH 2/4] Revert "Continue transfer on the file check page for judgment without waiting for the backend checks to be completed" This reverts commit 83925d67 --- app/configuration/ApplicationConfig.scala | 2 - app/controllers/FileChecksController.scala | 105 ++-- .../FileChecksResultsController.scala | 35 +- app/controllers/ViewTransfersController.scala | 2 +- app/services/ConsignmentService.scala | 8 + ...eChecksProgressAlreadyConfirmed.scala.html | 2 +- .../judgmentFileChecksProgress.scala.html | 2 +- build.sbt | 5 - conf/application.base.conf | 2 - conf/routes | 3 +- npm/src/checks/index.ts | 30 +- npm/test/update-check-progress.test.ts | 69 ++- .../FileChecksControllerSpec.scala | 381 +++++++------- .../FileChecksResultsControllerSpec.scala | 481 ++++++++++++------ .../ConsignmentStatusesOptions.scala | 14 +- 15 files changed, 638 insertions(+), 503 deletions(-) diff --git a/app/configuration/ApplicationConfig.scala b/app/configuration/ApplicationConfig.scala index 73f183d90..4319ee7c4 100644 --- a/app/configuration/ApplicationConfig.scala +++ b/app/configuration/ApplicationConfig.scala @@ -32,6 +32,4 @@ class ApplicationConfig @Inject() (configuration: Configuration) { val draftMetadataFileName: String = configuration.get[String]("draftMetadata.fileName") val notificationSnsTopicArn: String = get("notificationSnsTopicArn") - - val fileChecksTotalTimoutInSeconds: Int = configuration.get[Int]("fileChecksTotalTimoutInSeconds") } diff --git a/app/controllers/FileChecksController.scala b/app/controllers/FileChecksController.scala index bfccc4515..02cb2655f 100644 --- a/app/controllers/FileChecksController.scala +++ b/app/controllers/FileChecksController.scala @@ -7,14 +7,16 @@ import graphql.codegen.types.ConsignmentStatusInput import io.circe.syntax._ import org.pac4j.play.scala.SecurityComponents import play.api.i18n.I18nSupport -import play.api.mvc._ -import services.Statuses._ -import services._ +import play.api.mvc.{Action, AnyContent, Request, RequestHeader} +import services.Statuses.{CompletedValue, CompletedWithIssuesValue, UploadType} +import services.{BackendChecksService, ConsignmentService, ConsignmentStatusService} import viewsapi.Caching.preventCaching import java.util.UUID import javax.inject.{Inject, Singleton} -import scala.concurrent.{ExecutionContext, Future, blocking} +import scala.concurrent.duration.DurationInt +import scala.concurrent.{Await, ExecutionContext, Future} +import scala.util.control.Breaks.{break, breakable} @Singleton class FileChecksController @Inject() ( @@ -24,16 +26,14 @@ class FileChecksController @Inject() ( val consignmentService: ConsignmentService, val applicationConfig: ApplicationConfig, val backendChecksService: BackendChecksService, - val consignmentStatusService: ConsignmentStatusService, - val confirmTransferService: ConfirmTransferService, - val consignmentExportService: ConsignmentExportService + val consignmentStatusService: ConsignmentStatusService )(implicit val ec: ExecutionContext) extends TokenSecurity with I18nSupport { private def getFileChecksProgress(request: Request[AnyContent], consignmentId: UUID)(implicit requestHeader: RequestHeader): Future[FileChecksProgress] = { consignmentService - .fileCheckProgress(consignmentId, request.token.bearerAccessToken) + .getConsignmentFileChecks(consignmentId, request.token.bearerAccessToken) .map { fileCheckProgress => FileChecksProgress( fileCheckProgress.totalFiles, @@ -95,24 +95,6 @@ class FileChecksController @Inject() ( } yield result } - def judgmentCompleteTransfer(consignmentId: UUID): Action[AnyContent] = judgmentTypeAction(consignmentId) { implicit request: Request[AnyContent] => - val token = request.token.bearerAccessToken - for { - reference <- consignmentService.getConsignmentRef(consignmentId, token) - result <- - ( - for { - _ <- waitForFileChecksToBeCompleted(consignmentId) - result <- JudgmentCompleteTransfer(consignmentId) - } yield result - ).recover { case _: Exception => - Ok(views.html.uploadInProgress(consignmentId, reference, "Uploading your records", request.token.name, isJudgmentUser = true)).uncache() - } - } yield { - result - } - } - private def handleSuccessfulUpload(consignmentId: UUID, reference: String, isJudgmentUser: Boolean)(implicit request: Request[AnyContent]) = { val token = request.token.bearerAccessToken (for { @@ -139,7 +121,13 @@ class FileChecksController @Inject() ( ).uncache() } else { if (isJudgmentUser) { - Ok(views.html.judgment.judgmentFileChecksProgress(consignmentId, reference, applicationConfig.frontEndInfo, request.token.name)).uncache() + if (applicationConfig.blockAutomateJudgmentTransfers) { + Ok(views.html.judgment.judgmentFileChecksProgress(consignmentId, reference, applicationConfig.frontEndInfo, request.token.name)).uncache() + } else { + val totalSleepTime = 3 * 60 * 1000 // Total sleep time in milliseconds (5 minutes) + Await.result(waitForFileChecksToBeCompleted(consignmentId, totalSleepTime), totalSleepTime.millis + 1.minute) // Adding a little buffer time + Redirect(routes.FileChecksResultsController.judgmentFileCheckResultsPage(consignmentId)).uncache() + } } else { Ok(views.html.standard.fileChecksProgress(consignmentId, reference, applicationConfig.frontEndInfo, request.token.name)) .uncache() @@ -150,60 +138,27 @@ class FileChecksController @Inject() ( } } - private def JudgmentCompleteTransfer(consignmentId: UUID)(implicit request: Request[AnyContent]): Future[Result] = { - val pageTitle = "Results of checks" - for { - consignmentStatuses <- consignmentStatusService.getConsignmentStatuses(consignmentId, request.token.bearerAccessToken) - reference <- consignmentService.getConsignmentRef(consignmentId, request.token.bearerAccessToken) - exportStatus = consignmentStatusService.getStatusValues(consignmentStatuses, ExportType).values.headOption.flatten - result <- exportStatus match { - case Some(InProgressValue.value) | Some(CompletedValue.value) | Some(FailedValue.value) => - Future(Ok(views.html.transferAlreadyCompleted(consignmentId, reference, request.token.name, isJudgmentUser = true)).uncache()) - case None => - for { - fileCheck <- consignmentService.fileCheckProgress(consignmentId, request.token.bearerAccessToken) - result <- - if (fileCheck.allChecksSucceeded) { - val token: BearerAccessToken = request.token.bearerAccessToken - val legalCustodyTransferConfirmation = FinalTransferConfirmationData(transferLegalCustody = true) - for { - _ <- confirmTransferService.addFinalTransferConfirmation(consignmentId, token, legalCustodyTransferConfirmation) - _ <- consignmentExportService.updateTransferInitiated(consignmentId, request.token.bearerAccessToken) - _ <- consignmentExportService.triggerExport(consignmentId, request.token.bearerAccessToken.toString) - res <- Future(Redirect(routes.TransferCompleteController.judgmentTransferComplete(consignmentId)).uncache()) - } yield res - } else { - Future(Ok(views.html.fileChecksResultsFailed(request.token.name, pageTitle, reference, isJudgmentUser = true)).uncache()) - } - } yield result - case _ => - throw new IllegalStateException(s"Unexpected Export status: $exportStatus for consignment $consignmentId") - } - } yield result - } - - private def waitForFileChecksToBeCompleted(consignmentId: UUID)(implicit request: Request[AnyContent]): Future[Unit] = { - val totalSleepTime = applicationConfig.fileChecksTotalTimoutInSeconds * 1000 // Total sleep time in milliseconds + private def waitForFileChecksToBeCompleted(consignmentId: UUID, totalSleepTime: Int)(implicit request: Request[AnyContent]): Future[Unit] = { val interval = 5 * 1000 // Interval time in milliseconds (5 seconds) val intervals = totalSleepTime / interval - def checkStatus(remainingIntervals: Int): Future[Unit] = { - if (remainingIntervals <= 0) { - Future.unit - } else { - getFileChecksProgress(request, consignmentId).flatMap { progress => - if (progress.isComplete) { - Future.unit - } else { - Future { - blocking(Thread.sleep(interval)) - }.flatMap(_ => checkStatus(remainingIntervals - 1)) - } + def fileCheckProgress: Future[Boolean] = consignmentService + .fileCheckProgress(consignmentId, request.token.bearerAccessToken) + .map(consignment => + consignment.fileChecks.checksumProgress.filesProcessed == consignment.totalFiles && + consignment.fileChecks.ffidProgress.filesProcessed == consignment.totalFiles && + consignment.fileChecks.antivirusProgress.filesProcessed == consignment.totalFiles + ) + breakable { + for (_ <- 1 to intervals) { + if (Await.result(fileCheckProgress, 10.seconds)) { + break() + } else { + Thread.sleep(interval) } } } - - checkStatus(intervals) + Future.unit } } diff --git a/app/controllers/FileChecksResultsController.scala b/app/controllers/FileChecksResultsController.scala index d83c82189..7b722b888 100644 --- a/app/controllers/FileChecksResultsController.scala +++ b/app/controllers/FileChecksResultsController.scala @@ -32,7 +32,7 @@ class FileChecksResultsController @Inject() ( val pageTitle = "Results of your checks" for { - fileCheck <- consignmentService.fileCheckProgress(consignmentId, request.token.bearerAccessToken) + fileCheck <- consignmentService.getConsignmentFileChecks(consignmentId, request.token.bearerAccessToken) parentFolder = fileCheck.parentFolder.getOrElse(throw new IllegalStateException(s"No parent folder found for consignment: '$consignmentId'")) reference <- consignmentService.getConsignmentRef(consignmentId, request.token.bearerAccessToken) } yield { @@ -57,5 +57,38 @@ class FileChecksResultsController @Inject() ( } } } + + def judgmentFileCheckResultsPage(consignmentId: UUID): Action[AnyContent] = judgmentTypeAction(consignmentId) { implicit request: Request[AnyContent] => + val pageTitle = "Results of checks" + for { + consignmentStatuses <- consignmentStatusService.getConsignmentStatuses(consignmentId, request.token.bearerAccessToken) + reference <- consignmentService.getConsignmentRef(consignmentId, request.token.bearerAccessToken) + exportStatus = consignmentStatusService.getStatusValues(consignmentStatuses, ExportType).values.headOption.flatten + result <- exportStatus match { + case Some(InProgressValue.value) | Some(CompletedValue.value) | Some(FailedValue.value) => + Future(Ok(views.html.transferAlreadyCompleted(consignmentId, reference, request.token.name, isJudgmentUser = true)).uncache()) + case None => + for { + fileCheck <- consignmentService.getConsignmentFileChecks(consignmentId, request.token.bearerAccessToken) + result <- + if (fileCheck.allChecksSucceeded) { + val token: BearerAccessToken = request.token.bearerAccessToken + val legalCustodyTransferConfirmation = FinalTransferConfirmationData(transferLegalCustody = true) + for { + _ <- confirmTransferService.addFinalTransferConfirmation(consignmentId, token, legalCustodyTransferConfirmation) + _ <- consignmentExportService.updateTransferInitiated(consignmentId, request.token.bearerAccessToken) + _ <- consignmentExportService.triggerExport(consignmentId, request.token.bearerAccessToken.toString) + res <- Future(Redirect(routes.TransferCompleteController.judgmentTransferComplete(consignmentId)).uncache()) + } yield res + } else { + Future(Ok(views.html.fileChecksResultsFailed(request.token.name, pageTitle, reference, isJudgmentUser = true)).uncache()) + } + } yield result + case _ => + throw new IllegalStateException(s"Unexpected Export status: $exportStatus for consignment $consignmentId") + } + } yield result + } } + case class ConsignmentFolderInfo(numberOfFiles: Int, parentFolder: String) diff --git a/app/controllers/ViewTransfersController.scala b/app/controllers/ViewTransfersController.scala index 7e027279f..61989db62 100644 --- a/app/controllers/ViewTransfersController.scala +++ b/app/controllers/ViewTransfersController.scala @@ -132,7 +132,7 @@ class ViewTransfersController @Inject() ( } val resultsUrl = if (judgmentType) { - routes.FileChecksController.judgmentCompleteTransfer(consignmentId).url + routes.FileChecksResultsController.judgmentFileCheckResultsPage(consignmentId).url } else { routes.FileChecksResultsController.fileCheckResultsPage(consignmentId).url } diff --git a/app/services/ConsignmentService.scala b/app/services/ConsignmentService.scala index 4456f7821..55eac5e25 100644 --- a/app/services/ConsignmentService.scala +++ b/app/services/ConsignmentService.scala @@ -38,6 +38,7 @@ class ConsignmentService @Inject() (val graphqlConfiguration: GraphQLConfigurati private val getConsignmentClient = graphqlConfiguration.getClient[getConsignment.Data, getConsignment.Variables]() private val getConsignmentFilesMetadataClient = graphqlConfiguration.getClient[gcfm.Data, gcfm.Variables]() private val addConsignmentClient = graphqlConfiguration.getClient[addConsignment.Data, addConsignment.Variables]() + private val getConsignmentFileCheckClient = graphqlConfiguration.getClient[gfcp.Data, gfcp.Variables]() private val getConsignmentFolderDetailsClient = graphqlConfiguration.getClient[getConsignmentFolderDetails.Data, getConsignmentFolderDetails.Variables]() private val getConsignmentSummaryClient = graphqlConfiguration.getClient[getConsignmentSummary.Data, getConsignmentSummary.Variables]() private val getConsignmentReferenceClient = graphqlConfiguration.getClient[getConsignmentReference.Data, getConsignmentReference.Variables]() @@ -121,6 +122,13 @@ class ConsignmentService @Inject() (val graphqlConfiguration: GraphQLConfigurati .map(data => data.addConsignment) } + def getConsignmentFileChecks(consignmentId: UUID, token: BearerAccessToken): Future[gfcp.GetConsignment] = { + val variables: gfcp.Variables = new GetFileCheckProgress.getFileCheckProgress.Variables(consignmentId) + + sendApiRequest(getConsignmentFileCheckClient, gfcp.document, token, variables) + .map(data => data.getConsignment.get) + } + def getConsignmentFolderInfo(consignmentId: UUID, token: BearerAccessToken): Future[getConsignmentFolderDetails.GetConsignment] = { val variables: getConsignmentFolderDetails.Variables = new getConsignmentFolderDetails.Variables(consignmentId) diff --git a/app/views/fileChecksProgressAlreadyConfirmed.scala.html b/app/views/fileChecksProgressAlreadyConfirmed.scala.html index 2f42ba37f..d7706f09a 100644 --- a/app/views/fileChecksProgressAlreadyConfirmed.scala.html +++ b/app/views/fileChecksProgressAlreadyConfirmed.scala.html @@ -21,7 +21,7 @@

@title

Continue diff --git a/app/views/judgment/judgmentFileChecksProgress.scala.html b/app/views/judgment/judgmentFileChecksProgress.scala.html index 1b956fbb1..14313bcfe 100644 --- a/app/views/judgment/judgmentFileChecksProgress.scala.html +++ b/app/views/judgment/judgmentFileChecksProgress.scala.html @@ -20,7 +20,7 @@

Checking your upload

- @form(routes.FileChecksController.judgmentCompleteTransfer(consignmentId), Symbol("id") -> "continue-transfer") { } + @form(routes.FileChecksResultsController.judgmentFileCheckResultsPage(consignmentId), Symbol("id") -> "file-checks-form") { }
@transferReference(consignmentRef, isJudgmentUser = true) diff --git a/build.sbt b/build.sbt index 20aca6718..7c1ce4979 100644 --- a/build.sbt +++ b/build.sbt @@ -66,8 +66,3 @@ excludeDependencies ++= Seq( ) pipelineStages := Seq(digest) - -excludeDependencies ++= Seq( - ExclusionRule(organization = "com.typesafe.akka"), - ExclusionRule(organization = "com.typesafe.play") -) diff --git a/conf/application.base.conf b/conf/application.base.conf index 8f1da89c8..9d1001e11 100644 --- a/conf/application.base.conf +++ b/conf/application.base.conf @@ -66,5 +66,3 @@ draftMetadata { } notificationSnsTopicArn = ${NOTIFICATION_SNS_TOPIC_ARN} -fileChecksTotalTimoutInSeconds = 480 -fileChecksTotalTimoutInSeconds = ${?FILE_CHECKS_TOTAL_TIMEOUT_IN_SECONDS} diff --git a/conf/routes b/conf/routes index 02e6752ff..cd2d557be 100644 --- a/conf/routes +++ b/conf/routes @@ -75,7 +75,8 @@ GET /judgment/help GET /judgment/:consignmentId/before-uploading controllers.BeforeUploadingController.beforeUploading(consignmentId: java.util.UUID) GET /judgment/:consignmentId/upload controllers.UploadController.judgmentUploadPage(consignmentId: java.util.UUID) GET /judgment/:consignmentId/file-checks controllers.FileChecksController.judgmentFileChecksPage(consignmentId: java.util.UUID, uploadFailed: Option[String]) -GET /judgment/:consignmentId/continue-transfer controllers.FileChecksController.judgmentCompleteTransfer(consignmentId: java.util.UUID) +POST /judgment/:consignmentId/file-check-progress controllers.FileChecksController.fileCheckProgress(consignmentId: java.util.UUID) +GET /judgment/:consignmentId/file-checks-results controllers.FileChecksResultsController.judgmentFileCheckResultsPage(consignmentId: java.util.UUID) GET /judgment/:consignmentId/transfer-complete controllers.TransferCompleteController.judgmentTransferComplete(consignmentId: java.util.UUID) # Routes for TNA-User diff --git a/npm/src/checks/index.ts b/npm/src/checks/index.ts index 5302c5056..e06d3b0e0 100644 --- a/npm/src/checks/index.ts +++ b/npm/src/checks/index.ts @@ -19,23 +19,21 @@ export class Checks { isJudgmentUser: boolean, goToNextPage: (formId: string) => void ) => { - if (isJudgmentUser) { - goToNextPage("#continue-transfer") - } else { - const intervalId: ReturnType = setInterval(async () => { - const fileChecksProgress: IFileCheckProgress | Error = - await getFileChecksProgress() - if (!isError(fileChecksProgress)) { - const checksCompleted = haveFileChecksCompleted(fileChecksProgress) - if (checksCompleted) { - clearInterval(intervalId) - displayChecksCompletedBanner("file-checks") - } - } else { - return fileChecksProgress + const intervalId: ReturnType = setInterval(async () => { + const fileChecksProgress: IFileCheckProgress | Error = + await getFileChecksProgress() + if (!isError(fileChecksProgress)) { + const checksCompleted = haveFileChecksCompleted(fileChecksProgress) + if (checksCompleted) { + clearInterval(intervalId) + isJudgmentUser + ? goToNextPage("#file-checks-form") + : displayChecksCompletedBanner("file-checks") } - }, 5000) - } + } else { + return fileChecksProgress + } + }, 5000) } updateDraftMetadataValidationProgress: () => void | Error = () => { diff --git a/npm/test/update-check-progress.test.ts b/npm/test/update-check-progress.test.ts index 2e159dbe5..092bc489b 100644 --- a/npm/test/update-check-progress.test.ts +++ b/npm/test/update-check-progress.test.ts @@ -99,20 +99,6 @@ const mockDisplayChecksHaveCompletedBanner: () => void = () => () => {} ) -test("'updateFileCheckProgress' calls goToNextPage for a judgment user without checks", async () => { - const consignmentId = "e25438db-4bfb-41c9-8fff-6f2e4cca6421" - mockGetCheckProgress.getConsignmentId.mockImplementation( - () => consignmentId - ) - - checks.updateFileCheckProgress(true, mockGoToNextPage) - await jest.runOnlyPendingTimers() - - expect(mockGetCheckProgress.getFileChecksProgress).not.toHaveBeenCalled() - expect(haveFileChecksCompleted).not.toHaveBeenCalled() - expect(mockGoToNextPage).toHaveBeenCalled() -}) - test("'updateFileCheckProgress' calls setInterval correctly", async () => { jest.spyOn(global, "setInterval") await checks.updateFileCheckProgress(false, mockGoToNextPage) @@ -279,3 +265,58 @@ test("'updateDraftMetadataValidationProgress' shows a standard user, no banner a expect(hasDraftMetadataValidationCompleted).toBeCalled() expect(displayChecksCompletedBanner).not.toBeCalled() }) + +test("'updateFileCheckProgress' calls goToNextPage for a judgment user, if all checks are complete", async () => { + const consignmentId = "e25438db-4bfb-41c9-8fff-6f2e4cca6421" + mockGetCheckProgress.getConsignmentId.mockImplementation( + () => consignmentId + ) + + mockGetFileChecksProgress("complete") + + mockVerifyChecksHaveCompleted.haveFileChecksCompleted.mockImplementation( + () => true + ) + + checks.updateFileCheckProgress(true, mockGoToNextPage) + await jest.runOnlyPendingTimers() + + expect(haveFileChecksCompleted).toBeCalled() + expect(mockGoToNextPage).toHaveBeenCalled() +}) + +test("'updateFileCheckProgress' does not call goToNextPage for a judgment user if the checks are in progress", async () => { + const consignmentId = "e25438db-4bfb-41c9-8fff-6f2e4cca6421" + mockGetCheckProgress.getConsignmentId.mockImplementation( + () => consignmentId + ) + mockGetFileChecksProgress("inProgress") + + mockVerifyChecksHaveCompleted.haveFileChecksCompleted.mockImplementation( + () => false + ) + + checks.updateFileCheckProgress(true, mockGoToNextPage) + await jest.runOnlyPendingTimers() + + expect(haveFileChecksCompleted).toBeCalled() + expect(mockGoToNextPage).not.toHaveBeenCalled() +}) + +test("'updateFileCheckProgress' does not call goToNextPage for a judgment user if no file checks information is returned", async () => { + const consignmentId = "e25438db-4bfb-41c9-8fff-6f2e4cca6421" + mockGetCheckProgress.getConsignmentId.mockImplementation( + () => consignmentId + ) + mockGetFileChecksProgress("noData") + + mockVerifyChecksHaveCompleted.haveFileChecksCompleted.mockImplementation( + () => false + ) + + checks.updateFileCheckProgress(true, mockGoToNextPage) + await jest.runOnlyPendingTimers() + + expect(haveFileChecksCompleted).toBeCalled() + expect(mockGoToNextPage).not.toHaveBeenCalled() +}) diff --git a/test/controllers/FileChecksControllerSpec.scala b/test/controllers/FileChecksControllerSpec.scala index 86c4d644f..a44101107 100644 --- a/test/controllers/FileChecksControllerSpec.scala +++ b/test/controllers/FileChecksControllerSpec.scala @@ -1,10 +1,9 @@ package controllers -import cats.implicits.catsSyntaxOptionId import com.github.tomakehurst.wiremock.WireMockServer import com.github.tomakehurst.wiremock.client.WireMock._ import com.github.tomakehurst.wiremock.stubbing.Scenario -import configuration.{GraphQLConfiguration, KeycloakConfiguration} +import configuration.GraphQLConfiguration import graphql.codegen.GetConsignmentStatus.getConsignmentStatus.GetConsignment.ConsignmentStatuses import graphql.codegen.GetFileCheckProgress.{getFileCheckProgress => fileCheck} import io.circe.Printer @@ -12,17 +11,14 @@ import io.circe.generic.auto._ import io.circe.syntax._ import org.mockito.ArgumentMatchers.anyString import org.mockito.Mockito.when -import org.pac4j.play.scala.SecurityComponents import org.scalatest.concurrent.ScalaFutures.convertScalaFuture import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor1} -import play.api.Configuration -import play.api.libs.ws.WSClient import play.api.test.CSRFTokenHelper.CSRFRequest import play.api.test.FakeRequest -import play.api.test.Helpers.{status, status => playStatus, _} +import play.api.test.Helpers.{status => playStatus, _} import play.api.test.WsTestClient.InternalWSClient import services.Statuses.{CompletedValue, CompletedWithIssuesValue, UploadType} -import services.{BackendChecksService, ConfirmTransferService, ConsignmentExportService, ConsignmentService, ConsignmentStatusService} +import services.{BackendChecksService, ConsignmentService, ConsignmentStatusService} import testUtils.{CheckPageForStaticElements, FrontEndTestHelper} import java.time.{LocalDateTime, ZoneId, ZonedDateTime} @@ -57,35 +53,6 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper val checkPageForStaticElements = new CheckPageForStaticElements - def initialiseFileChecks( - keycloakConfiguration: KeycloakConfiguration, - controllerComponents: SecurityComponents = getAuthorisedSecurityComponents, - backendChecksService: Option[BackendChecksService] = None - ): FileChecksController = { - - val wsClient = mock[WSClient] - val config = mock[Configuration] - - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - val consignmentService = new ConsignmentService(graphQLConfiguration) - val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) - val backendChecks = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) - val confirmTransferService = new ConfirmTransferService(graphQLConfiguration) - val consignmentExportService = new ConsignmentExportService(wsClient, config, graphQLConfiguration) - - new FileChecksController( - controllerComponents, - graphQLConfiguration, - keycloakConfiguration, - consignmentService, - frontEndInfoConfiguration, - backendChecksService.getOrElse(backendChecks), - consignmentStatusService, - confirmTransferService, - consignmentExportService - ) - } - forAll(fileChecks) { userType => "FileChecksController GET" should { val (pathName, keycloakConfiguration, expectedTitle, expectedHeading, expectedInstruction, expectedForm) = if (userType == "judgment") { @@ -124,17 +91,34 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } s"render the $userType fileChecks page if the checks are incomplete" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) + val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) val filesProcessedWithAntivirus = 6 val filesProcessedWithChecksum = 12 val filesProcessedWithFFID = 8 val dataString: String = progressData(filesProcessedWithAntivirus, filesProcessedWithChecksum, filesProcessedWithFFID, allChecksSucceeded = false) val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, CompletedValue.value, someDateTime, None)) + if (userType == "judgment") { + val dataString2: String = progressData(40, 40, 40, allChecksSucceeded = true) + mockGetFileCheckProgressWithMultipleResults(dataString, dataString2, userType) + } else { + mockGetFileCheckProgress(dataString, userType) + } setConsignmentReferenceResponse(wiremockServer) setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - mockGetFileCheckProgress(dataString, userType) - val fileChecksController = initialiseFileChecks(keycloakConfiguration) + val fileChecksController = new FileChecksController( + getAuthorisedSecurityComponents, + graphQLConfiguration, + keycloakConfiguration, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) val uploadFailed = "false" val fileChecksPage = if (userType == "judgment") { fileChecksController @@ -148,17 +132,18 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper val fileChecksPageAsString = contentAsString(fileChecksPage) - playStatus(fileChecksPage) mustBe OK - contentType(fileChecksPage) mustBe Some("text/html") - headers(fileChecksPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksPageAsString, userType = userType) - fileChecksPageAsString must include(expectedTitle) - fileChecksPageAsString must include(expectedHeading) - fileChecksPageAsString must include(expectedInstruction) if (userType == "judgment") { - fileChecksPageAsString must include("""""") + playStatus(fileChecksPage) mustBe SEE_OTHER + redirectLocation(fileChecksPage) must be(Some(s"/judgment/$consignmentId/file-checks-results")) } else { + playStatus(fileChecksPage) mustBe OK + contentType(fileChecksPage) mustBe Some("text/html") + headers(fileChecksPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") + + checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksPageAsString, userType = userType) + fileChecksPageAsString must include(expectedTitle) + fileChecksPageAsString must include(expectedHeading) + fileChecksPageAsString must include(expectedInstruction) fileChecksPageAsString must include( """

For more information on these checks, please see our | FAQ (opens in new tab) for this service. @@ -180,7 +165,20 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } s"return a redirect to the auth server with an unauthenticated $userType user" in { - val controller = initialiseFileChecks(keycloakConfiguration, getUnauthorisedSecurityComponents) + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) + val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) + val controller = + new FileChecksController( + getUnauthorisedSecurityComponents, + graphQLConfiguration, + getValidKeycloakConfiguration, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) val fileChecksPage = controller.fileChecksPage(consignmentId, None).apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks")) playStatus(fileChecksPage) mustBe FOUND @@ -188,8 +186,11 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } s"render the $userType file checks complete page if the file checks are complete and all checks are successful" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) val backendChecksService = mock[BackendChecksService] - val dataString: String = progressData(filesProcessedWithAntivirus = 40, filesProcessedWithChecksum = 40, filesProcessedWithFFID = 40, allChecksSucceeded = true) + val dataString: String = progressData(40, 40, 40, allChecksSucceeded = true) val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, InProgress.value, someDateTime, None)) when(backendChecksService.triggerBackendChecks(org.mockito.ArgumentMatchers.any(classOf[UUID]), anyString())).thenReturn(Future.successful(true)) @@ -198,8 +199,15 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper setConsignmentReferenceResponse(wiremockServer) setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - val fileChecksController = initialiseFileChecks(keycloakConfiguration, backendChecksService = backendChecksService.some) - + val fileChecksController = new FileChecksController( + getAuthorisedSecurityComponents, + graphQLConfiguration, + keycloakConfiguration, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) val uploadFailed = "false" val fileChecksPage = if (userType == "judgment") { fileChecksController @@ -228,6 +236,10 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } s"render the $userType file checks complete page if the file checks are complete and all checks are not successful" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) + val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) val dataString: String = progressData(40, 40, 40, allChecksSucceeded = false) val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, CompletedValue.value, someDateTime, None)) @@ -235,8 +247,15 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) setConsignmentReferenceResponse(wiremockServer) - val controller = initialiseFileChecks(keycloakConfiguration) - + val controller = new FileChecksController( + getAuthorisedSecurityComponents, + graphQLConfiguration, + keycloakConfiguration, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) val fileChecksCompletePage = if (userType == "judgment") { controller.judgmentFileChecksPage(consignmentId, None).apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks")) } else { @@ -257,7 +276,12 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } s"render the $userType error page if the user upload fails and ensure consignment status is updated" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) + val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) val filesProcessedWithAntivirus = 6 + val filesProcessedWithChecksum = 12 val filesProcessedWithFFID = 8 val dataString: String = progressData(filesProcessedWithAntivirus, filesProcessedWithChecksum, filesProcessedWithFFID, allChecksSucceeded = false) @@ -268,8 +292,15 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper setConsignmentReferenceResponse(wiremockServer) setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - val fileChecksController = initialiseFileChecks(keycloakConfiguration) - + val fileChecksController = new FileChecksController( + getAuthorisedSecurityComponents, + graphQLConfiguration, + keycloakConfiguration, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) val uploadFailed = "true" val fileChecksPage = if (userType == "judgment") { fileChecksController @@ -299,6 +330,9 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } s"render the $userType error page if the user upload succeeds but backendChecks fails to trigger" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) val backendChecksService = mock[BackendChecksService] val filesProcessedWithAntivirus = 6 @@ -313,8 +347,15 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper setConsignmentReferenceResponse(wiremockServer) setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - val fileChecksController = initialiseFileChecks(keycloakConfiguration, backendChecksService = backendChecksService.some) - + val fileChecksController = new FileChecksController( + getAuthorisedSecurityComponents, + graphQLConfiguration, + keycloakConfiguration, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) val uploadFailed = "false" val fileChecksPage = if (userType == "judgment") { fileChecksController @@ -344,6 +385,9 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } s"render the $userType error page if the user upload status is 'CompletedWithIssues'" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) val backendChecksService = mock[BackendChecksService] val filesProcessedWithAntivirus = 6 @@ -358,8 +402,15 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper setConsignmentReferenceResponse(wiremockServer) setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) - val fileChecksController = initialiseFileChecks(keycloakConfiguration) - + val fileChecksController = new FileChecksController( + getAuthorisedSecurityComponents, + graphQLConfiguration, + keycloakConfiguration, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) val uploadFailed = "false" val fileChecksPage = if (userType == "judgment") { fileChecksController @@ -393,7 +444,19 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper "FileChecksController fileCheckProgress" should { "call the fileCheckProgress endpoint" in { - val controller = initialiseFileChecks(getValidKeycloakConfiguration) + val graphQLConfiguration: GraphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService: ConsignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) + val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) + val controller = new FileChecksController( + getAuthorisedSecurityComponents, + graphQLConfiguration, + getValidStandardUserKeycloakConfiguration, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) mockGetFileCheckProgress(progressData(1, 1, 1, allChecksSucceeded = true), "standard") @@ -407,7 +470,19 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } "throw an error if the API returns an error" in { - val controller = initialiseFileChecks(getValidKeycloakConfiguration) + val graphQLConfiguration: GraphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService: ConsignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) + val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) + val controller = new FileChecksController( + getAuthorisedSecurityComponents, + graphQLConfiguration, + getValidStandardUserKeycloakConfiguration, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) wiremockServer.stubFor( post(urlEqualTo("/graphql")) @@ -424,157 +499,53 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } } - "FileChecksController judgmentCompleteTransfer" should { - - s"return a redirect to the auth server if an unauthenticated user tries to access the judgment complete transfer page" in { - val fileChecksController = initialiseFileChecks(getValidJudgmentUserKeycloakConfiguration, getUnauthorisedSecurityComponents) - val recordChecksResultsPage = fileChecksController - .judgmentCompleteTransfer(consignmentId) - .apply(FakeRequest(GET, s"/judgment/$consignmentId/continue-transfer")) - - status(recordChecksResultsPage) mustBe FOUND - redirectLocation(recordChecksResultsPage).get must startWith("/auth/realms/tdr/protocol/openid-connect/auth") - } - - s"return the judgment error page if some file checks have failed" in { - - setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer) - val dataString: String = progressData(filesProcessedWithAntivirus = 40, filesProcessedWithChecksum = 40, filesProcessedWithFFID = 40, allChecksSucceeded = false) - mockGetFileCheckProgress(dataString, "judgment") - - val fileChecksController = initialiseFileChecks(getValidKeycloakConfiguration) - val recordCheckResultsPage = fileChecksController.judgmentCompleteTransfer(consignmentId).apply(FakeRequest(GET, s"/judgment/$consignmentId/continue-transfer")) - val resultsPageAsString = contentAsString(recordCheckResultsPage) - - status(recordCheckResultsPage) mustBe OK - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = "judgment") - verifyFileChecksResultPage(resultsPageAsString) - } - - s"return the judgment error page if file checks have failed with PasswordProtected" in { - setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer) - val fileStatus = "PasswordProtected" - val dataString: String = - progressData(filesProcessedWithAntivirus = 40, filesProcessedWithChecksum = 40, filesProcessedWithFFID = 40, allChecksSucceeded = false, List(fileStatus)) - mockGetFileCheckProgress(dataString, "judgment") - - val fileChecksController = initialiseFileChecks(getValidKeycloakConfiguration) - val recordCheckResultsPage = fileChecksController.judgmentCompleteTransfer(consignmentId).apply(FakeRequest(GET, s"/judgment/$consignmentId/continue-transfer")) - - val resultsPageAsString = contentAsString(recordCheckResultsPage) - - status(recordCheckResultsPage) mustBe OK - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = "judgment") - verifyFileChecksResultPage(resultsPageAsString) - } - - s"return the judgment error page if file checks have failed with Zip" in { - setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer) - val fileStatus = "Zip" - val dataString: String = - progressData(filesProcessedWithAntivirus = 40, filesProcessedWithChecksum = 40, filesProcessedWithFFID = 40, allChecksSucceeded = false, List(fileStatus)) - mockGetFileCheckProgress(dataString, "judgment") - - val fileChecksController = initialiseFileChecks(getValidKeycloakConfiguration) - val recordCheckResultsPage = fileChecksController.judgmentCompleteTransfer(consignmentId).apply(FakeRequest(GET, s"/judgment/$consignmentId/continue-transfer")) - - val resultsPageAsString = contentAsString(recordCheckResultsPage) - - status(recordCheckResultsPage) mustBe OK - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = "judgment") - verifyFileChecksResultPage(resultsPageAsString) - } - - s"return the judgment error page if file checks have failed with PasswordProtected and Zip" in { - setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer) - val fileStatuses = List("PasswordProtected", "Zip") - val dataString: String = - progressData(filesProcessedWithAntivirus = 40, filesProcessedWithChecksum = 40, filesProcessedWithFFID = 40, allChecksSucceeded = false, fileStatuses) - mockGetFileCheckProgress(dataString, "judgment") - - val fileChecksController = initialiseFileChecks(getValidKeycloakConfiguration) - val recordCheckResultsPage = fileChecksController.judgmentCompleteTransfer(consignmentId).apply(FakeRequest(GET, s"/judgment/$consignmentId/continue-transfer")) - - val resultsPageAsString = contentAsString(recordCheckResultsPage) - - status(recordCheckResultsPage) mustBe OK - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = "judgment") - verifyFileChecksResultPage(resultsPageAsString) - } - } - - private def verifyFileChecksResultPage(resultsPageAsString: String) = { - val expectedFailureReturnButton: String = - """ - | Return to start - | """.stripMargin - - val expectedFailureTitle: String = - """

- | There is a problem - |

""".stripMargin - - val expectedTitle = "Results of checks - Transfer Digital Records - GOV.UK" - val expectedHeading = """

Results of checks

""" - val expectedGenericErrorMessage = """

Your file has failed our checks. Please try again. If this continues, contact us at - | - | nationalArchives.email - | - |

""".stripMargin - - resultsPageAsString must include(expectedTitle) - resultsPageAsString must include(expectedHeading) - resultsPageAsString must include(expectedFailureTitle) - resultsPageAsString must include(expectedGenericErrorMessage) - resultsPageAsString must include(expectedFailureReturnButton) - } - - forAll(consignmentStatuses) { consignmentStatus => - s"render the 'transfer has already been confirmed' page with an authenticated judgment user if export status is '$consignmentStatus'" in { - val consignmentId = UUID.fromString("c2efd3e6-6664-4582-8c28-dcf891f60e68") - val userType = "judgment" - val fileChecksController = initialiseFileChecks(getValidJudgmentUserKeycloakConfiguration) - val consignmentStatuses = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), "Export", consignmentStatus, someDateTime, None)) - setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = consignmentStatuses) - setConsignmentTypeResponse(wiremockServer, userType) - setConsignmentReferenceResponse(wiremockServer) - - val transferAlreadyCompletedPage = - fileChecksController.judgmentCompleteTransfer(consignmentId).apply(FakeRequest(GET, s"/judgment/$consignmentId/continue-transfer").withCSRFToken) - - val transferAlreadyCompletedPageAsString = contentAsString(transferAlreadyCompletedPage) - - status(transferAlreadyCompletedPage) mustBe OK - contentType(transferAlreadyCompletedPage) mustBe Some("text/html") - headers(transferAlreadyCompletedPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(transferAlreadyCompletedPageAsString, userType = userType) - transferAlreadyCompletedPageAsString must include("Your transfer has already been completed - Transfer Digital Records - GOV.UK") - transferAlreadyCompletedPageAsString must include("""

Your transfer has already been completed

""") - transferAlreadyCompletedPageAsString must include("""

Click 'Continue' to see the confirmation page again or return to the start.

""") - transferAlreadyCompletedPageAsString must include(s"""href="/$userType/$consignmentId/transfer-complete">Continue""") - } + private def mockGetFileCheckProgress(dataString: String, userType: String) = { + setConsignmentTypeResponse(wiremockServer, userType) + wiremockServer.stubFor( + post(urlEqualTo("/graphql")) + .withRequestBody(containing("getFileCheckProgress")) + .willReturn(okJson(dataString)) + ) } - private def mockGetFileCheckProgress(dataString: String, userType: String) = { + private def mockGetFileCheckProgressWithMultipleResults(dataString: String, dataString2: String, userType: String) = { setConsignmentTypeResponse(wiremockServer, userType) + wiremockServer.stubFor( post(urlEqualTo("/graphql")) + .inScenario("FileChecksController GET should render the judgment fileChecks page if the checks are incomplete") .withRequestBody(containing("getFileCheckProgress")) + .whenScenarioStateIs(Scenario.STARTED) .willReturn(okJson(dataString)) + .willSetStateTo("Second call") + ) + + wiremockServer.stubFor( + post(urlEqualTo("/graphql")) + .inScenario("FileChecksController GET should render the judgment fileChecks page if the checks are incomplete") + .withRequestBody(containing("getFileCheckProgress")) + .whenScenarioStateIs("Second call") + .willReturn(okJson(dataString2)) ) } forAll(userChecks) { (user, url) => s"The $url upload page" should { s"return 403 if the url doesn't match the consignment type" in { - val controller = initialiseFileChecks(getValidKeycloakConfiguration) + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + val consignmentService = new ConsignmentService(graphQLConfiguration) + val consignmentStatusService = new ConsignmentStatusService(graphQLConfiguration) + val backendChecksService = new BackendChecksService(new InternalWSClient("http", 9007), app.configuration) + + val controller = new FileChecksController( + getAuthorisedSecurityComponents, + graphQLConfiguration, + user, + consignmentService, + frontEndInfoConfiguration, + backendChecksService, + consignmentStatusService + ) val fileChecksPage = url match { case "judgment" => @@ -593,24 +564,18 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper } } - private def progressData( - filesProcessedWithAntivirus: Int, - filesProcessedWithChecksum: Int, - filesProcessedWithFFID: Int, - allChecksSucceeded: Boolean, - fileStatusList: List[String] = List("Success") - ): String = { + private def progressData(filesProcessedWithAntivirus: Int, filesProcessedWithChecksum: Int, filesProcessedWithFFID: Int, allChecksSucceeded: Boolean): String = { val client = new GraphQLConfiguration(app.configuration).getClient[fileCheck.Data, fileCheck.Variables]() val antivirusProgress = fileCheck.GetConsignment.FileChecks.AntivirusProgress(filesProcessedWithAntivirus) val checksumProgress = fileCheck.GetConsignment.FileChecks.ChecksumProgress(filesProcessedWithChecksum) val ffidProgress = fileCheck.GetConsignment.FileChecks.FfidProgress(filesProcessedWithFFID) val fileChecks = fileCheck.GetConsignment.FileChecks(antivirusProgress, checksumProgress, ffidProgress) - val fileStatuses = fileStatusList.map(fileStatus => fileCheck.GetConsignment.Files(Some(fileStatus))) + val fileStatus = List(fileCheck.GetConsignment.Files(Some("Success"))) val data: client.GraphqlData = client.GraphqlData( Some( fileCheck.Data( Some( - fileCheck.GetConsignment(allChecksSucceeded, Option(""), totalFiles, fileStatuses, fileChecks) + fileCheck.GetConsignment(allChecksSucceeded, Option(""), totalFiles, fileStatus, fileChecks) ) ) ) diff --git a/test/controllers/FileChecksResultsControllerSpec.scala b/test/controllers/FileChecksResultsControllerSpec.scala index 573162e30..00495a01f 100644 --- a/test/controllers/FileChecksResultsControllerSpec.scala +++ b/test/controllers/FileChecksResultsControllerSpec.scala @@ -5,6 +5,7 @@ import configuration.{ApplicationConfig, GraphQLConfiguration, KeycloakConfigura import graphql.codegen.GetConsignmentFiles.getConsignmentFiles.GetConsignment.Files import graphql.codegen.GetConsignmentFiles.getConsignmentFiles.GetConsignment.Files.Metadata import graphql.codegen.GetConsignmentFiles.{getConsignmentFiles => gcf} +import graphql.codegen.GetConsignmentStatus.getConsignmentStatus.GetConsignment.ConsignmentStatuses import graphql.codegen.GetFileCheckProgress.getFileCheckProgress.GetConsignment.FileChecks import graphql.codegen.GetFileCheckProgress.getFileCheckProgress.GetConsignment.FileChecks.{AntivirusProgress, ChecksumProgress, FfidProgress} import graphql.codegen.GetFileCheckProgress.getFileCheckProgress.{Data, GetConsignment, Variables} @@ -23,9 +24,11 @@ import play.api.test.Helpers._ import play.api.test.WsTestClient.InternalWSClient import services.{ConfirmTransferService, ConsignmentExportService, ConsignmentService, ConsignmentStatusService} import testUtils.{CheckPageForStaticElements, FrontEndTestHelper} +import uk.gov.nationalarchives.tdr.GraphQLClient import java.time.{LocalDateTime, ZoneId, ZonedDateTime} import java.util.UUID +import scala.collection.immutable.TreeMap import scala.concurrent.ExecutionContext class FileChecksResultsControllerSpec extends FrontEndTestHelper { @@ -73,20 +76,6 @@ class FileChecksResultsControllerSpec extends FrontEndTestHelper { """

| There is a problem |

""".stripMargin - val expectedTitle = "Results of your checks - Transfer Digital Records - GOV.UK" - val expectedHeading = """

Results of your checks

""" - val expectedGenericErrorMessage = """

- | One or more files you uploaded have failed our checks. Contact us at - | nationalArchives.email - | if the problem persists. - |

- |

Possible failure causes:

- |
    - |
  • Password protected files
  • - |
  • Zip files
  • - |
  • Corrupted files
  • - |
  • Ambiguous naming of redacted files
  • - |
""".stripMargin "FileChecksResultsController GET after file check success" should { "render the fileChecksResults page with the confirmation box for a standard user" in { @@ -106,7 +95,7 @@ class FileChecksResultsControllerSpec extends FrontEndTestHelper { val recordCheckResultsPage = fileCheckResultsController .fileCheckResultsPage(consignmentId) - .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks-results").withCSRFToken) + .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks").withCSRFToken) val resultsPageAsString = contentAsString(recordCheckResultsPage) @@ -122,140 +111,253 @@ class FileChecksResultsControllerSpec extends FrontEndTestHelper { resultsPageAsString must include(expectedSuccessMessage) resultsPageAsString must include regex buttonToProgress } - } - - "FileChecksResultsController GET" should { - s"return a redirect to the auth server if an unauthenticated user tries to access the standard file checks page" in { - val fileCheckResultsController = instantiateController(getUnauthorisedSecurityComponents, getValidStandardUserKeycloakConfiguration) - val recordChecksResultsPage = fileCheckResultsController - .fileCheckResultsPage(consignmentId) - .apply(FakeRequest(GET, s"consignment/$consignmentId/file-checks-results")) + "return a redirect to judgments checked passed url for a judgements user" in { + val fileCheckResultsController = setUpFileChecksController("judgment", getValidJudgmentUserKeycloakConfiguration) - status(recordChecksResultsPage) mustBe FOUND - redirectLocation(recordChecksResultsPage).get must startWith("/auth/realms/tdr/protocol/openid-connect/auth") - } + // Export status not set + val consignmentStatuses = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), "Upload", "Completed", someDateTime, None)) + setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = consignmentStatuses) + setConsignmentTypeResponse(wiremockServer, "judgment") + setConsignmentReferenceResponse(wiremockServer) - s"return an error if an authenticated user tries to get information for a consignment they don't own from the standard file checks page" in { - val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, getValidStandardUserKeycloakConfiguration) - val exampleApiResponse = "{\"fileChecksData\":{" + - "\"getConsignment\":null}," + - "\"errors\":[{" + - "\"message\":\"User '7bee3c41-c059-46f6-8e9b-9ba44b0489b7' does not own consignment '0a3f617c-04e8-41c2-9f24-99622a779528'\"," + - "\"path\":[\"getConsignment\"],\"locations\":[{" + - "\"column\":3,\"line\":2}]," + - "\"extensions\":{" + - "\"code\":\"NOT_AUTHORISED\"}}]}" + // final transfer configuration + stubFinalTransferConfirmationResponseWithServer(wiremockServer, consignmentId) - wiremockServer.stubFor( - post(urlEqualTo("/graphql")) - .willReturn(okJson(exampleApiResponse)) + // initiated transfer + stubUpdateTransferInitiatedResponse(wiremockServer, consignmentId) + // start export + wiremockExportServer.stubFor( + post(urlEqualTo(s"/export/$consignmentId")) + .willReturn(ok()) ) - val results: Throwable = fileCheckResultsController - .fileCheckResultsPage(consignmentId) - .apply( - FakeRequest(GET, s"consignment/$consignmentId/file-checks-results") - ) - .failed - .futureValue - - results.getMessage mustBe "User '7bee3c41-c059-46f6-8e9b-9ba44b0489b7' does not own consignment '0a3f617c-04e8-41c2-9f24-99622a779528'" + val recordCheckResultsPage = fileCheckResultsController + .judgmentFileCheckResultsPage(consignmentId) + .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks").withCSRFToken) + status(recordCheckResultsPage) mustBe 303 + redirectLocation(recordCheckResultsPage).get must be("/judgment/0a3f617c-04e8-41c2-9f24-99622a779528/transfer-complete") } + "render an error when there is an error from the api for judgment after file checks passed" in { + val fileCheckResultsController = setUpFileChecksController("judgment", getValidJudgmentUserKeycloakConfiguration) - s"return the standard error page if some file checks have failed" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + // Export status not set + val consignmentStatuses = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), "Upload", "Completed", someDateTime, None)) + setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = consignmentStatuses) + setConsignmentTypeResponse(wiremockServer, "judgment") + setConsignmentReferenceResponse(wiremockServer) - setConsignmentStatusResponse(app.configuration, wiremockServer) - val fileStatus = List(gfcp.GetConsignment.Files(Some("fileStatusValue"))) + // final transfer configuration with errors + stubFinalTransferConfirmationResponseWithServer(wiremockServer, consignmentId, List(GraphQLClient.Error("Error", Nil, Nil, None))) - val data = Data( - Option( - GetConsignment(allChecksSucceeded = false, Option("parentFolder"), 1, fileStatus, FileChecks(AntivirusProgress(1), ChecksumProgress(1), FfidProgress(1))) - ) + // initiated transfer + stubUpdateTransferInitiatedResponse(wiremockServer, consignmentId) + // start export + wiremockExportServer.stubFor( + post(urlEqualTo(s"/export/$consignmentId")) + .willReturn(ok()) ) - val client = graphQLConfiguration.getClient[Data, Variables]() - val fileStatusResponse: String = client.GraphqlData(Option(data), List()).asJson.printWith(Printer(dropNullValues = false, "")) - mockGraphqlResponse("standard", fileStatusResponse) - setConsignmentReferenceResponse(wiremockServer) - - val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, getValidStandardUserKeycloakConfiguration) val recordCheckResultsPage = fileCheckResultsController - .fileCheckResultsPage(consignmentId) - .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks-results")) - val resultsPageAsString = contentAsString(recordCheckResultsPage) + .judgmentFileCheckResultsPage(consignmentId) + .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks").withCSRFToken) - status(recordCheckResultsPage) mustBe OK - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = "standard") - resultsPageAsString must include(expectedTitle) - resultsPageAsString must include(expectedHeading) - resultsPageAsString must include(expectedFailureTitle) - resultsPageAsString must include(expectedGenericErrorMessage) - resultsPageAsString must include(expectedFailureReturnButton) + val failure: Throwable = recordCheckResultsPage.failed.futureValue + failure mustBe an[Exception] } + } - s"return the passwordProtected standard error page if file checks have failed with PasswordProtected" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - setConsignmentStatusResponse(app.configuration, wiremockServer) - val fileStatus = List(gfcp.GetConsignment.Files(Some("PasswordProtected"))) + forAll(userTypes) { userType => + "FileChecksResultsController GET" should { + + val (pathName, keycloakConfiguration, expectedTitle, expectedHeading, expectedSuccessMessage, buttonToProgress, expectedGenericErrorMessage) = if (userType == "judgment") { + ( + "judgment", + getValidJudgmentUserKeycloakConfiguration, + "Results of checks - Transfer Digital Records - GOV.UK", + """

Results of checks

""", + s"""

Your uploaded file 'test file.docx' has now been validated.

+ |

Click 'Continue' to transfer it to The National Archives.

""".stripMargin, + s"""
+ | + | + |
""".stripMargin, + """

Your file has failed our checks. Please try again. If this continues, contact us at + | + | nationalArchives.email + | + |

""".stripMargin + ) + } else { + ( + "consignment", + getValidStandardUserKeycloakConfiguration, + "Results of your checks - Transfer Digital Records - GOV.UK", + """

Results of your checks

""", + """

+ | Your folder 'parentFolder' containing 1 record has been uploaded and checked. + |

+ |

You can leave and return to this upload at any time from the View transfers page.

""".stripMargin, + s""" + | Next + | """.stripMargin, + """

+ | One or more files you uploaded have failed our checks. Contact us at + | nationalArchives.email + | if the problem persists. + |

+ |

Possible failure causes:

+ |
    + |
  • Password protected files
  • + |
  • Zip files
  • + |
  • Corrupted files
  • + |
  • Ambiguous naming of redacted files
  • + |
""".stripMargin + ) + } - val data = Data( - Option( - GetConsignment(allChecksSucceeded = false, Option("parentFolder"), 1, fileStatus, FileChecks(AntivirusProgress(1), ChecksumProgress(1), FfidProgress(1))) + s"return a redirect to the auth server if an unauthenticated user tries to access the $userType file checks page" in { + val fileCheckResultsController = instantiateController(getUnauthorisedSecurityComponents, getValidKeycloakConfiguration) + val recordChecksResultsPage = fileCheckResultsController + .fileCheckResultsPage(consignmentId) + .apply(FakeRequest(GET, s"consignment/$consignmentId/file-checks-results")) + + status(recordChecksResultsPage) mustBe FOUND + redirectLocation(recordChecksResultsPage).get must startWith("/auth/realms/tdr/protocol/openid-connect/auth") + } + + s"return an error if an authenticated user tries to get information for a consignment they don't own from the $userType file checks page" in { + val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, getValidKeycloakConfiguration) + val exampleApiResponse = "{\"fileChecksData\":{" + + "\"getConsignment\":null}," + + "\"errors\":[{" + + "\"message\":\"User '7bee3c41-c059-46f6-8e9b-9ba44b0489b7' does not own consignment '0a3f617c-04e8-41c2-9f24-99622a779528'\"," + + "\"path\":[\"getConsignment\"],\"locations\":[{" + + "\"column\":3,\"line\":2}]," + + "\"extensions\":{" + + "\"code\":\"NOT_AUTHORISED\"}}]}" + + wiremockServer.stubFor( + post(urlEqualTo("/graphql")) + .willReturn(okJson(exampleApiResponse)) ) - ) - val client = graphQLConfiguration.getClient[Data, Variables]() - val fileStatusResponse: String = client.GraphqlData(Option(data), List()).asJson.printWith(Printer(dropNullValues = false, "")) - mockGraphqlResponse("standard", fileStatusResponse) - setConsignmentReferenceResponse(wiremockServer) + val results: Throwable = fileCheckResultsController + .fileCheckResultsPage(consignmentId) + .apply( + FakeRequest(GET, s"consignment/$consignmentId/file-checks-results") + ) + .failed + .futureValue - val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, getValidStandardUserKeycloakConfiguration) - val recordCheckResultsPage = fileCheckResultsController - .fileCheckResultsPage(consignmentId) - .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks-results")) - val resultsPageAsString = contentAsString(recordCheckResultsPage) + results.getMessage mustBe "User '7bee3c41-c059-46f6-8e9b-9ba44b0489b7' does not own consignment '0a3f617c-04e8-41c2-9f24-99622a779528'" + } - resultsPageAsString must include( - """

Your folder contains one or more password protected files.

""" + - """

We cannot accept password protected files. Once removed or replaced, try uploading your folder again.

""" - ) + s"return the $userType error page if some file checks have failed" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - status(recordCheckResultsPage) mustBe OK + setConsignmentStatusResponse(app.configuration, wiremockServer) + val fileStatus = List(gfcp.GetConsignment.Files(Some("fileStatusValue"))) - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = "standard") - resultsPageAsString must include(expectedTitle) - resultsPageAsString must include(expectedHeading) - resultsPageAsString must include(expectedFailureTitle) - resultsPageAsString must include(expectedFailureReturnButton) - } - - s"return the zip standard error page if file checks have failed with Zip" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - setConsignmentStatusResponse(app.configuration, wiremockServer) - val fileStatus = List(gfcp.GetConsignment.Files(Some("Zip"))) - - val data = Data( - Option( - GetConsignment(allChecksSucceeded = false, Option("parentFolder"), 1, fileStatus, FileChecks(AntivirusProgress(1), ChecksumProgress(1), FfidProgress(1))) + val data = Data( + Option( + GetConsignment(allChecksSucceeded = false, Option("parentFolder"), 1, fileStatus, FileChecks(AntivirusProgress(1), ChecksumProgress(1), FfidProgress(1))) + ) ) - ) - val client = graphQLConfiguration.getClient[Data, Variables]() - val fileStatusResponse: String = client.GraphqlData(Option(data), List()).asJson.printWith(Printer(dropNullValues = false, "")) - - mockGraphqlResponse("standard", fileStatusResponse) - setConsignmentReferenceResponse(wiremockServer) - - val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, getValidStandardUserKeycloakConfiguration) - val recordCheckResultsPage = fileCheckResultsController - .fileCheckResultsPage(consignmentId) - .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks-results")) - val resultsPageAsString = contentAsString(recordCheckResultsPage) - - resultsPageAsString must include( - """

Your folder contains one or more zip files.

+ val client = graphQLConfiguration.getClient[Data, Variables]() + val fileStatusResponse: String = client.GraphqlData(Option(data), List()).asJson.printWith(Printer(dropNullValues = false, "")) + + mockGraphqlResponse(userType, fileStatusResponse) + setConsignmentReferenceResponse(wiremockServer) + + val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, keycloakConfiguration) + val recordCheckResultsPage = { + if (userType == "judgment") { fileCheckResultsController.judgmentFileCheckResultsPage(consignmentId) } + else { fileCheckResultsController.fileCheckResultsPage(consignmentId) } + }.apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks")) + val resultsPageAsString = contentAsString(recordCheckResultsPage) + + status(recordCheckResultsPage) mustBe OK + + checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = userType) + resultsPageAsString must include(expectedTitle) + resultsPageAsString must include(expectedHeading) + resultsPageAsString must include(expectedFailureTitle) + resultsPageAsString must include(expectedGenericErrorMessage) + resultsPageAsString must include(expectedFailureReturnButton) + } + + s"return the passwordProtected $userType error page if file checks have failed with PasswordProtected" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + setConsignmentStatusResponse(app.configuration, wiremockServer) + val fileStatus = List(gfcp.GetConsignment.Files(Some("PasswordProtected"))) + + val data = Data( + Option( + GetConsignment(allChecksSucceeded = false, Option("parentFolder"), 1, fileStatus, FileChecks(AntivirusProgress(1), ChecksumProgress(1), FfidProgress(1))) + ) + ) + val client = graphQLConfiguration.getClient[Data, Variables]() + val fileStatusResponse: String = client.GraphqlData(Option(data), List()).asJson.printWith(Printer(dropNullValues = false, "")) + + mockGraphqlResponse(userType, fileStatusResponse) + setConsignmentReferenceResponse(wiremockServer) + + val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, keycloakConfiguration) + val recordCheckResultsPage = { + if (userType == "judgment") { fileCheckResultsController.judgmentFileCheckResultsPage(consignmentId) } + else { fileCheckResultsController.fileCheckResultsPage(consignmentId) } + }.apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks")) + val resultsPageAsString = contentAsString(recordCheckResultsPage) + + if (userType == "judgment") { + resultsPageAsString must include(expectedGenericErrorMessage) + } else { + resultsPageAsString must include( + """

Your folder contains one or more password protected files.

""" + + """

We cannot accept password protected files. Once removed or replaced, try uploading your folder again.

""" + ) + } + + status(recordCheckResultsPage) mustBe OK + + checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = userType) + resultsPageAsString must include(expectedTitle) + resultsPageAsString must include(expectedHeading) + resultsPageAsString must include(expectedFailureTitle) + resultsPageAsString must include(expectedFailureReturnButton) + } + + s"return the zip $userType error page if file checks have failed with Zip" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + setConsignmentStatusResponse(app.configuration, wiremockServer) + val fileStatus = List(gfcp.GetConsignment.Files(Some("Zip"))) + + val data = Data( + Option( + GetConsignment(allChecksSucceeded = false, Option("parentFolder"), 1, fileStatus, FileChecks(AntivirusProgress(1), ChecksumProgress(1), FfidProgress(1))) + ) + ) + val client = graphQLConfiguration.getClient[Data, Variables]() + val fileStatusResponse: String = client.GraphqlData(Option(data), List()).asJson.printWith(Printer(dropNullValues = false, "")) + + mockGraphqlResponse(userType, fileStatusResponse) + setConsignmentReferenceResponse(wiremockServer) + + val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, keycloakConfiguration) + val recordCheckResultsPage = { + if (userType == "judgment") { fileCheckResultsController.judgmentFileCheckResultsPage(consignmentId) } + else { fileCheckResultsController.fileCheckResultsPage(consignmentId) } + }.apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks")) + val resultsPageAsString = contentAsString(recordCheckResultsPage) + + if (userType == "judgment") { + resultsPageAsString must include(expectedGenericErrorMessage) + } else { + resultsPageAsString must include( + """

Your folder contains one or more zip files.

| We cannot accept zip files and similar archival package file formats. | These commonly have file extensions such as .zip, .iso, .7z, .rar and others. | please see our @@ -265,58 +367,99 @@ class FileChecksResultsControllerSpec extends FrontEndTestHelper { | for a full list. | Either remove or unpack your zip and archival package files and try uploading again. |

""".stripMargin - ) - - status(recordCheckResultsPage) mustBe OK - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = "standard") - resultsPageAsString must include(expectedTitle) - resultsPageAsString must include(expectedHeading) - resultsPageAsString must include(expectedFailureTitle) - resultsPageAsString must include(expectedFailureReturnButton) - } - - s"return the general standard error page if file checks have failed with PasswordProtected and Zip" in { - val graphQLConfiguration = new GraphQLConfiguration(app.configuration) - setConsignmentStatusResponse(app.configuration, wiremockServer) - val fileStatus = List(gfcp.GetConsignment.Files(Some("PasswordProtected")), gfcp.GetConsignment.Files(Some("Zip"))) - - val data = Data( - Option( - GetConsignment(allChecksSucceeded = false, Option("parentFolder"), 1, fileStatus, FileChecks(AntivirusProgress(1), ChecksumProgress(1), FfidProgress(1))) + ) + } + + status(recordCheckResultsPage) mustBe OK + + checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = userType) + resultsPageAsString must include(expectedTitle) + resultsPageAsString must include(expectedHeading) + resultsPageAsString must include(expectedFailureTitle) + resultsPageAsString must include(expectedFailureReturnButton) + } + + s"return the general $userType error page if file checks have failed with PasswordProtected and Zip" in { + val graphQLConfiguration = new GraphQLConfiguration(app.configuration) + setConsignmentStatusResponse(app.configuration, wiremockServer) + val fileStatus = List(gfcp.GetConsignment.Files(Some("PasswordProtected")), gfcp.GetConsignment.Files(Some("Zip"))) + + val data = Data( + Option( + GetConsignment(allChecksSucceeded = false, Option("parentFolder"), 1, fileStatus, FileChecks(AntivirusProgress(1), ChecksumProgress(1), FfidProgress(1))) + ) ) - ) - val client = graphQLConfiguration.getClient[Data, Variables]() - val fileStatusResponse: String = client.GraphqlData(Option(data), List()).asJson.printWith(Printer(dropNullValues = false, "")) + val client = graphQLConfiguration.getClient[Data, Variables]() + val fileStatusResponse: String = client.GraphqlData(Option(data), List()).asJson.printWith(Printer(dropNullValues = false, "")) + + mockGraphqlResponse(userType, fileStatusResponse) + setConsignmentReferenceResponse(wiremockServer) + + val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, keycloakConfiguration) + val recordCheckResultsPage = { + if (userType == "judgment") { fileCheckResultsController.judgmentFileCheckResultsPage(consignmentId) } + else { fileCheckResultsController.fileCheckResultsPage(consignmentId) } + }.apply(FakeRequest(GET, s"/$pathName/$consignmentId/file-checks")) + val resultsPageAsString = contentAsString(recordCheckResultsPage) + + status(recordCheckResultsPage) mustBe OK + + checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = userType) + resultsPageAsString must include(expectedTitle) + resultsPageAsString must include(expectedHeading) + resultsPageAsString must include(expectedFailureTitle) + resultsPageAsString must include(expectedGenericErrorMessage) + resultsPageAsString must include(expectedFailureReturnButton) + } + } + } - mockGraphqlResponse("standard", fileStatusResponse) + forAll(consignmentStatuses) { consignmentStatus => + s"render the 'transfer has already been confirmed' page with an authenticated judgment user if export status is '$consignmentStatus'" in { + val consignmentId = UUID.fromString("c2efd3e6-6664-4582-8c28-dcf891f60e68") + val userType = "judgment" + val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, getValidJudgmentUserKeycloakConfiguration) + val consignmentStatuses = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), "Export", consignmentStatus, someDateTime, None)) + setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = consignmentStatuses) + setConsignmentTypeResponse(wiremockServer, userType) setConsignmentReferenceResponse(wiremockServer) - val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, getValidStandardUserKeycloakConfiguration) - val recordCheckResultsPage = fileCheckResultsController - .fileCheckResultsPage(consignmentId) - .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks-results")) - val resultsPageAsString = contentAsString(recordCheckResultsPage) + val transferAlreadyCompletedPage = fileCheckResultsController + .judgmentFileCheckResultsPage(consignmentId) + .apply(FakeRequest(GET, s"/$userType/$consignmentId/file-checks").withCSRFToken) - status(recordCheckResultsPage) mustBe OK + val transferAlreadyCompletedPageAsString = contentAsString(transferAlreadyCompletedPage) - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(resultsPageAsString, userType = "standard") - resultsPageAsString must include(expectedTitle) - resultsPageAsString must include(expectedHeading) - resultsPageAsString must include(expectedFailureTitle) - resultsPageAsString must include(expectedGenericErrorMessage) - resultsPageAsString must include(expectedFailureReturnButton) - } + status(transferAlreadyCompletedPage) mustBe OK + contentType(transferAlreadyCompletedPage) mustBe Some("text/html") + headers(transferAlreadyCompletedPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") - s"return 403 if the url doesn't match the consignment type" in { - val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, getValidJudgmentUserKeycloakConfiguration) - mockGraphqlResponse(consignmentType = "judgment") - val fileCheckResultsPage = - fileCheckResultsController - .fileCheckResultsPage(consignmentId) - .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks-results")) + checkPageForStaticElements.checkContentOfPagesThatUseMainScala(transferAlreadyCompletedPageAsString, userType = userType) + transferAlreadyCompletedPageAsString must include("Your transfer has already been completed - Transfer Digital Records - GOV.UK") + transferAlreadyCompletedPageAsString must include("""

Your transfer has already been completed

""") + transferAlreadyCompletedPageAsString must include("""

Click 'Continue' to see the confirmation page again or return to the start.

""") + transferAlreadyCompletedPageAsString must include(s"""href="/$userType/$consignmentId/transfer-complete">Continue""") + } + } - status(fileCheckResultsPage) mustBe FORBIDDEN + forAll(userChecks) { (user, url) => + s"The $url upload page" should { + s"return 403 if the url doesn't match the consignment type" in { + val fileCheckResultsController = instantiateController(getAuthorisedSecurityComponents, user) + val fileCheckResultsPage = url match { + case "judgment" => + mockGraphqlResponse(consignmentType = "standard") + fileCheckResultsController + .judgmentFileCheckResultsPage(consignmentId) + .apply(FakeRequest(GET, s"/judgment/$consignmentId/file-checks-results")) + case "consignment" => + mockGraphqlResponse(consignmentType = "judgment") + fileCheckResultsController + .fileCheckResultsPage(consignmentId) + .apply(FakeRequest(GET, s"/consignment/$consignmentId/file-checks-results")) + } + status(fileCheckResultsPage) mustBe FORBIDDEN + } } } diff --git a/test/testUtils/ConsignmentStatusesOptions.scala b/test/testUtils/ConsignmentStatusesOptions.scala index 565b78714..7a23d7c82 100644 --- a/test/testUtils/ConsignmentStatusesOptions.scala +++ b/test/testUtils/ConsignmentStatusesOptions.scala @@ -507,7 +507,7 @@ object ConsignmentStatusesOptions { generateStatuses( clientChecksCompleted ++ uploadCompleted ++ antivirusFailed ), - "/continue-transfer", + "/file-checks-results", "Failed", "View errors" ), @@ -516,7 +516,7 @@ object ConsignmentStatusesOptions { generateStatuses( clientChecksCompleted ++ uploadCompleted ++ antivirusWithIssues ), - "/continue-transfer", + "/file-checks-results", "Failed", "View errors" ), @@ -543,7 +543,7 @@ object ConsignmentStatusesOptions { generateStatuses( clientChecksCompleted ++ uploadCompleted ++ antivirusCompleted ++ checksumFailed ), - "/continue-transfer", + "/file-checks-results", "Failed", "View errors" ), @@ -552,7 +552,7 @@ object ConsignmentStatusesOptions { generateStatuses( clientChecksCompleted ++ uploadCompleted ++ antivirusCompleted ++ checksumWithIssues ), - "/continue-transfer", + "/file-checks-results", "Failed", "View errors" ), @@ -579,7 +579,7 @@ object ConsignmentStatusesOptions { generateStatuses( clientChecksCompleted ++ uploadCompleted ++ antivirusCompleted ++ checksumCompleted ++ ffidFailed ), - "/continue-transfer", + "/file-checks-results", "Failed", "View errors" ), @@ -588,7 +588,7 @@ object ConsignmentStatusesOptions { generateStatuses( clientChecksCompleted ++ uploadCompleted ++ antivirusCompleted ++ checksumCompleted ++ ffidWithIssues ), - "/continue-transfer", + "/file-checks-results", "Failed", "View errors" ), @@ -597,7 +597,7 @@ object ConsignmentStatusesOptions { generateStatuses( clientChecksCompleted ++ uploadCompleted ++ antivirusCompleted ++ checksumCompleted ++ ffidCompleted ), - "/continue-transfer", + "/file-checks-results", "In Progress", "Resume transfer" ), From 26bcbbcc90dfea4c783cc88027b83dda06499250 Mon Sep 17 00:00:00 2001 From: VimleshGupta Date: Tue, 23 Jul 2024 11:40:53 +0100 Subject: [PATCH 3/4] Revert "TDRD-309 - After upload redirect to transfer complete page" This reverts commit d9b2e7bdc77547e276ac740d217c0acd8a3f162d. --- app/controllers/FileChecksController.scala | 37 +--------- conf/application.local-base.conf | 2 - .../FileChecksControllerSpec.scala | 71 ++++++------------- 3 files changed, 25 insertions(+), 85 deletions(-) diff --git a/app/controllers/FileChecksController.scala b/app/controllers/FileChecksController.scala index 02cb2655f..d3fbb1d62 100644 --- a/app/controllers/FileChecksController.scala +++ b/app/controllers/FileChecksController.scala @@ -14,9 +14,7 @@ import viewsapi.Caching.preventCaching import java.util.UUID import javax.inject.{Inject, Singleton} -import scala.concurrent.duration.DurationInt -import scala.concurrent.{Await, ExecutionContext, Future} -import scala.util.control.Breaks.{break, breakable} +import scala.concurrent.{ExecutionContext, Future} @Singleton class FileChecksController @Inject() ( @@ -121,45 +119,16 @@ class FileChecksController @Inject() ( ).uncache() } else { if (isJudgmentUser) { - if (applicationConfig.blockAutomateJudgmentTransfers) { - Ok(views.html.judgment.judgmentFileChecksProgress(consignmentId, reference, applicationConfig.frontEndInfo, request.token.name)).uncache() - } else { - val totalSleepTime = 3 * 60 * 1000 // Total sleep time in milliseconds (5 minutes) - Await.result(waitForFileChecksToBeCompleted(consignmentId, totalSleepTime), totalSleepTime.millis + 1.minute) // Adding a little buffer time - Redirect(routes.FileChecksResultsController.judgmentFileCheckResultsPage(consignmentId)).uncache() - } + Ok(views.html.judgment.judgmentFileChecksProgress(consignmentId, reference, applicationConfig.frontEndInfo, request.token.name)).uncache() } else { Ok(views.html.standard.fileChecksProgress(consignmentId, reference, applicationConfig.frontEndInfo, request.token.name)) .uncache() } } - }).recover { case _: Exception => + }).recover { case exception: Exception => Ok(views.html.uploadInProgress(consignmentId, reference, "Uploading your records", request.token.name, isJudgmentUser)).uncache() } } - - private def waitForFileChecksToBeCompleted(consignmentId: UUID, totalSleepTime: Int)(implicit request: Request[AnyContent]): Future[Unit] = { - val interval = 5 * 1000 // Interval time in milliseconds (5 seconds) - val intervals = totalSleepTime / interval - - def fileCheckProgress: Future[Boolean] = consignmentService - .fileCheckProgress(consignmentId, request.token.bearerAccessToken) - .map(consignment => - consignment.fileChecks.checksumProgress.filesProcessed == consignment.totalFiles && - consignment.fileChecks.ffidProgress.filesProcessed == consignment.totalFiles && - consignment.fileChecks.antivirusProgress.filesProcessed == consignment.totalFiles - ) - breakable { - for (_ <- 1 to intervals) { - if (Await.result(fileCheckProgress, 10.seconds)) { - break() - } else { - Thread.sleep(interval) - } - } - } - Future.unit - } } case class FileChecksProgress(totalFiles: Int, avMetadataProgressPercentage: Int, checksumProgressPercentage: Int, ffidMetadataProgressPercentage: Int) { diff --git a/conf/application.local-base.conf b/conf/application.local-base.conf index 907c6e821..a9fbf3ff2 100644 --- a/conf/application.local-base.conf +++ b/conf/application.local-base.conf @@ -23,5 +23,3 @@ featureAccessBlock { blockDraftMetadataUpload=false blockMetadataReview=false } - -notificationSnsTopicArn = "arn:test-arn" diff --git a/test/controllers/FileChecksControllerSpec.scala b/test/controllers/FileChecksControllerSpec.scala index a44101107..2f213766e 100644 --- a/test/controllers/FileChecksControllerSpec.scala +++ b/test/controllers/FileChecksControllerSpec.scala @@ -2,7 +2,6 @@ package controllers import com.github.tomakehurst.wiremock.WireMockServer import com.github.tomakehurst.wiremock.client.WireMock._ -import com.github.tomakehurst.wiremock.stubbing.Scenario import configuration.GraphQLConfiguration import graphql.codegen.GetConsignmentStatus.getConsignmentStatus.GetConsignment.ConsignmentStatuses import graphql.codegen.GetFileCheckProgress.{getFileCheckProgress => fileCheck} @@ -100,13 +99,9 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper val filesProcessedWithChecksum = 12 val filesProcessedWithFFID = 8 val dataString: String = progressData(filesProcessedWithAntivirus, filesProcessedWithChecksum, filesProcessedWithFFID, allChecksSucceeded = false) + val uploadStatus = List(ConsignmentStatuses(UUID.randomUUID(), UUID.randomUUID(), UploadType.id, CompletedValue.value, someDateTime, None)) - if (userType == "judgment") { - val dataString2: String = progressData(40, 40, 40, allChecksSucceeded = true) - mockGetFileCheckProgressWithMultipleResults(dataString, dataString2, userType) - } else { - mockGetFileCheckProgress(dataString, userType) - } + mockGetFileCheckProgress(dataString, userType) setConsignmentReferenceResponse(wiremockServer) setConsignmentStatusResponse(app.configuration, wiremockServer, consignmentStatuses = uploadStatus) @@ -132,36 +127,35 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper val fileChecksPageAsString = contentAsString(fileChecksPage) + playStatus(fileChecksPage) mustBe OK + contentType(fileChecksPage) mustBe Some("text/html") + headers(fileChecksPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") + + checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksPageAsString, userType = userType) + fileChecksPageAsString must include(expectedTitle) + fileChecksPageAsString must include(expectedHeading) + fileChecksPageAsString must include(expectedInstruction) if (userType == "judgment") { - playStatus(fileChecksPage) mustBe SEE_OTHER - redirectLocation(fileChecksPage) must be(Some(s"/judgment/$consignmentId/file-checks-results")) + fileChecksPageAsString must include("""""") } else { - playStatus(fileChecksPage) mustBe OK - contentType(fileChecksPage) mustBe Some("text/html") - headers(fileChecksPage) mustBe TreeMap("Cache-Control" -> "no-store, must-revalidate") - - checkPageForStaticElements.checkContentOfPagesThatUseMainScala(fileChecksPageAsString, userType = userType) - fileChecksPageAsString must include(expectedTitle) - fileChecksPageAsString must include(expectedHeading) - fileChecksPageAsString must include(expectedInstruction) fileChecksPageAsString must include( """

For more information on these checks, please see our - | FAQ (opens in new tab) for this service. - |

""".stripMargin + | FAQ (opens in new tab) for this service. + |

""".stripMargin ) fileChecksPageAsString must include( """
- |

- | Important - |

- |
- |
- |

Your records have been checked

- |

Please click 'Continue' to see your results.

- |
""".stripMargin + |

+ | Important + |

+ | + |
+ |

Your records have been checked

+ |

Please click 'Continue' to see your results.

+ |
""".stripMargin ) - fileChecksPageAsString must include(expectedForm) } + fileChecksPageAsString must include(expectedForm) } s"return a redirect to the auth server with an unauthenticated $userType user" in { @@ -508,27 +502,6 @@ class FileChecksControllerSpec extends FrontEndTestHelper with TableDrivenProper ) } - private def mockGetFileCheckProgressWithMultipleResults(dataString: String, dataString2: String, userType: String) = { - setConsignmentTypeResponse(wiremockServer, userType) - - wiremockServer.stubFor( - post(urlEqualTo("/graphql")) - .inScenario("FileChecksController GET should render the judgment fileChecks page if the checks are incomplete") - .withRequestBody(containing("getFileCheckProgress")) - .whenScenarioStateIs(Scenario.STARTED) - .willReturn(okJson(dataString)) - .willSetStateTo("Second call") - ) - - wiremockServer.stubFor( - post(urlEqualTo("/graphql")) - .inScenario("FileChecksController GET should render the judgment fileChecks page if the checks are incomplete") - .withRequestBody(containing("getFileCheckProgress")) - .whenScenarioStateIs("Second call") - .willReturn(okJson(dataString2)) - ) - } - forAll(userChecks) { (user, url) => s"The $url upload page" should { s"return 403 if the url doesn't match the consignment type" in { From 50edf674d6df253c06789ed960643c98dcd2b3be Mon Sep 17 00:00:00 2001 From: VimleshGupta Date: Tue, 23 Jul 2024 12:39:56 +0100 Subject: [PATCH 4/4] Fix compilation error --- app/services/ConsignmentService.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/ConsignmentService.scala b/app/services/ConsignmentService.scala index 55eac5e25..0565b255b 100644 --- a/app/services/ConsignmentService.scala +++ b/app/services/ConsignmentService.scala @@ -22,7 +22,7 @@ import graphql.codegen.GetConsignmentsForMetadataReview.{getConsignmentsForMetad import graphql.codegen.GetFileCheckProgress.{getFileCheckProgress => gfcp} import graphql.codegen.UpdateConsignmentSeriesId.updateConsignmentSeriesId import graphql.codegen.types._ -import graphql.codegen.{AddConsignment, GetConsignmentFilesMetadata} +import graphql.codegen.{AddConsignment, GetConsignmentFilesMetadata, GetFileCheckProgress} import services.ApiErrorHandling._ import services.ConsignmentService.{File, StatusTag} import uk.gov.nationalarchives.tdr.keycloak.Token