Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

initial import

  • Loading branch information...
commit e71a4a1f32b05f5ff012e97f98ecae5b73855328 1 parent 69a649e
Rached Ben Mustapha authored

Showing 18 changed files with 1,120 additions and 0 deletions. Show diff stats Hide diff stats

  1. +20 0 LICENSE
  2. +21 0 build.sbt
  3. +44 0 src/main/scala/net/benmur/riemann/client/Destination.scala
  4. +71 0 src/main/scala/net/benmur/riemann/client/DomainObjects.scala
  5. +32 0 src/main/scala/net/benmur/riemann/client/EventDSL.scala
  6. +21 0 src/main/scala/net/benmur/riemann/client/EventSenderDSL.scala
  7. +105 0 src/main/scala/net/benmur/riemann/client/ReliableIO.scala
  8. +16 0 src/main/scala/net/benmur/riemann/client/RiemannClient.scala
  9. +63 0 src/main/scala/net/benmur/riemann/client/Serializers.scala
  10. +47 0 src/main/scala/net/benmur/riemann/client/UnreliableIO.scala
  11. +85 0 src/test/scala/net/benmur/riemann/client/EventDSLTest.scala
  12. +50 0 src/test/scala/net/benmur/riemann/client/EventSenderDSLTest.scala
  13. +89 0 src/test/scala/net/benmur/riemann/client/ReliableIOTest.scala
  14. +76 0 src/test/scala/net/benmur/riemann/client/RiemannClientWithDestinationAPITest.scala
  15. +238 0 src/test/scala/net/benmur/riemann/client/SerializersTest.scala
  16. +43 0 src/test/scala/net/benmur/riemann/client/UnreliableIOTest.scala
  17. +36 0 src/test/scala/net/benmur/riemann/client/testingsupport/SerializersFixture.scala
  18. +63 0 src/test/scala/net/benmur/riemann/client/testingsupport/TestingTransportSupport.scala
20 LICENSE
... ... @@ -0,0 +1,20 @@
  1 +Copyright (c) 2012 Rached Ben Mustapha
  2 +
  3 +Permission is hereby granted, free of charge, to any person obtaining
  4 +a copy of this software and associated documentation files (the
  5 +"Software"), to deal in the Software without restriction, including
  6 +without limitation the rights to use, copy, modify, merge, publish,
  7 +distribute, sublicense, and/or sell copies of the Software, and to
  8 +permit persons to whom the Software is furnished to do so, subject to
  9 +the following conditions:
  10 +
  11 +The above copyright notice and this permission notice shall be
  12 +included in all copies or substantial portions of the Software.
  13 +
  14 +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15 +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16 +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17 +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18 +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19 +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20 +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 build.sbt
... ... @@ -0,0 +1,21 @@
  1 +name := "riemann-scala-client"
  2 +
  3 +version := "0.1"
  4 +
  5 +scalaVersion := "2.9.2"
  6 +
  7 +scalacOptions += "-deprecation"
  8 +
  9 +resolvers += "Clojars" at "http://clojars.org/repo"
  10 +
  11 +resolvers += "Akka" at "http://repo.akka.io/releases"
  12 +
  13 +libraryDependencies += "com.aphyr" % "riemann-java-client" % "0.0.6"
  14 +
  15 +libraryDependencies += "com.typesafe.akka" % "akka-actor" % "2.0.4"
  16 +
  17 +libraryDependencies += "org.scalatest" %% "scalatest" % "1.8" % "test"
  18 +
  19 +libraryDependencies += "org.scalamock" %% "scalamock-scalatest-support" % "latest.integration"
  20 +
  21 +libraryDependencies += "com.typesafe.akka" % "akka-testkit" % "2.0.4" % "test"
44 src/main/scala/net/benmur/riemann/client/Destination.scala
... ... @@ -0,0 +1,44 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import java.net.SocketAddress
  4 +
  5 +import scala.annotation.implicitNotFound
  6 +import scala.collection.JavaConversions.iterableAsScalaIterable
  7 +
  8 +import akka.actor.ActorSystem
  9 +import akka.dispatch.Future
  10 +import akka.util.Timeout
  11 +
  12 +trait DestinationOps {
  13 + class DestinationBuilder[T <: TransportType](connectionBuilder: ConnectionBuilder[T])(implicit system: ActorSystem, timeout: Timeout) {
  14 + def to(where: SocketAddress): RiemannDestination[T] =
  15 + new RiemannDestination[T](EventPart(), connectionBuilder.buildConnection(where))
  16 + }
  17 +
  18 + class RiemannDestination[T <: TransportType](baseEvent: EventPart, val connection: Connection[T])(implicit system: ActorSystem, timeout: Timeout)
  19 + extends Destination[T] {
  20 +
  21 + def send(event: EventPart)(implicit messenger: SendOff[T]): Unit =
  22 + messenger.sendOff(connection, Write(
  23 + Serializers.serializeEventPartToProtoMsg(EventDSL.mergeEvents(baseEvent, event))))
  24 +
  25 + def ask(event: EventPart)(implicit messenger: SendAndExpectFeedback[T]): Future[Either[RemoteError, List[EventPart]]] =
  26 + messenger.send(connection, Write(
  27 + Serializers.serializeEventPartToProtoMsg(EventDSL.mergeEvents(baseEvent, event))))
  28 +
  29 + def send(events: Iterable[EventPart])(implicit messenger: SendOff[T]): Unit =
  30 + messenger.sendOff(connection, Write(
  31 + Serializers.serializeEventPartsToProtoMsg(events map (EventDSL.mergeEvents(baseEvent, _)))))
  32 +
  33 + def ask(events: Iterable[EventPart])(implicit messenger: SendAndExpectFeedback[T]): Future[Either[RemoteError, List[EventPart]]] =
  34 + messenger.send(connection, Write(
  35 + Serializers.serializeEventPartsToProtoMsg(events map (EventDSL.mergeEvents(baseEvent, _)))))
  36 +
  37 + def ask(query: Query)(implicit messenger: SendAndExpectFeedback[T]): Future[Either[RemoteError, List[EventPart]]] =
  38 + messenger.send(connection, Write(
  39 + Serializers.serializeQueryToProtoMsg(query)))
  40 +
  41 + def withValues(event: EventPart): RiemannDestination[T] =
  42 + new RiemannDestination[T](EventDSL.mergeEvents(baseEvent, event), connection)
  43 + }
  44 +}
