Files
CoqDecks/test/controllers/massivedecks/notifications/FakeWebSocketClient.scala
T
2016-06-17 13:52:35 +01:00

118 lines
3.8 KiB
Scala

package controllers.massivedecks.notifications
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Promise
import play.api.libs.iteratee.{Enumerator, Iteratee}
import controllers.massivedecks.notifications.FakeWebSocketClient.{ReceivedBehaviour, SendingBehaviour}
/**
* A fake web socket client that sits on the iteratee/enumerator interface and acts like a websocket.
*/
class FakeWebSocketClient(socket: (Iteratee[String, Unit], Enumerator[String])) {
private val (iteratee, enumerator) = socket
/**
* Connect to the server, following the given behaviours for recieving and sending messages.
* @param receivedBehaviour The behaviour to follow when recieving messages.
* @param sendingBehaviour The behaviour to follow when sending messages initially.
* @param rest Any more behaviours to follow when sending messages after the first finishes.
*/
def connect(receivedBehaviour: ReceivedBehaviour)(sendingBehaviour: SendingBehaviour, rest: SendingBehaviour*): Unit = {
val consumer = receivedBehaviour.consumer
val producer = SendingBehaviour.sequentially(sendingBehaviour :: rest.toList)
// We bind the consumer first to try to ensure we catch responses to our messages from the producer.
enumerator(consumer)
producer(iteratee)
}
}
object FakeWebSocketClient {
/**
* A behaviour to take when receiving messages to the server.
*/
sealed trait ReceivedBehaviour {
def consumer: Iteratee[String, Unit]
}
/**
* Ignore all messages from the server.
*/
case object Ignore extends ReceivedBehaviour {
val consumer = Iteratee.ignore[String]
}
/**
* Call the given callback with each message as it comes.
* @param callback The callback.
*/
case class Callback(callback: (String) => Unit) extends ReceivedBehaviour {
val consumer = Iteratee.foreach(callback)
}
/**
* A custom consumer from any iteratee, where each chunk is a message, and EOF is the socket closing.
* @param consumer The iteratee.
*/
case class Consumer(consumer: Iteratee[String, Unit]) extends ReceivedBehaviour
/**
* A behaviour to take when sending messages to the server.
*/
sealed trait SendingBehaviour {
def producer: Enumerator[String]
}
object SendingBehaviour {
/**
* Sequentially merge the given behaviours into a single enumerator. If no behaviours are given, give an eof.
* @param behaviours The behaviours.
* @return An enumerator.
*/
def sequentially(behaviours: List[SendingBehaviour]): Enumerator[String] =
behaviours match {
case List() => Enumerator.eof
case enumerator :: List() => enumerator.producer
case enumerator :: rest => enumerator.producer andThen sequentially(rest)
}
}
/**
* Close the socket. Future sends will fail.
*/
case object Close extends SendingBehaviour {
val producer = Enumerator.eof[String]
}
/**
* Do nothing, but keep the socket open.
*/
case object Nothing extends SendingBehaviour {
val producer = Enumerator.empty[String]
}
/**
* Send the exact values, keeping the socket open when done.
* @param values The values to send.
*/
case class Values(values: String*) extends SendingBehaviour {
val producer = Enumerator.enumerate(values)
}
/**
* Send the value of the promise when it succeeds.
* @param promise The promise.
*/
case class PromisedValue(promise: Promise[String]) extends SendingBehaviour {
val producer = Enumerator.flatten(promise.future.map(value => Enumerator(value)))
}
/**
* A custom producer from any enumerator, where each chunk is a message, and EOF is the socket closing.
* @param producer Thr enumerator.
*/
case class Producer(producer: Enumerator[String]) extends SendingBehaviour
}