Skip to content
Permalink
Browse files

[reactivemongo] Write response & handler specs

  • Loading branch information
cchantep
cchantep committed Sep 15, 2014
1 parent 357c676 commit 66cc12f36f0127598f72c2c9c2cc59d01d1ba9d2
@@ -19,8 +19,10 @@ import reactivemongo.api.MongoDriver
import acolyte.reactivemongo.AcolyteDSL.{ driver, handleStatement }
val mongoDriver: MongoDriver = driver {
handleStatement
??? // dispatch query and write request as you want using pattern matching
}
val noOpDriver = driver { handleStatement/* ConnectionHandler.empty */}
```

### Request patterns
@@ -159,10 +161,12 @@ import acolyte.reactivemongo.WriteResponse
val error1: Option[Try[Response]] = WriteResponse.failed("Error #1")
val error2 = WriteResponse("Error #1") // equivalent
val error3 = WriteResponse("Error #2" -> Some(1)/* code */)
val error3 = WriteResponse.failed("Error #2", 1/* code */)
val error4 = WriteResponse("Error #2" -> 1/* code */) // equivalent
val success1 = WriteResponse(true/* updatedExisting */)
val success2 = WriteResponse.successful(true) // equivalent
val success3 = WriteResponse() // = WriteResponse.successful(false)
```