71 src/main/scala/net/benmur/riemann/client/DomainObjects.scala
... ... @@ -0,0 +1,71 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import java.io.{ InputStream, OutputStream }
  4 +import java.net.SocketAddress
  5 +import scala.annotation.implicitNotFound
  6 +import com.aphyr.riemann.Proto
  7 +import akka.actor.ActorSystem
  8 +import akka.dispatch.Future
  9 +import akka.util.Timeout
  10 +import scala.collection.mutable.WrappedArray
  11 +
  12 +case class EventPart(
  13 + host: Option[String] = None,
  14 + service: Option[String] = None,
  15 + state: Option[String] = None,
  16 + time: Option[Long] = None,
  17 + description: Option[String] = None,
  18 + tags: Iterable[String] = Nil,
  19 + metric: Option[AnyVal] = None,
  20 + ttl: Option[Float] = None)
  21 +
  22 +case class Query(q: String)
  23 +
  24 +case class Write(m: Proto.Msg)
  25 +
  26 +case class RemoteError(message: String) extends Throwable
  27 +
  28 +trait TransportType {
  29 + type SocketFactory
  30 +}
  31 +trait Reliable extends TransportType {
  32 + type SocketFactory = SocketAddress => ConnectedSocketWrapper
  33 +}
  34 +trait Unreliable extends TransportType {
  35 + type SocketFactory = SocketAddress => UnconnectedSocketWrapper
  36 +}
  37 +
  38 +trait Connection[T <: TransportType]
  39 +
  40 +trait ConnectedSocketWrapper {
  41 + def inputStream: InputStream
  42 + def outputStream: OutputStream
  43 +}
  44 +
  45 +trait UnconnectedSocketWrapper {
  46 + def send(data: WrappedArray[Byte]): Unit
  47 +}
  48 +
  49 +trait Destination[T <: TransportType] {
  50 + def send(event: EventPart)(implicit messenger: SendOff[T]): Unit
  51 + def ask(event: EventPart)(implicit messenger: SendAndExpectFeedback[T]): Future[Either[RemoteError, List[EventPart]]]
  52 + def send(events: Iterable[EventPart])(implicit messenger: SendOff[T]): Unit
  53 + def ask(events: Iterable[EventPart])(implicit messenger: SendAndExpectFeedback[T]): Future[Either[RemoteError, List[EventPart]]]
  54 + def ask(query: Query)(implicit messenger: SendAndExpectFeedback[T]): Future[Either[RemoteError, List[EventPart]]]
  55 + def withValues(event: EventPart): Destination[T]
  56 +}
  57 +
  58 +@implicitNotFound(msg = "No way of building a connection to Riemann of type ${T}.")
  59 +trait ConnectionBuilder[T <: TransportType] {
  60 + def buildConnection(where: SocketAddress, factory: Option[T#SocketFactory] = None, dispatcherId: Option[String] = None)(implicit system: ActorSystem, timeout: Timeout): Connection[T]
  61 +}
  62 +
  63 +@implicitNotFound(msg = "Connection type ${T} does not allow sending to Riemann because there is no implicit in scope returning a implementation of SendOff[${T}].")
  64 +trait SendOff[T <: TransportType] {
  65 + def sendOff(connection: Connection[T], command: Write): Unit
  66 +}
  67 +
  68 +@implicitNotFound(msg = "Connection type ${T} does not allow getting feedback from Riemann.")
  69 +trait SendAndExpectFeedback[T <: TransportType] {
  70 + def send(connection: Connection[T], command: Write)(implicit system: ActorSystem, timeout: Timeout): Future[Either[RemoteError, List[EventPart]]]
  71 +}
32 src/main/scala/net/benmur/riemann/client/EventDSL.scala
... ... @@ -0,0 +1,32 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +trait EventDSL {
  4 + def mergeEvents(e: EventPart, overlay: EventPart) = EventPart(
  5 + overlay.host orElse e.host,
  6 + overlay.service orElse e.service,
  7 + overlay.state orElse e.state,
  8 + overlay.time orElse e.time,
  9 + overlay.description orElse e.description,
  10 + (overlay.tags.toSet ++ e.tags).toSeq.sorted,
  11 + overlay.metric orElse e.metric,
  12 + overlay.ttl orElse e.ttl)
  13 +
  14 + class EventPartCombinator(e: EventPart) {
  15 + def |(overlay: EventPart) = mergeEvents(e, overlay)
  16 + }
  17 +
  18 + implicit def eventPartToEventPartCombinator(e: EventPart) = new EventPartCombinator(e)
  19 +
  20 + def host(s: String) = EventPart(host = Some(s))
  21 + def service(s: String) = EventPart(service = Some(s))
  22 + def state(s: String) = EventPart(state = Some(s))
  23 + def time(l: Long) = EventPart(time = Some(l))
  24 + def description(s: String) = EventPart(description = Some(s))
  25 + def tags(s: String*) = EventPart(tags = s)
  26 + def metric(m: Long) = EventPart(metric = Some(m))
  27 + def metric(m: Float) = EventPart(metric = Some(m))
  28 + def metric(m: Double) = EventPart(metric = Some(m))
  29 + def ttl(f: Float) = EventPart(ttl = Some(f))
  30 +}
  31 +
  32 +object EventDSL extends EventDSL
21 src/main/scala/net/benmur/riemann/client/EventSenderDSL.scala
... ... @@ -0,0 +1,21 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +trait EventSenderDSL {
  4 + class EventSenderOff[T <: TransportType](e: EventPart)(implicit messenger: SendOff[T]) {
  5 + def |>>(d: Destination[T]) = d send e
  6 + }
  7 +
  8 + class EventSender[T <: TransportType](e: EventPart)(implicit messenger: SendAndExpectFeedback[T]) {
  9 + def |><(d: Destination[T]) = d ask e
  10 + }
  11 +
  12 + class QuerySender[T <: TransportType](q: Query)(implicit messenger: SendAndExpectFeedback[T]) {
  13 + def |><(d: Destination[T]) = d ask q
  14 + }
  15 +
  16 + implicit def event2EventSenderOff[T <: TransportType](e: EventPart)(implicit messenger: SendOff[T]) = new EventSenderOff[T](e)
  17 + implicit def event2EventSender[T <: TransportType](e: EventPart)(implicit messenger: SendAndExpectFeedback[T]) = new EventSender[T](e)
  18 + implicit def query2QuerySender[T <: TransportType](q: Query)(implicit messenger: SendAndExpectFeedback[T]) = new QuerySender[T](q)
  19 +}
  20 +
  21 +object EventSenderDSL extends EventSenderDSL
105 src/main/scala/net/benmur/riemann/client/ReliableIO.scala
... ... @@ -0,0 +1,105 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import java.io.{ DataInputStream, DataOutputStream }
  4 +import java.net.{ Socket, SocketAddress }
  5 +import java.util.concurrent.atomic.AtomicLong
  6 +import com.aphyr.riemann.Proto
  7 +import akka.actor.{ Actor, ActorLogging, ActorSystem, Props, actorRef2Scala }
  8 +import akka.dispatch.Future
  9 +import akka.pattern.ask
  10 +import akka.util.Timeout
  11 +import akka.dispatch.Promise
  12 +import akka.actor.OneForOneStrategy
  13 +import akka.actor.SupervisorStrategy._
  14 +import akka.util.duration._
  15 +import akka.actor.ActorRef
  16 +import java.net.SocketException
  17 +
  18 +trait ReliableIO {
  19 + private val nClients = new AtomicLong(0L) // FIXME this should be more global
  20 +
  21 + private[this] class ReliableConnectionActor(where: SocketAddress, factory: Reliable#SocketFactory, dispatcherId: Option[String])(implicit system: ActorSystem) extends Actor {
  22 + override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 36000, withinTimeRange = 1 hour) { // This needs to be more reasonable
  23 + case _ => Restart
  24 + }
  25 +
  26 + val props = {
  27 + val p = Props(new TcpConnectionActor(where, factory))
  28 + if (dispatcherId.isEmpty) p else p.withDispatcher(dispatcherId.get)
  29 + }
  30 +
  31 + val ioActor = context.actorOf(props, "io")
  32 +
  33 + def receive = {
  34 + case message => ioActor forward message
  35 + }
  36 + }
  37 +
  38 + implicit object ReliableSendAndExpectFeedback extends SendAndExpectFeedback[Reliable] {
  39 + def send(connection: Connection[Reliable], command: Write)(implicit system: ActorSystem, timeout: Timeout): Future[Either[RemoteError, List[EventPart]]] =
  40 + connection match {
  41 + case rc: ReliableConnection =>
  42 + (rc.ioActor ask command).mapTo[Either[RemoteError, List[EventPart]]]
  43 + case c =>
  44 + Promise.successful(Left(RemoteError(
  45 + "don't know how to send data to " + c.getClass.getName)))
  46 + }
  47 + }
  48 +
  49 + implicit object ReliableSendOff extends SendOff[Reliable] {
  50 + def sendOff(connection: Connection[Reliable], command: Write): Unit = connection match {
  51 + case rc: ReliableConnection =>
  52 + rc.ioActor tell command
  53 + case c =>
  54 + System.err.println(
  55 + "don't know how to send data to " + c.getClass.getName)
  56 + }
  57 + }
  58 +
  59 + class TcpConnectionActor(where: SocketAddress, factory: Reliable#SocketFactory) extends Actor with ActorLogging {
  60 + val connection = factory(where)
  61 + val outputStream = new DataOutputStream(connection.outputStream)
  62 + val inputStream = new DataInputStream(connection.inputStream)
  63 + println("actor init")
  64 + def receive = {
  65 + case Write(msg) =>
  66 + try {
  67 + val ab = msg.toByteArray
  68 + outputStream writeInt ab.length
  69 + outputStream write ab
  70 + outputStream.flush
  71 + val buf = Array.ofDim[Byte](inputStream.readInt())
  72 + inputStream.readFully(buf)
  73 + sender ! Serializers.unserializeProtoMsg(Proto.Msg.parseFrom(buf))
  74 + } catch {
  75 + case e: SocketException => throw e
  76 + case exception =>
  77 + log.error(exception, "could not send or receive data")
  78 + sender ! Left(RemoteError(exception.getMessage()))
  79 + }
  80 + }
  81 + }
  82 +
  83 + val makeTcpConnection: Reliable#SocketFactory = (addr) => {
  84 + val socket = new Socket()
  85 + socket.connect(addr)
  86 + new ConnectedSocketWrapper {
  87 + override def outputStream = socket.getOutputStream()
  88 + override def inputStream = socket.getInputStream()
  89 + }
  90 + }
  91 +
  92 + class ReliableConnection(val ioActor: ActorRef) extends Connection[Reliable]
  93 +
  94 + implicit object TwoWayConnectionBuilder extends ConnectionBuilder[Reliable] {
  95 + def buildConnection(where: SocketAddress, factory: Option[Reliable#SocketFactory] = None, dispatcherId: Option[String])(implicit system: ActorSystem, timeout: Timeout): Connection[Reliable] = {
  96 + val props = {
  97 + val p = Props(new ReliableConnectionActor(where, factory getOrElse makeTcpConnection, dispatcherId))
  98 + if (dispatcherId.isEmpty) p else p.withDispatcher(dispatcherId.get)
  99 + }
  100 + new ReliableConnection(system.actorOf(props, "riemann-tcp-client-" + nClients.incrementAndGet))
  101 + }
  102 + }
  103 +}
  104 +
  105 +object ReliableIO extends ReliableIO
16 src/main/scala/net/benmur/riemann/client/RiemannClient.scala
... ... @@ -0,0 +1,16 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import akka.actor.ActorSystem
  4 +import akka.util.Timeout
  5 +
  6 +object RiemannClient
  7 + extends EventDSL
  8 + with EventSenderDSL
  9 + with Serializers
  10 + with ReliableIO
  11 + with UnreliableIO
  12 + with DestinationOps {
  13 +
  14 + def riemannConnectAs[T <: TransportType](implicit connectionBuilder: ConnectionBuilder[T], system: ActorSystem, timeout: Timeout): DestinationBuilder[T] =
  15 + new DestinationBuilder[T](connectionBuilder)
  16 +}
63 src/main/scala/net/benmur/riemann/client/Serializers.scala
... ... @@ -0,0 +1,63 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import scala.Option.option2Iterable
  4 +import scala.collection.JavaConversions.{ asJavaIterable, iterableAsScalaIterable }
  5 +
  6 +import com.aphyr.riemann.Proto
  7 +
  8 +trait Serializers {
  9 + def serializeQueryToProtoMsg(q: Query) = Proto.Msg.newBuilder
  10 + .setQuery(Proto.Query.newBuilder().setString(q.q))
  11 + .build
  12 +
  13 + def serializeEventPartToProtoMsg(e: EventPart) = serializeEventPartsToProtoMsg(Some(e))
  14 +
  15 + def serializeEventPartsToProtoMsg(ei: Iterable[EventPart]) = Proto.Msg.newBuilder
  16 + .addAllEvents(ei map convertOneEventPart)
  17 + .build
  18 +
  19 + def unserializeProtoMsg(m: Proto.Msg): Either[RemoteError, List[EventPart]] = m.hasOk match {
  20 + case true if m.getOk => Right(m.getEventsList map convertProtoEventToEventPart toList)
  21 + case true => Left(RemoteError(m.getError))
  22 + case false => Left(RemoteError("Response has no status"))
  23 + }
  24 +
  25 + private def convertOneEventPart(e: EventPart) = {
  26 + val b = Proto.Event.newBuilder
  27 + e.host foreach (b.setHost(_))
  28 + e.service foreach (b.setService(_))
  29 + e.state foreach (b.setState(_))
  30 + e.time foreach (b.setTime(_))
  31 + e.description foreach (b.setDescription(_))
  32 + e.tags foreach (b.addTags(_))
  33 + e.metric foreach (_ match {
  34 + case value: Long => b.setMetricSint64(value)
  35 + case value: Double => b.setMetricD(value)
  36 + case value: Float => b.setMetricF(value)
  37 + case v => System.err.println("Warning: don't know what to do with value " + v)
  38 + })
  39 + e.ttl foreach (b.setTtl(_))
  40 + b.build
  41 + }
  42 +
  43 + private def convertProtoEventToEventPart(e: Proto.Event) = EventPart(
  44 + host = if (e.hasHost) Some(e.getHost()) else None,
  45 + service = if (e.hasService) Some(e.getService) else None,
  46 + state = if (e.hasState) Some(e.getState) else None,
  47 + time = if (e.hasTime) Some(e.getTime) else None,
  48 + description = if (e.hasDescription) Some(e.getDescription) else None,
  49 + tags = if (e.getTagsList.isEmpty) List() else e.getTagsList.toList,
  50 + metric = extractMetric(e),
  51 + ttl = if (e.hasTtl) Some(e.getTtl) else None)
  52 +
  53 + private def extractMetric(e: Proto.Event) =
  54 + if (e.hasMetricD)
  55 + Some(e.getMetricD)
  56 + else if (e.hasMetricF)
  57 + Some(e.getMetricF)
  58 + else if (e.hasMetricSint64)
  59 + Some(e.getMetricSint64)
  60 + else None
  61 +}
  62 +
  63 +object Serializers extends Serializers
47 src/main/scala/net/benmur/riemann/client/UnreliableIO.scala
... ... @@ -0,0 +1,47 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import java.net.{ DatagramPacket, DatagramSocket, SocketAddress }
  4 +import java.util.concurrent.atomic.AtomicLong
  5 +import akka.actor.{ Actor, ActorSystem, Props }
  6 +import akka.util.Timeout
  7 +import scala.collection.mutable.WrappedArray
  8 +
  9 +trait UnreliableIO {
  10 + private val nClients = new AtomicLong(0L) // FIXME this should be more global
  11 +
  12 + class UnreliableConnection(where: SocketAddress, factory: Unreliable#SocketFactory, dispatcherId: Option[String] = None)(implicit system: ActorSystem) extends Connection[Unreliable] {
  13 + val props = {
  14 + val p = Props(new UnconnectedConnectionActor(where, factory))
  15 + if (dispatcherId.isEmpty) p else p.withDispatcher(dispatcherId.get)
  16 + }
  17 + val ioActor = system.actorOf(props, "riemann-udp-client-" + nClients.incrementAndGet)
  18 + }
  19 +
  20 + implicit object UnreliableSendOff extends SendOff[Unreliable] {
  21 + def sendOff(connection: Connection[Unreliable], command: Write): Unit = connection match {
  22 + case uc: UnreliableConnection => uc.ioActor tell command
  23 + case c => System.err.println("don't know how to send data to " + c.getClass.getName)
  24 + }
  25 + }
  26 +
  27 + private[this] class UnconnectedConnectionActor(where: SocketAddress, factory: Unreliable#SocketFactory) extends Actor {
  28 + val connection = factory(where)
  29 + def receive = {
  30 + case Write(msg) => connection send msg.toByteArray
  31 + }
  32 + }
  33 +
  34 + val makeUdpConnection: Unreliable#SocketFactory = (addr) => {
  35 + val dest = new DatagramSocket(addr)
  36 + new UnconnectedSocketWrapper {
  37 + override def send(data: WrappedArray[Byte]) = dest send new DatagramPacket(data.array, data.length)
  38 + }
  39 + }
  40 +
  41 + implicit object OneWayConnectionBuilder extends ConnectionBuilder[Unreliable] {
  42 + implicit def buildConnection(where: SocketAddress, factory: Option[Unreliable#SocketFactory], dispatcherId: Option[String])(implicit system: ActorSystem, timeout: Timeout): Connection[Unreliable] =
  43 + new UnreliableConnection(where, factory getOrElse makeUdpConnection, dispatcherId)
  44 + }
  45 +}
  46 +
  47 +object UnreliableIO extends UnreliableIO
85 src/test/scala/net/benmur/riemann/client/EventDSLTest.scala
... ... @@ -0,0 +1,85 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import org.scalatest.FunSuite
  4 +
  5 +class EventDSLTest extends FunSuite {
  6 + import EventDSL._
  7 +
  8 + test("provide an EventPart builder function for host") {
  9 + expect(EventPart(host = Some("h"))) {
  10 + host("h")
  11 + }
  12 + }
  13 +
  14 + test("provide an EventPart builder function for service") {
  15 + expect(EventPart(service = Some("se"))) {
  16 + service("se")
  17 + }
  18 + }
  19 +
  20 + test("provide an EventPart builder function for state") {
  21 + expect(EventPart(state = Some("st"))) {
  22 + state("st")
  23 + }
  24 + }
  25 +
  26 + test("provide an EventPart builder function for time") {
  27 + expect(EventPart(time = Some(1234L))) {
  28 + time(1234L)
  29 + }
  30 + }
  31 +
  32 + test("provide an EventPart builder function for description") {
  33 + expect(EventPart(description = Some("d"))) {
  34 + description("d")
  35 + }
  36 + }
  37 +
  38 + test("provide an EventPart builder function for tags") {
  39 + expect(EventPart(tags = Array("t1", "t2"))) {
  40 + EventDSL.tags("t1", "t2")
  41 + }
  42 + }
  43 +
  44 + test("provide an EventPart builder function for metric (float)") {
  45 + expect(EventPart(metric = Some(1.12f))) {
  46 + metric(1.12f)
  47 + }
  48 + }
  49 +
  50 + test("provide an EventPart builder function for metric (double)") {
  51 + expect(EventPart(metric = Some(1.12))) {
  52 + metric(1.12)
  53 + }
  54 + }
  55 +
  56 + test("provide an EventPart builder function for metric (long)") {
  57 + expect(EventPart(metric = Some(112L))) {
  58 + metric(112L)
  59 + }
  60 + }
  61 +
  62 + test("provide an EventPart builder function for ttl") {
  63 + expect(EventPart(ttl = Some(10))) {
  64 + ttl(10)
  65 + }
  66 + }
  67 +
  68 + test("EventParts combination merges tags") {
  69 + expect(EventPart(tags = Array("tag1", "tag2", "tag3"))) {
  70 + EventDSL.tags("tag1", "tag2") | EventDSL.tags() | EventDSL.tags("tag3") | EventDSL.tags("tag1")
  71 + }
  72 + }
  73 +
  74 + test("provide a method to combine EventParts") {
  75 + val expected = EventPart(host = Some("server"), service = Some("service-name"),
  76 + state = Some("ok"), time = Some(1234L), description = Some("descript"),
  77 + tags = Array("tag1", "tag2"), metric = Some(112L), ttl = Some(10L))
  78 +
  79 + expect(expected) {
  80 + ttl(10) | metric(112) | EventDSL.tags("tag1", "tag2") | description("descript") |
  81 + time(1234L) | state("discarded-state") | state("ok") | service("service-name") |
  82 + host("server")
  83 + }
  84 + }
  85 +}
50 src/test/scala/net/benmur/riemann/client/EventSenderDSLTest.scala
... ... @@ -0,0 +1,50 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import org.scalatest.{ BeforeAndAfterAll, FunSuite }
  4 +import com.aphyr.riemann.Proto
  5 +import akka.actor.ActorSystem
  6 +import net.benmur.riemann.client.testingsupport.TestingTransportSupport
  7 +
  8 +class EventSenderDSLTest extends FunSuite with BeforeAndAfterAll {
  9 + import TestingTransportSupport._
  10 + import EventSenderDSL._
  11 +
  12 + implicit val system = ActorSystem()
  13 +
  14 + override def afterAll {
  15 + system.shutdown
  16 + }
  17 +
  18 + def makeDestination = {
  19 + val conn = new TestingTransportConnection
  20 + val dest = new RiemannDestination[TestingTransport](EventPart(), conn)
  21 + (conn, dest)
  22 + }
  23 +
  24 + test("DSL operator to send an event without expecting a result") {
  25 + val (conn, dest) = makeDestination
  26 +
  27 + expect(Write(protoMsgEvent)) {
  28 + event |>> dest
  29 + conn.sentOff
  30 + }
  31 + }
  32 +
  33 + test("DSL operator to send operator to send an event expecting a status") {
  34 + val (conn, dest) = makeDestination
  35 +
  36 + expect(Write(protoMsgEvent)) {
  37 + event |>< dest
  38 + conn.sentExpect
  39 + }
  40 + }
  41 +
  42 + test("DSL operator to send operator to send a query expecting a status") {
  43 + val (conn, dest) = makeDestination
  44 +
  45 + expect(Write(protoMsgQuery)) {
  46 + Query("true") |>< dest
  47 + conn.sentExpect
  48 + }
  49 + }
  50 +}
89 src/test/scala/net/benmur/riemann/client/ReliableIOTest.scala
... ... @@ -0,0 +1,89 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import org.scalatest.FunSuite
  4 +import net.benmur.riemann.client.testingsupport.TestingTransportSupport
  5 +import akka.actor.ActorSystem
  6 +import java.net.InetSocketAddress
  7 +import java.net.Socket
  8 +import org.scalatest.BeforeAndAfterAll
  9 +import org.scalamock.scalatest.MockFactory
  10 +import org.scalamock.ProxyMockFactory
  11 +import org.scalatest.matchers.ShouldMatchers
  12 +import java.net.SocketAddress
  13 +import akka.testkit.CallingThreadDispatcher
  14 +import org.scalamock.annotation.mock
  15 +import java.io.ByteArrayInputStream
  16 +import java.io.ByteArrayOutputStream
  17 +import java.io.DataInputStream
  18 +import java.io.DataOutputStream
  19 +import com.aphyr.riemann.Proto
  20 +import akka.dispatch.Await
  21 +import akka.util.duration._
  22 +
  23 +class ReliableIOTest extends FunSuite
  24 + with BeforeAndAfterAll
  25 + with MockFactory
  26 + with ProxyMockFactory
  27 + with ShouldMatchers {
  28 +
  29 + import ReliableIO._
  30 + import TestingTransportSupport._
  31 +
  32 + implicit val system = ActorSystem()
  33 + val address = new InetSocketAddress(0)
  34 +
  35 + override def afterAll {
  36 + system.shutdown
  37 + }
  38 +
  39 + test("sending a protobuf Msg") {
  40 + val in = Array.ofDim[Byte](256)
  41 + val ios = new ByteArrayInputStream(in)
  42 + val oos = new ByteArrayOutputStream()
  43 +
  44 + val wrapper = mock[ConnectedSocketWrapper]
  45 + wrapper expects 'outputStream returning oos once;
  46 + wrapper expects 'inputStream returning ios once
  47 +
  48 + val socketFactory = mockFunction[SocketAddress, ConnectedSocketWrapper]
  49 + socketFactory expects address returning wrapper once
  50 +
  51 + val conn = implicitly[ConnectionBuilder[Reliable]].buildConnection(address, Some(socketFactory), Some(CallingThreadDispatcher.Id))
  52 + implicitly[SendOff[Reliable]].sendOff(conn, Write(protoMsgEvent))
  53 +
  54 + val out = oos.toByteArray
  55 + val outRef = protoMsgEvent.toByteArray
  56 + new DataInputStream(new ByteArrayInputStream(out)).readInt should be === outRef.length
  57 + out.slice(4, out.length) should be === outRef
  58 + }
  59 +
  60 + test("sending a protobuf Msg, with feedback") {
  61 + val response = Proto.Msg.newBuilder.setOk(true).build
  62 + val responseBytes = response.toByteArray
  63 +
  64 + val outBuilder = new ByteArrayOutputStream()
  65 + val outBuilderData = new DataOutputStream(outBuilder)
  66 + outBuilderData.writeInt(responseBytes.length)
  67 + outBuilderData.write(responseBytes)
  68 +
  69 + val oos = new ByteArrayOutputStream()
  70 +
  71 + val wrapper = mock[ConnectedSocketWrapper]
  72 + wrapper expects 'outputStream returning oos once;
  73 + wrapper expects 'inputStream returning new ByteArrayInputStream(outBuilder.toByteArray) once
  74 +
  75 + val socketFactory = mockFunction[SocketAddress, ConnectedSocketWrapper]
  76 + socketFactory expects address returning wrapper once
  77 +
  78 + val conn = implicitly[ConnectionBuilder[Reliable]].buildConnection(address, Some(socketFactory), Some(CallingThreadDispatcher.Id))
  79 + val respFuture = implicitly[SendAndExpectFeedback[Reliable]].send(conn, Write(protoMsgEvent))
  80 +
  81 + val out = oos.toByteArray
  82 + val outRef = protoMsgEvent.toByteArray
  83 + new DataInputStream(new ByteArrayInputStream(out)).readInt should be === outRef.length
  84 + out.slice(4, out.length) should be === outRef
  85 +
  86 + val resp = Await.result(respFuture, 1 second)
  87 + resp should be === Right(Nil)
  88 + }
  89 +}
76 src/test/scala/net/benmur/riemann/client/RiemannClientWithDestinationAPITest.scala
... ... @@ -0,0 +1,76 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import org.scalatest.FunSuite
  4 +import net.benmur.riemann.client.testingsupport.TestingTransportSupport
  5 +import akka.actor.ActorSystem
  6 +import org.scalatest.BeforeAndAfterAll
  7 +import java.net.InetSocketAddress
  8 +import org.scalatest.matchers.ShouldMatchers
  9 +
  10 +class RiemannClientWithDestinationAPITest extends FunSuite with BeforeAndAfterAll with ShouldMatchers {
  11 + import RiemannClient._
  12 + import TestingTransportSupport._
  13 +
  14 + implicit val system = ActorSystem()
  15 + val address = new InetSocketAddress(0)
  16 +
  17 + override def afterAll {
  18 + system.shutdown
  19 + }
  20 +
  21 + test("entry point to create a connection (pristine state)") {
  22 + val dest = riemannConnectAs[TestingTransport] to address
  23 + val conn = dest.connection.asInstanceOf[TestingTransportConnection]
  24 +
  25 + conn.where should be theSameInstanceAs address
  26 + conn.sentOff should be === null
  27 + }
  28 +
  29 + test("entry point to create a connection (sending an event)") {
  30 + val dest = riemannConnectAs[TestingTransport] to address
  31 + val conn = dest.connection.asInstanceOf[TestingTransportConnection]
  32 +
  33 + dest send event
  34 + conn.sentOff should be === Write(protoMsgEvent)
  35 + }
  36 +
  37 + test("entry point to create a connection (sending multiple events)") {
  38 + val dest = riemannConnectAs[TestingTransport] to address
  39 + val conn = dest.connection.asInstanceOf[TestingTransportConnection]
  40 +
  41 + dest send List(event, event2)
  42 + conn.sentOff should be === Write(protoMsgEvents)
  43 + }
  44 +
  45 + test("entry point to create a connection (sending an event expecting feedback)") {
  46 + val dest = riemannConnectAs[TestingTransport] to address
  47 + val conn = dest.connection.asInstanceOf[TestingTransportConnection]
  48 +
  49 + dest ask event
  50 + conn.sentExpect should be === Write(protoMsgEvent)
  51 + }
  52 +
  53 + test("entry point to create a connection (sending multiple events expecting feedback)") {
  54 + val dest = riemannConnectAs[TestingTransport] to address
  55 + val conn = dest.connection.asInstanceOf[TestingTransportConnection]
  56 +
  57 + dest ask List(event, event2)
  58 + conn.sentExpect should be === Write(protoMsgEvents)
  59 + }
  60 +
  61 + test("entry point to create a connection (sending an query)") {
  62 + val dest = riemannConnectAs[TestingTransport] to address
  63 + val conn = dest.connection.asInstanceOf[TestingTransportConnection]
  64 +
  65 + dest ask Query("true")
  66 + conn.sentExpect should be === Write(protoMsgQuery)
  67 + }
  68 +
  69 + test("entry point to create a connection (sending an event, combining default EventPart values)") {
  70 + val dest = riemannConnectAs[TestingTransport] to address withValues (host("h") | service("s"))
  71 + val conn = dest.connection.asInstanceOf[TestingTransportConnection]
  72 +
  73 + dest send state("ok")
  74 + conn.sentOff should be === Write(protoMsgEvent)
  75 + }
  76 +}
238 src/test/scala/net/benmur/riemann/client/SerializersTest.scala
... ... @@ -0,0 +1,238 @@
  1 +package net.benmur.riemann.client
  2 +import org.scalatest.FunSuite
  3 +import com.aphyr.riemann.Proto
  4 +import net.benmur.riemann.client.testingsupport.SerializersFixture
  5 +import scala.collection.JavaConversions.asJavaIterable
  6 +
  7 +class SerializersTest extends FunSuite {
  8 + import Serializers._
  9 + import SerializersFixture._
  10 +
  11 + test("out: convert a full EventPart to a protobuf Msg") {
  12 + val expected = Proto.Msg.newBuilder.addEvents(protobufEvent1).build
  13 +
  14 + expect(expected) {
  15 + serializeEventPartToProtoMsg(event1)
  16 + }
  17 + }
  18 +
  19 + test("out: convert an empty EventPart to a protobuf Msg") {
  20 + val expected = Proto.Msg.newBuilder.addEvents(
  21 + Proto.Event.newBuilder).build
  22 +
  23 + expect(expected) {
  24 + serializeEventPartToProtoMsg(EventPart())
  25 + }
  26 + }
  27 +
  28 + test("out: convert an EventPart with only host to a protobuf Msg") {
  29 + val expected = Proto.Msg.newBuilder.addEvents(
  30 + Proto.Event.newBuilder.setHost("host")).build
  31 +
  32 + expect(expected) {
  33 + serializeEventPartToProtoMsg(EventPart(host = Some("host")))
  34 + }
  35 + }
  36 +
  37 + test("out: convert an EventPart with only service to a protobuf Msg") {
  38 + val expected = Proto.Msg.newBuilder.addEvents(
  39 + Proto.Event.newBuilder.setService("service")).build
  40 +
  41 + expect(expected) {
  42 + serializeEventPartToProtoMsg(EventPart(service = Some("service")))
  43 + }
  44 + }
  45 +
  46 + test("out: convert an EventPart with only state to a protobuf Msg") {
  47 + val expected = Proto.Msg.newBuilder.addEvents(
  48 + Proto.Event.newBuilder.setState("state")).build
  49 +
  50 + expect(expected) {
  51 + serializeEventPartToProtoMsg(EventPart(state = Some("state")))
  52 + }
  53 + }
  54 +
  55 + test("out: convert an EventPart with only time to a protobuf Msg") {
  56 + val expected = Proto.Msg.newBuilder.addEvents(
  57 + Proto.Event.newBuilder.setTime(1234L)).build
  58 +
  59 + expect(expected) {
  60 + serializeEventPartToProtoMsg(EventPart(time = Some(1234L)))
  61 + }
  62 + }
  63 +
  64 + test("out: convert an EventPart with only description to a protobuf Msg") {
  65 + val expected = Proto.Msg.newBuilder.addEvents(
  66 + Proto.Event.newBuilder.setDescription("description")).build
  67 +
  68 + expect(expected) {
  69 + serializeEventPartToProtoMsg(EventPart(description = Some("description")))
  70 + }
  71 + }
  72 +
  73 + test("out: convert an EventPart with only tags to a protobuf Msg") {
  74 + val expected = Proto.Msg.newBuilder.addEvents(
  75 + Proto.Event.newBuilder.addAllTags(List("tag1"))).build
  76 +
  77 + expect(expected) {
  78 + serializeEventPartToProtoMsg(EventPart(tags = List("tag1")))
  79 + }
  80 + }
  81 +
  82 + test("out: convert an EventPart with only metric (long) to a protobuf Msg") {
  83 + val expected = Proto.Msg.newBuilder.addEvents(
  84 + Proto.Event.newBuilder.setMetricSint64(1234L)).build
  85 +
  86 + expect(expected) {
  87 + serializeEventPartToProtoMsg(EventPart(metric = Some(1234L)))
  88 + }
  89 + }
  90 +
  91 + test("out: convert an EventPart with only metric (double) to a protobuf Msg") {
  92 + val expected = Proto.Msg.newBuilder.addEvents(
  93 + Proto.Event.newBuilder.setMetricD(1234.9)).build
  94 +
  95 + expect(expected) {
  96 + serializeEventPartToProtoMsg(EventPart(metric = Some(1234.9)))
  97 + }
  98 + }
  99 +
  100 + test("out: convert an EventPart with only metric (float) to a protobuf Msg") {
  101 + val expected = Proto.Msg.newBuilder.addEvents(
  102 + Proto.Event.newBuilder.setMetricF(1234.9f)).build
  103 +
  104 + expect(expected) {
  105 + serializeEventPartToProtoMsg(EventPart(metric = Some(1234.9f)))
  106 + }
  107 + }
  108 +
  109 + test("out: convert an EventPart with only ttl to a protobuf Msg") {
  110 + val expected = Proto.Msg.newBuilder.addEvents(
  111 + Proto.Event.newBuilder.setTtl(1234L)).build
  112 +
  113 + expect(expected) {
  114 + serializeEventPartToProtoMsg(EventPart(ttl = Some(1234L)))
  115 + }
  116 + }
  117 +
  118 + test("out: convert an Iterable of full EventParts to a protobuf Msg") {
  119 + val expected = Proto.Msg.newBuilder.addEvents(protobufEvent1).addEvents(protobufEvent2).build
  120 +
  121 + expect(expected) {
  122 + serializeEventPartsToProtoMsg(List(event1, event2))
  123 + }
  124 + }
  125 +
  126 + test("out: convert a Query to a protobuf Msg") {
  127 + expect(Proto.Msg.newBuilder.setQuery(Proto.Query.newBuilder.setString("true")).build) {
  128 + serializeQueryToProtoMsg(Query("true"))
  129 + }
  130 + }
  131 +
  132 + test("in: convert a protobuf Msg response with an ok status") {
  133 + expect(Right(Nil)) {
  134 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).build)
  135 + }
  136 + }
  137 +
  138 + test("in: convert a protobuf Msg response with a non-ok status and an error message") {
  139 + expect(Left(RemoteError("meh"))) {
  140 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(false).setError("meh").build)
  141 + }
  142 + }
  143 +
  144 + test("in: convert a failed Query result from a protobuf Msg with events") {
  145 + expect(Left(RemoteError("meh"))) {
  146 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(false).setError("meh").addEvents(protobufEvent1).build)
  147 + }
  148 + }
  149 +
  150 + test("in: convert a successful Query result from a protobuf Msg to multiple EventParts") {
  151 + expect(Right(List(event1))) {
  152 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(protobufEvent1).build)
  153 + }
  154 + }
  155 +
  156 + test("in: convert Query result with missing ok from a protobuf Msg to RemoteError") {
  157 + expect(Left(RemoteError("Response has no status"))) {
  158 + unserializeProtoMsg(Proto.Msg.newBuilder.addEvents(protobufEvent1).build)
  159 + }
  160 + }
  161 +
  162 + test("in: convert a protobuf Msg with empty Event to a Right(List(EventPart))") {
  163 + expect(Right(List(EventPart()))) {
  164 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  165 + Proto.Event.newBuilder).build)
  166 + }
  167 + }
  168 +
  169 + test("in: convert a protobuf Msg with Event with only host to a Right(List(EventPart))") {
  170 + expect(Right(List(EventPart(host = Some("host"))))) {
  171 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  172 + Proto.Event.newBuilder.setHost("host")).build)
  173 + }
  174 + }
  175 +
  176 + test("in: convert a protobuf Msg with Event with only service to a Right(List(EventPart))") {
  177 + expect(Right(List(EventPart(service = Some("service"))))) {
  178 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  179 + Proto.Event.newBuilder.setService("service")).build)
  180 + }
  181 + }
  182 +
  183 + test("in: convert a protobuf Msg with Event with only state to a Right(List(EventPart))") {
  184 + expect(Right(List(EventPart(state = Some("state"))))) {
  185 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  186 + Proto.Event.newBuilder.setState("state")).build)
  187 + }
  188 + }
  189 +
  190 + test("in: convert a protobuf Msg with Event with only time to a Right(List(EventPart))") {
  191 + expect(Right(List(EventPart(time = Some(1234L))))) {
  192 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  193 + Proto.Event.newBuilder.setTime(1234L)).build)
  194 + }
  195 + }
  196 +
  197 + test("in: convert a protobuf Msg with Event with only description to a Right(List(EventPart))") {
  198 + expect(Right(List(EventPart(description = Some("description"))))) {
  199 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  200 + Proto.Event.newBuilder.setDescription("description")).build)
  201 + }
  202 + }
  203 +
  204 + test("in: convert a protobuf Msg with Event with only tags to a Right(List(EventPart))") {
  205 + expect(Right(List(EventPart(tags = List("tag1", "tag2"))))) {
  206 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  207 + Proto.Event.newBuilder.addAllTags(List("tag1", "tag2"))).build)
  208 + }
  209 + }
  210 +
  211 + test("in: convert a protobuf Msg with Event with only metric (long) to a Right(List(EventPart))") {
  212 + expect(Right(List(EventPart(metric = Some(1234L))))) {
  213 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  214 + Proto.Event.newBuilder.setMetricSint64(1234L)).build)
  215 + }
  216 + }
  217 +
  218 + test("in: convert a protobuf Msg with Event with only metric (float) to a Right(List(EventPart))") {
  219 + expect(Right(List(EventPart(metric = Some(1234.0f))))) {
  220 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  221 + Proto.Event.newBuilder.setMetricF(1234.0f)).build)
  222 + }
  223 + }
  224 +
  225 + test("in: convert a protobuf Msg with Event with only metric (double) to a Right(List(EventPart))") {
  226 + expect(Right(List(EventPart(metric = Some(1234.1: Double))))) {
  227 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  228 + Proto.Event.newBuilder.setMetricD(1234.1: Double)).build)
  229 + }
  230 + }
  231 +
  232 + test("in: convert a protobuf Msg with Event with only ttl to a Right(List(EventPart))") {
  233 + expect(Right(List(EventPart(ttl = Some(1234L))))) {
  234 + unserializeProtoMsg(Proto.Msg.newBuilder.setOk(true).addEvents(
  235 + Proto.Event.newBuilder.setTtl(1234L)).build)
  236 + }
  237 + }
  238 +}
