/mist/worker/src/test/scala/io/hydrosphere/mist/worker/runners/ArtifactDownloaderSpec.scala

https://github.com/Hydrospheredata/mist · Scala · 141 lines · 114 code · 27 blank · 0 comment · 5 complexity · edd2d790ebda540b40278fb2eb188a6d MD5 · raw file

  1. package io.hydrosphere.mist.worker.runners
  2. import java.nio.file.{Files, Paths}
  3. import akka.http.scaladsl.Http
  4. import akka.http.scaladsl.model.{HttpRequest, HttpResponse, StatusCodes}
  5. import akka.stream.scaladsl.Flow
  6. import org.apache.commons.codec.digest.DigestUtils
  7. import org.apache.commons.io.FileUtils
  8. import org.scalatest.{BeforeAndAfter, BeforeAndAfterAll, FunSpecLike, Matchers}
  9. import scala.concurrent.Await
  10. import scala.concurrent.duration.{Duration, _}
  11. class ArtifactDownloaderSpec extends FunSpecLike with Matchers with BeforeAndAfterAll with BeforeAndAfter {
  12. val basePath = Paths.get("./target/artifacts")
  13. it("should create SimpleArtifactDownloader") {
  14. val downloader = ArtifactDownloader.create("localhost", 2004, 262144000, basePath)
  15. downloader shouldBe a[HttpArtifactDownloader]
  16. }
  17. describe("SimpleArtifactDownloader") {
  18. val routes = Flow[HttpRequest].map { request =>
  19. val uri = request.uri.toString()
  20. if (uri.endsWith(".jar")) {
  21. HttpResponse(status = StatusCodes.OK, entity = "JAR CONTENT")
  22. } else if (uri.endsWith("/sha")) {
  23. val data = DigestUtils.sha1Hex("JAR CONTENT")
  24. HttpResponse(status = StatusCodes.OK, entity = data)
  25. } else {
  26. HttpResponse(status = StatusCodes.NotFound, entity = s"Not found ${request.uri}")
  27. }
  28. }
  29. before {
  30. val f = basePath.toFile
  31. FileUtils.deleteQuietly(f)
  32. FileUtils.forceMkdir(f)
  33. }
  34. it("should download file if it not found locally") {
  35. val fileContent = MockHttpServer.onServer(routes, binding => {
  36. val port = binding.localAddress.getPort
  37. val downloader = ArtifactDownloader.create("localhost", port, 262144000, basePath)
  38. val artifact = Await.result(downloader.downloadArtifact("test.jar"), Duration.Inf)
  39. new String(Files.readAllBytes(artifact.local.toPath))
  40. })
  41. Await.result(fileContent, Duration.Inf) shouldBe "JAR CONTENT"
  42. }
  43. it("should not download file if sha of local file and remote not equal") {
  44. val localFile = basePath.resolve("test.jar")
  45. Files.write(localFile, "DIFFERENT".getBytes())
  46. val fileContent = MockHttpServer.onServer(routes, binding => {
  47. val port = binding.localAddress.getPort
  48. val downloader = ArtifactDownloader.create("localhost", port, 262144000, basePath)
  49. val file = Await.result(downloader.downloadArtifact("test.jar"), Duration.Inf)
  50. file
  51. })
  52. Await.result(fileContent, Duration.Inf).local.lastModified() shouldBe localFile.toFile.lastModified()
  53. }
  54. it("should not download file if checksums are correct") {
  55. val localFile = basePath.resolve("test.jar")
  56. Files.write(localFile, "JAR CONTENT".getBytes())
  57. val fileF = MockHttpServer.onServer(routes, binding => {
  58. val port = binding.localAddress.getPort
  59. val downloader = ArtifactDownloader.create("localhost", port, 262144000, basePath)
  60. val file = Await.result(downloader.downloadArtifact("test.jar"), Duration.Inf)
  61. file
  62. })
  63. Await.result(fileF, Duration.Inf).local.lastModified() == localFile.toFile.lastModified()
  64. }
  65. it("should fail when local and remote file not found") {
  66. val routes = Flow[HttpRequest].map {request => {
  67. HttpResponse(status = StatusCodes.NotFound, entity = s"Not found ${request.uri}")
  68. }}
  69. val fileF = MockHttpServer.onServer(routes, binding => {
  70. val port = binding.localAddress.getPort
  71. val downloader = ArtifactDownloader.create("localhost", port, 262144000, basePath)
  72. val fileF = downloader.downloadArtifact("test.jar")
  73. Await.result(fileF, Duration.Inf)
  74. })
  75. intercept[IllegalArgumentException] {
  76. Await.result(fileF, 30.seconds)
  77. }
  78. }
  79. }
  80. }
  81. object MockHttpServer {
  82. import akka.actor.ActorSystem
  83. import akka.stream.ActorMaterializer
  84. import akka.util.Timeout
  85. import scala.concurrent.duration._
  86. import scala.concurrent.{Future, Promise}
  87. def onServer[A](
  88. routes: Flow[HttpRequest, HttpResponse, _],
  89. f: (Http.ServerBinding) => A): Future[A] = {
  90. implicit val system = ActorSystem("mock-http-cli")
  91. implicit val materializer = ActorMaterializer()
  92. implicit val executionContext = system.dispatcher
  93. implicit val timeout = Timeout(1.seconds)
  94. val binding = Http().bindAndHandle(routes, "localhost", 0)
  95. val close = Promise[Http.ServerBinding]
  96. close.future
  97. .flatMap(binding => binding.unbind())
  98. .onComplete(_ => {
  99. materializer.shutdown()
  100. Await.result(system.terminate(), Duration.Inf)
  101. })
  102. val result = binding.flatMap(binding => {
  103. try {
  104. Future.successful(f(binding))
  105. } catch {
  106. case e: Throwable =>
  107. Future.failed(e)
  108. } finally {
  109. close.success(binding)
  110. }
  111. })
  112. result
  113. }
  114. }