diff --git a/build.sbt b/build.sbt index 7a3f230..a37d9b4 100644 --- a/build.sbt +++ b/build.sbt @@ -1,25 +1,27 @@ name := "sqsmock" -version := "0.3.2" +version := "0.3.3-SNAPSHOT" organization := "io.findify" -scalaVersion := "2.11.8" +scalaVersion := "2.12.3" -val akkaVersion = "2.4.9" +val akkaVersion = "2.5.1" + +crossScalaVersions := Seq("2.12.3", "2.11.8") libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-stream" % akkaVersion, "com.typesafe.akka" %% "akka-slf4j" % akkaVersion, - "com.typesafe.akka" %% "akka-http-experimental" % akkaVersion, - "org.scala-lang.modules" %% "scala-xml" % "1.0.5", + "com.typesafe.akka" %% "akka-http" % "10.0.6", + "org.scala-lang.modules" %% "scala-xml" % "1.0.6", "joda-time" % "joda-time" % "2.9.4", - "org.scalatest" %% "scalatest" % "3.0.0" % "test", - "com.amazonaws" % "aws-java-sdk-sqs" % "1.11.32" % "test" + "org.scalatest" %% "scalatest" % "3.0.1" % "test", + "com.amazonaws" % "aws-java-sdk-sqs" % "1.11.126" % "test" ) licenses += ("MIT", url("https://opensource.org/licenses/MIT")) bintrayOrganization := Some("findify") -parallelExecution in Test := false \ No newline at end of file +parallelExecution in Test := false diff --git a/project/build.properties b/project/build.properties index 034c10d..3e8bd1a 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version = 0.13.10 +sbt.version = 0.13.15 diff --git a/src/main/scala/io/findify/sqsmock/SQSBackend.scala b/src/main/scala/io/findify/sqsmock/SQSBackend.scala index 628fa36..bc220d4 100644 --- a/src/main/scala/io/findify/sqsmock/SQSBackend.scala +++ b/src/main/scala/io/findify/sqsmock/SQSBackend.scala @@ -15,19 +15,31 @@ import scala.collection.mutable class SQSBackend(account:Long, port:Int, system:ActorSystem) { val log = Logger(this.getClass, "sqs_backend") val queueCache = mutable.Map[String, QueueCache]() - val createQueueWorker = new CreateQueueWorker(account, port, queueCache, system) - val sendMessageWorker = new SendMessageWorker(account, queueCache, system) - val receiveMessageWorker = new ReceiveMessageWorker(account, queueCache, system) + + val createQueueWorker = new CreateQueueWorker(account, port, queueCache, system) + val sendMessageWorker = new SendMessageWorker(account, queueCache, system) + val receiveMessageWorker = new ReceiveMessageWorker(account, queueCache, system) val sendMessageBatchWorker = new SendMessageBatchWorker(account, queueCache, system) - val deleteMessageWorker = new DeleteMessageWorker(account, queueCache, system) + val deleteMessageWorker = new DeleteMessageWorker(account, queueCache, system) + val listQueuesWorker = new ListQueuesWorker(account, queueCache, system) + val getQueueUrlWorker = new GetQueueUrlWorker(account, queueCache, system) + val getQueueAttributesWorker = new GetQueueAttributesWorker(account, queueCache, system) + val deleteMessageBatchWorker = new DeleteMessageBatchWorker(account, queueCache, system) + val deleteQueueUrlWorker = new DeleteQueueUrlWorker(account, queueCache, system) + def process(fields:Map[String,String]) = { log.debug(s"processing request for fields $fields") fields.get("Action") match { - case Some("SendMessage") => sendMessageWorker.process(fields) + case Some("SendMessage") => sendMessageWorker.process(fields) case Some("SendMessageBatch") => sendMessageBatchWorker.process(fields) - case Some("ReceiveMessage") => receiveMessageWorker.process(fields) - case Some("CreateQueue") => createQueueWorker.process(fields) - case Some("DeleteMessage") => deleteMessageWorker.process(fields) + case Some("ReceiveMessage") => receiveMessageWorker.process(fields) + case Some("CreateQueue") => createQueueWorker.process(fields) + case Some("DeleteMessage") => deleteMessageWorker.process(fields) + case Some("ListQueues") => listQueuesWorker.process(fields) + case Some("GetQueueUrl") => getQueueUrlWorker.process(fields) + case Some("GetQueueAttributes") => getQueueAttributesWorker.process(fields) + case Some("DeleteMessageBatch") => deleteMessageBatchWorker.process(fields) + case Some("DeleteQueue") => deleteQueueUrlWorker.process(fields) case _ => HttpResponse(StatusCodes.BadRequest, entity = ErrorResponse("Sender", "InvalidParameterValue", "operation not supported").toXML.toString()) } } diff --git a/src/main/scala/io/findify/sqsmock/SQSService.scala b/src/main/scala/io/findify/sqsmock/SQSService.scala index dfabc5e..d16fa6c 100644 --- a/src/main/scala/io/findify/sqsmock/SQSService.scala +++ b/src/main/scala/io/findify/sqsmock/SQSService.scala @@ -17,13 +17,24 @@ import scala.concurrent.duration.Duration import scala.concurrent.{Await, Future} import scala.collection.JavaConversions._ +object SQSService { + def main(args: Array[String]) { + val sqs = new SQSService(8001, 1) + sqs.start() + sqs.block + } + + val config = ConfigFactory.parseMap(Map("akka.http.parsing.illegal-header-warnings" -> "off")) +} + /** * Created by shutty on 3/29/16. */ -class SQSService(port:Int, account:Int = 1) { - val config = ConfigFactory.parseMap(Map("akka.http.parsing.illegal-header-warnings" -> "off")) - implicit val system = ActorSystem.create("sqsmock", config) - def start():Unit = { +class SQSService(port:Int, account:Int = 1)(implicit system: ActorSystem = ActorSystem.create("sqsmock", SQSService.config)) { + + private var bind: Http.ServerBinding = _ + + def start() = { val log = Logger(system.getClass, "sqs_client") implicit val mat = ActorMaterializer() val http = Http(system) @@ -48,17 +59,10 @@ class SQSService(port:Int, account:Int = 1) { } } } - Await.result(http.bindAndHandle(route, "localhost", 8001), Duration.Inf) + bind = Await.result(http.bindAndHandle(route, "localhost", port), Duration.Inf) + bind } - def shutdown():Unit = Await.result(system.terminate(), Duration.Inf) + def stop():Unit = Await.result(system.terminate(), Duration.Inf) def block():Unit = Await.result(system.whenTerminated, Duration.Inf) } - -object SQSService { - def main(args: Array[String]) { - val sqs = new SQSService(8001, 1) - sqs.start() - sqs.block - } -} diff --git a/src/main/scala/io/findify/sqsmock/actions/DeleteMessageBatchWorker.scala b/src/main/scala/io/findify/sqsmock/actions/DeleteMessageBatchWorker.scala new file mode 100644 index 0000000..d4e9483 --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/actions/DeleteMessageBatchWorker.scala @@ -0,0 +1,47 @@ +package io.findify.sqsmock.actions + +import akka.actor.ActorSystem +import akka.event.slf4j.Logger +import akka.http.scaladsl.model.{HttpResponse, StatusCodes} +import io.findify.sqsmock.messages.{DeleteMessageBatchResponse, ErrorResponse, SendMessageBatchResponse} +import io.findify.sqsmock.model.{DeleteMessageBatchEntry, MessageBatchEntry, QueueCache} + +import scala.collection.mutable + +/** + * Handle DeleteQueue request. + * @since May 14 2017. + */ +class DeleteMessageBatchWorker(account:Long, queues:mutable.Map[String,QueueCache], system:ActorSystem) extends Worker { + + val log = Logger(this.getClass, "delete_message_batch_worker") + + val fieldFormat = """DeleteMessageBatchRequestEntry\.([0-9]+)\.([0-9A-Za-z\.]+)""".r + + def process(fields:Map[String,String]) = { + val result = for ( + queueUrl <- fields.get("QueueUrl"); + queue <- queues.get(queueUrl) + ) yield { + val deletedMsgs = fields + .filter(_._1.startsWith("DeleteMessageBatchRequestEntry")) + .flatMap { case (key,value) => key match { + case fieldFormat(index, name) => Some((index.toInt, name, value)) + case _ => None + }} + .groupBy(_._1) + .values.toList + .flatMap(DeleteMessageBatchEntry(_)) + .filter { entry => + log.debug(s"deleting message ${entry.id} from queue") + queue.delete(entry.receiptHandle) + } + + HttpResponse(StatusCodes.OK, entity = DeleteMessageBatchResponse(deletedMsgs).toXML.toString()) + } + result.getOrElse { + log.warn("cannot send message: possibly, some request parameter is missing") + HttpResponse(StatusCodes.BadRequest, entity = ErrorResponse("Sender", "InvalidParameterValue", "oops").toXML.toString()) + } + } +} diff --git a/src/main/scala/io/findify/sqsmock/actions/DeleteQueueUrlWorker.scala b/src/main/scala/io/findify/sqsmock/actions/DeleteQueueUrlWorker.scala new file mode 100644 index 0000000..370403e --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/actions/DeleteQueueUrlWorker.scala @@ -0,0 +1,36 @@ +package io.findify.sqsmock.actions + +import akka.actor.ActorSystem +import akka.event.slf4j.Logger +import akka.http.scaladsl.model.{HttpResponse, StatusCodes} +import io.findify.sqsmock.messages.{DeleteQueueResponse, ErrorResponse, GetQueueUrlResponse} +import io.findify.sqsmock.model.{QueueCache, Queues} + +import scala.collection.mutable + +/** + * Worker to respond to DeleteQueue requests. + * @since May 14 2017 + * + * @param account + * @param queues + * @param system + */ +class DeleteQueueUrlWorker(account:Long, queues:mutable.Map[String,QueueCache], system:ActorSystem) extends Worker { + + val log = Logger(this.getClass, "get_queue_url_worker") + def process(fields:Map[String,String]) = { + val result = for ( + queueUrl <- fields.get("QueueUrl") + ) yield { + log.debug(s"Deleting queue '$queueUrl'") + queues.remove(queueUrl) + HttpResponse(StatusCodes.OK, entity = DeleteQueueResponse.toXML.toString) + } + + result.getOrElse{ + log.warn("cannot send message: possibly, some request parameter is missing") + HttpResponse(StatusCodes.BadRequest, entity = ErrorResponse("Sender", "InvalidParameterValue", "oops").toXML.toString()) + } + } +} diff --git a/src/main/scala/io/findify/sqsmock/actions/GetQueueAttributesWorker.scala b/src/main/scala/io/findify/sqsmock/actions/GetQueueAttributesWorker.scala new file mode 100644 index 0000000..afcc31f --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/actions/GetQueueAttributesWorker.scala @@ -0,0 +1,28 @@ +package io.findify.sqsmock.actions + +import akka.actor.ActorSystem +import akka.event.slf4j.Logger +import akka.http.scaladsl.model.{HttpResponse, StatusCodes} +import io.findify.sqsmock.messages.{ErrorResponse, GetQueueAttributesResponse, GetQueueUrlResponse} +import io.findify.sqsmock.model.{QueueCache, Queues} + +import scala.collection.mutable + +/** + * Worker to respond to GetQueueUrl requests. + * @since May 14 2017 + * + * @param account + * @param queues + * @param system + */ +class GetQueueAttributesWorker(account:Long, queues:mutable.Map[String,QueueCache], system:ActorSystem) extends Worker { + + val log = Logger(this.getClass, "get_queue_url_worker") + def process(fields:Map[String,String]) = { + // TODO Implement completely. See http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_GetQueueAttributes.html + + log.warn("Not implemented completely. Returning static result.") + HttpResponse(StatusCodes.OK, entity = GetQueueAttributesResponse(Map.empty).toXML.toString()) + } +} diff --git a/src/main/scala/io/findify/sqsmock/actions/GetQueueUrlWorker.scala b/src/main/scala/io/findify/sqsmock/actions/GetQueueUrlWorker.scala new file mode 100644 index 0000000..7b09282 --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/actions/GetQueueUrlWorker.scala @@ -0,0 +1,52 @@ +package io.findify.sqsmock.actions + +import akka.actor.ActorSystem +import akka.event.slf4j.Logger +import akka.http.scaladsl.model.{HttpResponse, StatusCodes} +import io.findify.sqsmock.messages.{ErrorResponse, GetQueueUrlResponse, ListQueuesResponse, ReceiveMessageResponse} +import io.findify.sqsmock.model.{QueueCache, Queues} + +import scala.collection.mutable + +/** + * Worker to respond to GetQueueUrl requests. + * @since May 14 2017 + * + * @param account + * @param queues + * @param system + */ +class GetQueueUrlWorker(account:Long, queues:mutable.Map[String,QueueCache], system:ActorSystem) extends Worker { + + val log = Logger(this.getClass, "get_queue_url_worker") + def process(fields:Map[String,String]) = { + val result = for ( + queueName <- fields.get("QueueName") + ) yield { + // optional field + val accountId = fields.get("QueueOwnerAWSAccountId") + val validAccount = accountId.map(_ == s"$account").getOrElse(true) + + if (validAccount) { + // filter queue on name + Queues.queueWithName(queues.keys, queueName) match { + case Some(url) => + log.debug(s"Found url $url for queue name $queueName") + HttpResponse(StatusCodes.OK, entity = GetQueueUrlResponse(Option(url)).toXML.toString()) + case None => + log.debug(s"No url found for queue name $queueName") + HttpResponse(StatusCodes.BadRequest, entity = ErrorResponse("Sender", "AWS.SimpleQueueService.NonExistentQueue", s"No queue with name '$queueName'").toXML.toString()) + } + } else { + // return empty list + log.debug(s"Given account ${accountId.get} does not match current account $account") + HttpResponse(StatusCodes.OK, entity = GetQueueUrlResponse(None).toXML.toString()) + } + } + + result.getOrElse{ + log.warn("cannot send message: possibly, some request parameter is missing") + HttpResponse(StatusCodes.BadRequest, entity = ErrorResponse("Sender", "InvalidParameterValue", "oops").toXML.toString()) + } + } +} diff --git a/src/main/scala/io/findify/sqsmock/actions/ListQueuesWorker.scala b/src/main/scala/io/findify/sqsmock/actions/ListQueuesWorker.scala new file mode 100644 index 0000000..b9d1d86 --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/actions/ListQueuesWorker.scala @@ -0,0 +1,31 @@ +package io.findify.sqsmock.actions + +import akka.actor.ActorSystem +import akka.event.slf4j.Logger +import akka.http.scaladsl.model.{HttpResponse, StatusCodes} +import io.findify.sqsmock.messages.ListQueuesResponse +import io.findify.sqsmock.model.{QueueCache, Queues} + +import scala.collection.mutable + +/** + * Worker to respond to ListQueue requests. + * @since May 13 2017 + * + * @param account + * @param queues + * @param system + */ +class ListQueuesWorker(account:Long, queues:mutable.Map[String,QueueCache], system:ActorSystem) extends Worker { + + val log = Logger(this.getClass, "list_queues_worker") + def process(fields:Map[String,String]) = { + // get queues with prefix or all queues + val queueNames = fields.get("QueueNamePrefix").map( + prefix => Queues.queuesWithPrefix(queues.keys, prefix) + ).getOrElse(queues.keys.toList) + + log.debug("listing queues: {}", queueNames.mkString(",")) + HttpResponse(StatusCodes.OK, entity = ListQueuesResponse(queueNames).toXML.toString) + } +} diff --git a/src/main/scala/io/findify/sqsmock/messages/DeleteMessageBatchResponse.scala b/src/main/scala/io/findify/sqsmock/messages/DeleteMessageBatchResponse.scala new file mode 100644 index 0000000..41a513f --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/messages/DeleteMessageBatchResponse.scala @@ -0,0 +1,27 @@ +package io.findify.sqsmock.messages + +import io.findify.sqsmock.model.{DeleteMessageBatchEntry, MessageBatchEntry} + +/** + * Response to DeleteMessageBatch request. + * @see http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_DeleteMessageBatch.html + * @since May 14 2017 + * @param entries List of deleted messages + */ +case class DeleteMessageBatchResponse(entries:List[DeleteMessageBatchEntry]) extends Response { + def toXML = + + + { + entries.map { entry => + + { entry.id } + + } + } + + + {uuid} + + +} diff --git a/src/main/scala/io/findify/sqsmock/messages/DeleteQueueResponse.scala b/src/main/scala/io/findify/sqsmock/messages/DeleteQueueResponse.scala new file mode 100644 index 0000000..884fcd9 --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/messages/DeleteQueueResponse.scala @@ -0,0 +1,15 @@ +package io.findify.sqsmock.messages + +/** + * Response to DeleteQueue request. + * @see http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_DeleteQueue.html + * @since May 14 2017. + */ +case object DeleteQueueResponse extends Response { + def toXML = + + + {uuid} + + +} diff --git a/src/main/scala/io/findify/sqsmock/messages/GetQueueAttributesResponse.scala b/src/main/scala/io/findify/sqsmock/messages/GetQueueAttributesResponse.scala new file mode 100644 index 0000000..925fe5d --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/messages/GetQueueAttributesResponse.scala @@ -0,0 +1,27 @@ +package io.findify.sqsmock.messages + +/** + * Response to ListQueues request. + * @see http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_GetQueueAttributes.html + * @since May 13 2017 + * @param attributesMap map of attributes names and value to be returned in the response + */ +case class GetQueueAttributesResponse(attributesMap: Map[String, Any]) extends Response { + def toXML = + + + + VisibilityTimeout + 30 + + + DelaySeconds + 0 + + + ReceiveMessageWaitTimeSeconds + 2 + + + +} diff --git a/src/main/scala/io/findify/sqsmock/messages/GetQueueUrlResponse.scala b/src/main/scala/io/findify/sqsmock/messages/GetQueueUrlResponse.scala new file mode 100644 index 0000000..f7b0c88 --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/messages/GetQueueUrlResponse.scala @@ -0,0 +1,24 @@ +package io.findify.sqsmock.messages + +/** + * Response to ListQueues request. + * @see http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_GetQueueUrl.html + * @since May 13 2017 + * @param queueUrl Optional queue url. If None, not queue url is returned. + */ +case class GetQueueUrlResponse(queueUrl:Option[String]) extends Response { + def toXML = + + + { + queueUrl match { + case Some(url) => { url } + case None => + } + } + + + 470a6f13-2ed9-4181-ad8a-2fdea142988e + + +} diff --git a/src/main/scala/io/findify/sqsmock/messages/ListQueuesResponse.scala b/src/main/scala/io/findify/sqsmock/messages/ListQueuesResponse.scala new file mode 100644 index 0000000..8d3d3b1 --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/messages/ListQueuesResponse.scala @@ -0,0 +1,25 @@ +package io.findify.sqsmock.messages + +/** + * Response to ListQueues request. + * @see http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ListQueues.html + * @since May 13 2017 + * @param queueNames Names of all queues. + */ +case class ListQueuesResponse(queueNames:Seq[String]) extends Response { + def toXML = + + + { + queueNames.map { queueName => + {queueName} + } + } + + + + {uuid} + + + +} diff --git a/src/main/scala/io/findify/sqsmock/model/DeleteMessageBatchEntry.scala b/src/main/scala/io/findify/sqsmock/model/DeleteMessageBatchEntry.scala new file mode 100644 index 0000000..8395c68 --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/model/DeleteMessageBatchEntry.scala @@ -0,0 +1,20 @@ +package io.findify.sqsmock.model + +/** + * Representing a DeleteMessageBatchRequestEntry. + * + * @see http://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_DeleteMessageBatchRequestEntry.html + * @since May 14 2017 + */ +case class DeleteMessageBatchEntry(id:String, receiptHandle: String) + +object DeleteMessageBatchEntry { + + def apply(fields:Iterable[(Int,String,String)]) = { + val attrs = fields.map(f => f._2 -> f._3).toMap + for ( + id <- attrs.get("Id"); + handle <- attrs.get("ReceiptHandle") + ) yield new DeleteMessageBatchEntry(id, handle) + } +} \ No newline at end of file diff --git a/src/main/scala/io/findify/sqsmock/model/Queues.scala b/src/main/scala/io/findify/sqsmock/model/Queues.scala new file mode 100644 index 0000000..c6c55bb --- /dev/null +++ b/src/main/scala/io/findify/sqsmock/model/Queues.scala @@ -0,0 +1,42 @@ +package io.findify.sqsmock.model + +/** + * Utility functions for queues. + */ +object Queues { + + val queueUrlRegex = """https?://(?:\w+)(?::[0-9]{2,5})?/(?:\w+)/([\w-]+)""".r + + /** + * @param queueUrls Seq of queue urls. + * @return A map of queue name and urls. + */ + def mapQueueUrlsWithName(queueUrls: Traversable[String]): Map[String, String] = { + queueUrls.map { url => + val queueUrlRegex(name) = url + name -> url + }.toMap + } + + /** + * @param queueUrls All queue urls. + * @param queueName Wanted queue name. + * @return Queue urls of queue with given name (if found) + */ + def queueWithName(queueUrls: Traversable[String], queueName: String): Option[String] = { + mapQueueUrlsWithName(queueUrls).find { + case (name, url) => name == queueName + }.map(_._2) + } + + /** + * @param queueUrls All queue urls. + * @param prefix Wanted queue name prefix. + * @return All queues with matching prefix. + */ + def queuesWithPrefix(queueUrls: Traversable[String], prefix: String): Seq[String] = { + mapQueueUrlsWithName(queueUrls).filter { + case (name, _) => name.startsWith(prefix) + }.values.toSeq + } +} diff --git a/src/test/scala/io/findify/sqsmock/BatchDeleteTest.scala b/src/test/scala/io/findify/sqsmock/BatchDeleteTest.scala new file mode 100644 index 0000000..5a16a2b --- /dev/null +++ b/src/test/scala/io/findify/sqsmock/BatchDeleteTest.scala @@ -0,0 +1,47 @@ +package io.findify.sqsmock + +import com.amazonaws.services.sqs.model.{DeleteMessageBatchRequestEntry, ReceiveMessageRequest, SendMessageBatchRequestEntry} +import org.scalatest._ + +import scala.collection.JavaConversions._ + +/** + * Testing [[io.findify.sqsmock.actions.DeleteMessageBatchWorker]]. + */ +class BatchDeleteTest extends FlatSpec with Matchers with SQSStartStop { + import collection.JavaConverters._ + + val queue = "http://localhost:8001/123/foo" + + "sqs mock" should "send delete messages in batch" in { + // setup some messages + client.createQueue("foo") + val batch = List( + new SendMessageBatchRequestEntry("1", "hello"), + new SendMessageBatchRequestEntry("2", "world") + ) + val sendResult = client.sendMessageBatch(queue, batch) + assert(sendResult.getFailed.isEmpty) + assert(sendResult.getSuccessful.length == 2) + + // can only delete the message after they have been received + // since the delete request requires a receive handle. + val request = new ReceiveMessageRequest() + .withMaxNumberOfMessages(10) + .withQueueUrl(queue) + val result = client.receiveMessage(request) + assert(result.getMessages.size() == 2) + + // delete the messages + val deleteBatch = result.getMessages.asScala.map { msg => + new DeleteMessageBatchRequestEntry(msg.getMessageId, msg.getReceiptHandle) + } + val deleteResult = client.deleteMessageBatch(queue, deleteBatch) + assert(deleteResult.getFailed.isEmpty) + assert(deleteResult.getSuccessful.length == 2) + + // queue should now be empty + val receiveResult = client.receiveMessage(queue) + assert(receiveResult.getMessages.isEmpty) + } +} diff --git a/src/test/scala/io/findify/sqsmock/BatchSendReceiveTest.scala b/src/test/scala/io/findify/sqsmock/BatchSendReceiveTest.scala index 5e92891..70e0d58 100644 --- a/src/test/scala/io/findify/sqsmock/BatchSendReceiveTest.scala +++ b/src/test/scala/io/findify/sqsmock/BatchSendReceiveTest.scala @@ -1,6 +1,5 @@ package io.findify.sqsmock -import com.amazonaws.services.sqs.AmazonSQSClient import com.amazonaws.services.sqs.model.{ReceiveMessageRequest, SendMessageBatchRequestEntry} import org.scalatest._ diff --git a/src/test/scala/io/findify/sqsmock/DeleteQueueTest.scala b/src/test/scala/io/findify/sqsmock/DeleteQueueTest.scala new file mode 100644 index 0000000..e49e1b3 --- /dev/null +++ b/src/test/scala/io/findify/sqsmock/DeleteQueueTest.scala @@ -0,0 +1,31 @@ +package io.findify.sqsmock + +import org.scalatest.{FlatSpec, Matchers} + +/** + * Testing [[io.findify.sqsmock.actions.ListQueuesWorker]] + */ +class DeleteQueueTest extends FlatSpec with Matchers with SQSStartStop { + + import collection.JavaConverters._ + + override def beforeAll: Unit = { + super.beforeAll + // create some queues + client.createQueue("foo").getQueueUrl should be ("http://localhost:8001/123/foo") + client.createQueue("bar").getQueueUrl should be ("http://localhost:8001/123/bar") + client.createQueue("baz").getQueueUrl should be ("http://localhost:8001/123/baz") + } + + "sqs mock" should "delete a queue" in { + val deleteResponse = client.deleteQueue("http://localhost:8001/123/foo") + + // verify queue has been removed + val listResponse = client.listQueues() + val queueUrls = listResponse.getQueueUrls.asScala.toList + queueUrls should contain theSameElementsAs(List( + "http://localhost:8001/123/bar", + "http://localhost:8001/123/baz" + )) + } +} diff --git a/src/test/scala/io/findify/sqsmock/GetQueueAttributesTest.scala b/src/test/scala/io/findify/sqsmock/GetQueueAttributesTest.scala new file mode 100644 index 0000000..08a59f6 --- /dev/null +++ b/src/test/scala/io/findify/sqsmock/GetQueueAttributesTest.scala @@ -0,0 +1,22 @@ +package io.findify.sqsmock + +import java.util + +import org.scalatest.{FlatSpec, Matchers} + +/** + * Testing [[io.findify.sqsmock.actions.GetQueueAttributesWorker]] + */ +class GetQueueAttributesTest extends FlatSpec with Matchers with SQSStartStop { + + override def beforeAll: Unit = { + super.beforeAll + // create some queues + client.createQueue("foo").getQueueUrl should be ("http://localhost:8001/123/foo") + } + + "sqs mock" should "return queue attributes" in { + val response = client.getQueueAttributes("http://localhost:8001/123/foo", new util.ArrayList()) + response.getAttributes.size() should be (3) + } +} diff --git a/src/test/scala/io/findify/sqsmock/GetQueueUrlTest.scala b/src/test/scala/io/findify/sqsmock/GetQueueUrlTest.scala new file mode 100644 index 0000000..6070a69 --- /dev/null +++ b/src/test/scala/io/findify/sqsmock/GetQueueUrlTest.scala @@ -0,0 +1,39 @@ +package io.findify.sqsmock + +import com.amazonaws.services.sqs.model.{GetQueueUrlRequest, QueueDoesNotExistException} +import org.scalatest.{FlatSpec, Matchers} + +/** + * Testing [[io.findify.sqsmock.actions.GetQueueUrlWorker]] + */ +class GetQueueUrlTest extends FlatSpec with Matchers with SQSStartStop { + + override def beforeAll: Unit = { + super.beforeAll + // create some queues + client.createQueue("foo").getQueueUrl should be ("http://localhost:8001/123/foo") + client.createQueue("bar").getQueueUrl should be ("http://localhost:8001/123/bar") + client.createQueue("baz").getQueueUrl should be ("http://localhost:8001/123/baz") + } + + "sqs mock" should "return queue with name" in { + val response = client.getQueueUrl("foo") + response.getQueueUrl should be("http://localhost:8001/123/foo") + } + + it should "return queue if name and account match" in { + val response = client.getQueueUrl(new GetQueueUrlRequest().withQueueName("foo").withQueueOwnerAWSAccountId("123")) + response.getQueueUrl should be("http://localhost:8001/123/foo") + } + + it should "return no url if account does not match" in { + val response = client.getQueueUrl(new GetQueueUrlRequest().withQueueName("foo").withQueueOwnerAWSAccountId("1")) + response.getQueueUrl should be("") + } + + it should "return error if no name matches" in { + intercept[QueueDoesNotExistException] { + client.getQueueUrl("non-existant") + } + } +} diff --git a/src/test/scala/io/findify/sqsmock/ListQueuesTest.scala b/src/test/scala/io/findify/sqsmock/ListQueuesTest.scala new file mode 100644 index 0000000..7f270b5 --- /dev/null +++ b/src/test/scala/io/findify/sqsmock/ListQueuesTest.scala @@ -0,0 +1,44 @@ +package io.findify.sqsmock + +import org.scalatest.{FlatSpec, Matchers} + +import scala.collection.JavaConversions._ + +/** + * Testing [[io.findify.sqsmock.actions.ListQueuesWorker]] + */ +class ListQueuesTest extends FlatSpec with Matchers with SQSStartStop { + + import collection.JavaConverters._ + + override def beforeAll: Unit = { + super.beforeAll + // create some queues + client.createQueue("foo").getQueueUrl should be ("http://localhost:8001/123/foo") + client.createQueue("bar").getQueueUrl should be ("http://localhost:8001/123/bar") + client.createQueue("baz").getQueueUrl should be ("http://localhost:8001/123/baz") + } + + "sqs mock" should "list all queus" in { + val response = client.listQueues() + val queueUrls = response.getQueueUrls.asScala.toList + queueUrls should contain theSameElementsAs(List( + "http://localhost:8001/123/foo", + "http://localhost:8001/123/bar", + "http://localhost:8001/123/baz" + )) + } + + it should "list some queues" in { + val response1 = client.listQueues("ba") + response1.getQueueUrls.asScala.toList should contain theSameElementsAs(List( + "http://localhost:8001/123/bar", + "http://localhost:8001/123/baz" + )) + + val response2 = client.listQueues("fo") + response2.getQueueUrls.asScala.toList should contain theSameElementsAs(List( + "http://localhost:8001/123/foo" + )) + } +} diff --git a/src/test/scala/io/findify/sqsmock/ReceiveDeleteTest.scala b/src/test/scala/io/findify/sqsmock/ReceiveDeleteTest.scala index 3d73928..4806436 100644 --- a/src/test/scala/io/findify/sqsmock/ReceiveDeleteTest.scala +++ b/src/test/scala/io/findify/sqsmock/ReceiveDeleteTest.scala @@ -1,8 +1,7 @@ package io.findify.sqsmock -import com.amazonaws.services.sqs.AmazonSQSClient import com.amazonaws.services.sqs.model.CreateQueueRequest -import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers} +import org.scalatest.{FlatSpec, Matchers} import scala.collection.JavaConversions._ /** diff --git a/src/test/scala/io/findify/sqsmock/SQSStartStop.scala b/src/test/scala/io/findify/sqsmock/SQSStartStop.scala index a37fe84..db03705 100644 --- a/src/test/scala/io/findify/sqsmock/SQSStartStop.scala +++ b/src/test/scala/io/findify/sqsmock/SQSStartStop.scala @@ -1,22 +1,31 @@ package io.findify.sqsmock -import com.amazonaws.auth.AnonymousAWSCredentials -import com.amazonaws.services.sqs.AmazonSQSClient +import com.amazonaws.auth.{AWSCredentials, AWSCredentialsProvider, AnonymousAWSCredentials} +import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration +import com.amazonaws.services.sqs.{AmazonSQS, AmazonSQSClient, AmazonSQSClientBuilder} import org.scalatest.{BeforeAndAfterAll, FlatSpec} /** * Created by shutty on 3/31/16. */ trait SQSStartStop extends FlatSpec with BeforeAndAfterAll { - var sqs:SQSService = _ - var client:AmazonSQSClient = _ + var sqs: SQSService = _ + var client: AmazonSQS = _ + override def beforeAll = { sqs = new SQSService(8001, 123) sqs.start() - client = new AmazonSQSClient(new AnonymousAWSCredentials()) - client.setEndpoint("http://localhost:8001") + + client = AmazonSQSClientBuilder.standard() + .withCredentials(new AWSCredentialsProvider { + val creds = new AnonymousAWSCredentials() + override def refresh(): Unit = () + override def getCredentials: AWSCredentials = creds + }) + .withEndpointConfiguration(new EndpointConfiguration("http://localhost:8001", "us-east-1")) + .build() } override def afterAll = { - sqs.shutdown() + sqs.stop() } } diff --git a/src/test/scala/io/findify/sqsmock/SendReceiveTest.scala b/src/test/scala/io/findify/sqsmock/SendReceiveTest.scala index 5b2799f..4b06365 100644 --- a/src/test/scala/io/findify/sqsmock/SendReceiveTest.scala +++ b/src/test/scala/io/findify/sqsmock/SendReceiveTest.scala @@ -1,7 +1,6 @@ package io.findify.sqsmock -import com.amazonaws.services.sqs.AmazonSQSClient -import org.scalatest.{BeforeAndAfterAll, FlatSpec, Matchers} +import org.scalatest.{FlatSpec, Matchers} import scala.collection.JavaConversions._ /**