43 src/test/scala/net/benmur/riemann/client/UnreliableIOTest.scala
... ... @@ -0,0 +1,43 @@
  1 +package net.benmur.riemann.client
  2 +
  3 +import java.net.{ InetSocketAddress, SocketAddress }
  4 +
  5 +import scala.annotation.implicitNotFound
  6 +import scala.collection.mutable.WrappedArray
  7 +
  8 +import org.scalamock.ProxyMockFactory
  9 +import org.scalamock.scalatest.MockFactory
  10 +import org.scalatest.{ BeforeAndAfterAll, FunSuite }
  11 +import org.scalatest.matchers.ShouldMatchers
  12 +
  13 +import akka.actor.ActorSystem
  14 +import akka.testkit.CallingThreadDispatcher
  15 +import net.benmur.riemann.client.testingsupport.TestingTransportSupport
  16 +
  17 +class UnreliableIOTest extends FunSuite
  18 + with BeforeAndAfterAll
  19 + with MockFactory
  20 + with ProxyMockFactory
  21 + with ShouldMatchers {
  22 +
  23 + import UnreliableIO._
  24 + import TestingTransportSupport._
  25 +
  26 + implicit val system = ActorSystem()
  27 + val address = new InetSocketAddress(0)
  28 +
  29 + override def afterAll {
  30 + system.shutdown
  31 + }
  32 +
  33 + test("send a protobuf Msg") {
  34 + val socket = mock[UnconnectedSocketWrapper]
  35 + socket expects 'send withArguments (WrappedArray.make(protoMsgEvent.toByteArray)) once
  36 +
  37 + val socketFactory = mockFunction[SocketAddress, UnconnectedSocketWrapper]
  38 + socketFactory expects address returning socket once
  39 +
  40 + val conn = implicitly[ConnectionBuilder[Unreliable]].buildConnection(address, Some(socketFactory), Some(CallingThreadDispatcher.Id))
  41 + implicitly[SendOff[Unreliable]].sendOff(conn, Write(protoMsgEvent))