package controllers import scala.concurrent.Await import scala.concurrent.duration.Duration import org.scalatestplus.play._ import akka.stream.Materializer import play.api.mvc._ import play.api.test._ import play.api.test.Helpers._ import play.api.http.Status import play.api.http.HeaderNames import play.api.libs.json.{JsValue,JsObject,Json} import org.scalatest._ import org.scalatestplus.play._ import org.scalatestplus.play.guice._ import com.github.simplyscala.{MongoEmbedDatabase,MongodProps} import org.mongodb.scala.{MongoClient,MongoDatabase,MongoCollection,Observer}; import models.Food class FoodControllerSpec extends PlaySpec with GuiceOneAppPerTest with Injecting with MongoEmbedDatabase { val testDBPort: Int = 12345 implicit lazy val materializer: Materializer = app.materializer def getDBColl(): MongoCollection[Food] = { val db: MongoDatabase = MongoClient.apply(s"mongodb://localhost:${testDBPort}") .getDatabase("recipes") .withCodecRegistry(MongoDB.codecRegistry) db.createCollection("Food") db.getCollection[Food]("Food") } // def blockingObserver[T](sem: Semaphore, processor: T => Unit) = new Observer[T] { // override def onNext(result: T) = {println("next");processor(result)} // override def onError(e: Throwable) = { // println("error") // sem.release() // throw e // } // override def onComplete() = {println("complete");sem.release()} // } "FoodController PUT /food" should { "return the resulting food object and insert into the DB" in { val body: JsValue = Json.parse( """{"name": "Example Food", |"glutenFree": false, |"vegitarian": true, |"vegan": false, |"nutrients": {"iron": 1.2, "B": 7}, |"source": "Test Script", |"category": ["Breakfast", "Asian"], |"primaryMeasure": "mass", |"density": 3.2, |"mass_p_u": 2.7, |"price": 1237, |"alternatives": ["Scrambled Eggs"] |} """.stripMargin ) val fakeRequ: FakeRequest[JsValue] = FakeRequest[JsValue](PUT, "/food", FakeHeaders(), body) .withHeaders(HeaderNames.CONTENT_TYPE -> "application/json") withEmbedMongoFixture() { mongodProps: MongodProps => val coll: MongoCollection[Food] = getDBColl val controller: FoodController = new FoodController(coll) val result = controller.put().apply(fakeRequ) status(result) mustBe OK contentType(result) mustBe Some("application/json") val resultJs: JsObject = contentAsJson(result).asInstanceOf[JsObject] resultJs - "_id" mustBe body Await.result(coll.countDocuments().toFuture, Duration.Inf) mustBe 1 Await.result(coll.find().first().toFuture, Duration.Inf) mustBe resultJs.as[Food] } } // Removed: not presenting id's to front-end // "return the resulting food object with a distinct id" in { // val controller = inject[HomeController] // val home = controller.index().apply(FakeRequest(GET, "/")) // status(home) mustBe OK // contentType(home) mustBe Some("text/html") // contentAsString(home) must include ("Welcome to Play") // } "return an error when given an empty request and not " + "insert into the DB" in { val fakeRequ = FakeRequest(PUT, "/food", FakeHeaders(), "") .withHeaders(HeaderNames.CONTENT_TYPE -> "application/json") withEmbedMongoFixture() { mongodProps: MongodProps => val coll: MongoCollection[Food] = getDBColl val controller: FoodController = new FoodController(coll) val result = controller.put().apply(fakeRequ) status(result) mustBe Status.BAD_REQUEST Await.result(coll.countDocuments().toFuture, Duration.Inf) mustBe 0 } } "return an error when given an invalid request and not " + "insert into the DB" in { val body: JsValue = Json.parse(// Name intentionally moved """{ |"glutenFree": false, |"vegitarian": true, |"vegan": false, |"nutrients": {"iron": 1.2, "B": 7}, |"source": "Test Script", |"category": ["Breakfast", "Asian"], |"primaryMeasure": "mass", |"density": 3.2, |"mass_p_u": 2.7, |"price": 1237, |"alternatives": ["Scrambled Eggs"] |} """.stripMargin ) val fakeRequ = FakeRequest(PUT, "/food", FakeHeaders(), body) .withHeaders(HeaderNames.CONTENT_TYPE -> "application/json") withEmbedMongoFixture() { mongodProps: MongodProps => val coll: MongoCollection[Food] = getDBColl val controller: FoodController = new FoodController(coll) val result = controller.put().apply(fakeRequ) status(result) mustBe Status.BAD_REQUEST Await.result(coll.countDocuments().toFuture, Duration.Inf) mustBe 0 } } "return the resulting object with default values and " + "insert into the DB" in { val body: JsValue = Json.parse( """{"name": "Example Food", |"vegitarian": true, |"nutrients": {"iron": 1.2, "B": 7}, |"source": "Test Script", |"category": ["Breakfast", "Asian"], |"primaryMeasure": "mass", |"density": 3.2, |"mass_p_u": 2.7, |"price": 1237, |"alternatives": ["Scrambled Eggs"] |} """.stripMargin ) val expected: JsValue = Json.parse( """{"name": "Example Food", |"glutenFree": false, |"vegitarian": true, |"vegan": false, |"nutrients": {"iron": 1.2, "B": 7}, |"source": "Test Script", |"category": ["Breakfast", "Asian"], |"primaryMeasure": "mass", |"density": 3.2, |"mass_p_u": 2.7, |"price": 1237, |"alternatives": ["Scrambled Eggs"] |} """.stripMargin ) val fakeRequ = FakeRequest(PUT, "/food", FakeHeaders(), body) .withHeaders(HeaderNames.CONTENT_TYPE -> "application/json") withEmbedMongoFixture() { mongodProps: MongodProps => val coll: MongoCollection[Food] = getDBColl val controller: FoodController = new FoodController(coll) val result = controller.put().apply(fakeRequ) status(result) mustBe OK contentType(result) mustBe Some("application/json") val resultJs: JsObject = contentAsJson(result).asInstanceOf[JsObject] resultJs - "_id" mustBe expected Await.result(coll.countDocuments().toFuture, Duration.Inf) mustBe 1 Await.result(coll.find().first().toFuture, Duration.Inf) mustBe resultJs.as[Food] } } } }