When a handler supports some write cases, but not other, it can return an undefined response, to let the chance other handlers would manage it.
@@ -25,5 +25,5 @@ object AcolyteDSL {
* connection { handleStatement }
* }}}
*/
def handleStatement: ConnectionHandler = ???
def handleStatement: ConnectionHandler = ConnectionHandler.empty
}
@@ -50,9 +50,6 @@ private[reactivemongo] class Actor(
val req = Request(op.fullCollectionName, doc.merged)
val exp = new ExpectingResponse(msg)
val cid = r()._1.channelIdHint getOrElse 1

println(s"chan = $cid, ${req.body.elements.toList}")

val resp = MongoDB.WriteOp(op).fold({
MongoDB.WriteError(cid, s"No write operator: $msg") match {
case Success(err) err
@@ -70,32 +67,6 @@ private[reactivemongo] class Actor(
})
}

/*
val xxp = scala.concurrent.Promise[Response]() completeWith {
exp.promise.future.transform({ resp ⇒
println(s"Suc: $resp ${resp.getClass}")
println(s"--> ${resp.reply.flags}") // 8
resp
}, { err ⇒
err.printStackTrace()
err
//new RuntimeException("Yipee")
})
Future.successful[Response](MongoDB.Success(chan, List(
reactivemongo.bson.BSONDocument("ok" -> 0, "err" -> "Err_1",
"code" -> 7, "errmsg" -> "Err_Msg", "n" -> 0,
"updatedExisting" -> false)
)).get)
}
*/

// Success:
//exp.promise.success(MongoDB.WriteSuccess(chan).get)

// Error:
//exp.promise.success(MongoDB.WriteError(chan, "Err_1").get)
//exp.promise.success(NoWriteResponse(chanId, msg.toString))
exp.promise.success(resp)

case msg @ RequestMakerExpectingResponse(RequestMaker(
@@ -16,9 +16,16 @@ object WriteResponse {
* Named factory for error response.
*
* @param message Error message
* @param code Optional error code
*/
def failed(message: String, code: Option[Int] = None) = apply(message -> code)
def failed(message: String) = apply(message)

/**
* Named factory for error response.
*
* @param message Error message
* @param code Error code
*/
def failed(message: String, code: Int) = apply(message -> code)

/**
* Factory for successful response.
@@ -30,19 +30,45 @@ object WriteResponseMaker {
override def apply(channelId: Int, updatedExisting: Boolean): Option[Try[Response]] = Some(MongoDB.WriteSuccess(channelId, updatedExisting))
}

/**
* {{{
* import acolyte.reactivemongo.WriteResponseMaker
*
* val maker = implicitly[WriteResponseMaker[Unit]]
* }}}
*/
implicit def UnitWriteResponseMaker = new WriteResponseMaker[Unit] {
override def apply(channelId: Int, effect: Unit): Option[Try[Response]] = Some(MongoDB.WriteSuccess(channelId, false))
}

/**
* Provides response maker for an error.
*
* {{{
* import acolyte.reactivemongo.WriteResponseMaker
*
* val maker = implicitly[WriteResponseMaker[(String, Option[Int])]]
* val maker = implicitly[WriteResponseMaker[String]]
* }}}
*/
implicit def ErrorWriteResponseMaker = new WriteResponseMaker[(String, Option[Int])] {
override def apply(channelId: Int, error: (String, Option[Int])): Option[Try[Response]] = Some(MongoDB.WriteError(channelId, error._1, error._2))
implicit def ErrorWriteResponseMaker = new WriteResponseMaker[String] {
override def apply(channelId: Int, error: String): Option[Try[Response]] =
Some(MongoDB.WriteError(channelId, error, None))
}

/**
* Provides response maker for an error.
*
* {{{
* import acolyte.reactivemongo.WriteResponseMaker
*
* val maker = implicitly[WriteResponseMaker[(String, Int)]]
* }}}
*/
implicit def ErrorCodeWriteResponseMaker =
new WriteResponseMaker[(String, Int)] {
override def apply(channelId: Int, error: (String, Int)): Option[Try[Response]] = Some(MongoDB.WriteError(channelId, error._1, Some(error._2)))
}

/**
* Provides response maker for handler not supporting
* specific write operation.
@@ -1,15 +1,18 @@
package acolyte.reactivemongo

object ConnectionHandlerSpec
extends org.specs2.mutable.Specification with QueryHandlerFixtures {
object ConnectionHandlerSpec extends org.specs2.mutable.Specification
with QueryHandlerFixtures with WriteHandlerFixtures {

"Connection handler" title

"Empty handler" should {
"not respond to any query" in {
ConnectionHandler.empty aka "connection handler" must beLike {
case h h.queryHandler(1, query1) must beNone
case h
h.queryHandler(1, query1) aka "query result" must beNone and (
h.writeHandler(2, write1._1, write1._2).
aka("write result") must beNone)
}
}
}
}
}
@@ -13,7 +13,7 @@ object QueryHandlerSpec extends org.specs2.mutable.Specification
_: Request QueryResponse(Seq(BSONDocument("prop" -> "A")))
}) aka "query handler" must beLike {
case h h(1, query1) must beSome.which(
_ aka "response" must beSuccessResponse {
_ aka "response" must beResponse {
case ValueDocument(("prop", BSONString("A")) :: Nil) :: Nil ok
})
}
@@ -24,7 +24,7 @@ object QueryHandlerSpec extends org.specs2.mutable.Specification
_: Request QueryResponse.successful(BSONDocument("prop" -> "A"))
}) aka "query handler" must beLike {
case h h(1, query1) must beSome.which(
_ aka "response" must beSuccessResponse {
_ aka "response" must beResponse {
case ValueDocument(("prop", BSONString("A")) :: Nil) :: Nil ok
})
}
@@ -34,7 +34,7 @@ object QueryHandlerSpec extends org.specs2.mutable.Specification
implicitly[QueryHandler]({ _: Request
QueryResponse("Error message") }) aka "query handler" must beLike {
case h h(1, query1) must beSome.which(
_ aka "response" must beErrorResponse("Error message"))
_ aka "response" must beQueryError("Error message"))
}
}

@@ -77,14 +77,14 @@ object QueryHandlerSpec extends org.specs2.mutable.Specification
"return an error response" in {
handler aka "mixed handler" must beLike {
case h h(2, query2) aka "prepared" must beSome.which(
_ aka "query response" must beErrorResponse("Error #2"))
_ aka "query response" must beQueryError("Error #2"))
}
}

"return an success response with many documents" in {
handler aka "mixed handler" must beLike {
case h h(3, query3) aka "prepared" must beSome.which(
_ aka "query response" must beSuccessResponse {
_ aka "query response" must beResponse {
case ValueDocument(("prop", BSONString("A")) :: Nil) ::
ValueDocument(("a", BSONInteger(1)) :: Nil) :: Nil ok
})
@@ -12,14 +12,14 @@ object QueryResponseSpec
"using generic factory" in {
QueryResponse("error message #1") aka "prepared" must beLike {
case prepared prepared(1) aka "applied" must beSome.which(
_ aka "query response" must beErrorResponse("error message #1"))
_ aka "query response" must beQueryError("error message #1"))
}
}

"using named factory" in {
QueryResponse.failed("error message #2") aka "prepared" must beLike {
case prepared prepared(2) aka "applied" must beSome.which(
_ aka "query response" must beErrorResponse("error message #2"))
_ aka "query response" must beQueryError("error message #2"))
}
}
}
@@ -30,7 +30,7 @@ object QueryResponseSpec
"with a single document using named factory" in {
QueryResponse.successful(Doc1) aka "prepared" must beLike {
case prepared prepared(3) aka "applied" must beSome.which(
_ aka "query response" must beSuccessResponse {
_ aka "query response" must beResponse {
case ValueDocument(("a", BSONString("b")) :: Nil) :: Nil ok
})
}
@@ -40,7 +40,7 @@ object QueryResponseSpec
QueryResponse(Seq(Doc1, BSONDocument("b" -> 2))).
aka("prepared") must beLike {
case prepared prepared(3) aka "applied" must beSome.which(
_ aka "response" must beSuccessResponse {
_ aka "response" must beResponse {
case Doc1 :: ValueDocument(
("b", BSONInteger(2)) :: Nil) :: Nil ok
})
@@ -51,7 +51,7 @@ object QueryResponseSpec
QueryResponse.successful(Doc1, BSONDocument("b" -> 3)).
aka("prepared") must beLike {
case prepared prepared(3) aka "applied" must beSome.which(
_ aka "response" must beSuccessResponse {
_ aka "response" must beResponse {
case Doc1 :: ValueDocument(
("b", BSONInteger(3)) :: Nil) :: Nil ok
})
@@ -35,7 +35,7 @@ object ResponseMakerSpec
}

"Write response maker" should {
"be working for boolean (updatedExisting)" in {
"be a successful one for boolean (updatedExisting)" in {
val makr = implicitly[WriteResponseMaker[Boolean]]

makr(4, true) aka "response" must beSome.which { prepared
@@ -46,10 +46,21 @@ object ResponseMakerSpec
}
}

"be working for an error (String, None)" in {
val makr = implicitly[WriteResponseMaker[(String, Option[Int])]]
"be a successful one for Unit (updatedExisting = false)" in {
val makr = implicitly[WriteResponseMaker[Unit]]

makr(5, "Custom error #1" -> None) aka "response" must beSome.
makr(4, ()) aka "response" must beSome.which { prepared
zip(prepared, MongoDB.WriteSuccess(4, false)).
aka("maker") must beSuccessfulTry.like {
case (a, b) a.documents aka "response" must_== b.documents
}
}
}

"be working for an error message (String)" in {
val makr = implicitly[WriteResponseMaker[String]]

makr(5, "Custom error #1") aka "response" must beSome.
which { prepared
zip(prepared, MongoDB.WriteError(5, "Custom error #1", None)).
aka("maker") must beSuccessfulTry.like {
@@ -58,10 +69,10 @@ object ResponseMakerSpec
}
}

"be working for an error (String, Some(Int))" in {
val makr = implicitly[WriteResponseMaker[(String, Option[Int])]]
"be working for an error with code (String, Int)" in {
val makr = implicitly[WriteResponseMaker[(String, Int)]]

makr(5, "Custom error #2" -> Some(7)) aka "response" must beSome.
makr(5, "Custom error #2" -> 7) aka "response" must beSome.
which { prepared
zip(prepared, MongoDB.WriteError(5, "Custom error #2", Some(7))).
aka("maker") must beSuccessfulTry.like {
@@ -5,21 +5,11 @@ import scala.util.Try
import org.specs2.mutable.Specification
import org.specs2.matcher.{ Expectable, Matcher, MatchResult }

import reactivemongo.bson.{ BSONDocument, BSONString }
import reactivemongo.bson.{ BSONDocument, BSONInteger, BSONString }
import reactivemongo.core.protocol.Response

trait ResponseMatchers { specs: Specification
def beErrorResponse(msg: String) = new Matcher[Try[Response]] {
def apply[R <: Try[Response]](e: Expectable[R]) =
e.value aka "prepared" must beSuccessfulTry.which {
Response.parse(_).toList aka "response" must beLike {
case ValueDocument(("$err", BSONString(m)) :: Nil) :: Nil
m aka "error message" must_== msg
}
}
}

def beSuccessResponse(f: List[BSONDocument] MatchResult[_]) =
def beResponse(f: List[BSONDocument] MatchResult[_]) =
new Matcher[Try[Response]] {
def apply[R <: Try[Response]](e: Expectable[R]) = {
e.value aka "prepared" must beSuccessfulTry.which { resp
@@ -32,4 +22,28 @@ trait ResponseMatchers { specs: Specification ⇒
}
}

def beQueryError(msg: String) = new Matcher[Try[Response]] {
def apply[R <: Try[Response]](e: Expectable[R]) =
e.value aka "prepared" must beSuccessfulTry.which {
Response.parse(_).toList aka "response" must beLike {
case ValueDocument(("$err", BSONString(m)) :: Nil) :: Nil
m aka "error message" must_== msg
}
}
}

def beWriteError(msg: String, code: Option[Int] = None) =
new Matcher[Try[Response]] {
def apply[R <: Try[Response]](e: Expectable[R]) =
e.value aka "prepared" must beSuccessfulTry.which {
Response.parse(_).toList aka "response" must beLike {
case ValueDocument(("ok", BSONInteger(0)) ::
("err", BSONString(err)) :: ("errmsg", BSONString(errmsg)) ::
("code", BSONInteger(c)) :: Nil) :: Nil
err aka "error message (err)" must_== msg and (
errmsg aka "error message (errmsg)" must_== msg) and (
c aka "error code" must_== code.getOrElse(-1))
}
}
}
}

0 comments on commit 66cc12f

Please sign in to comment.
You can’t perform that action at this time.