|
|
@@ -1,7 +1,8 @@
|
|
|
package controllers
|
|
|
|
|
|
-import scala.concurrent.Await
|
|
|
-import scala.concurrent.duration.Duration
|
|
|
+import scala.concurrent.{Await,Future}
|
|
|
+import scala.language.postfixOps
|
|
|
+import scala.concurrent.duration.{Duration,DurationInt}
|
|
|
import org.scalatestplus.play._
|
|
|
import akka.stream.Materializer
|
|
|
|
|
|
@@ -10,16 +11,17 @@ 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 play.api.libs.json.{JsValue,JsObject,Json,JsString}
|
|
|
|
|
|
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 org.mongodb.scala.{MongoClient,MongoDatabase,MongoCollection,Observer,Completed};
|
|
|
|
|
|
import models.Food
|
|
|
+import models.{Mass,Volume}
|
|
|
|
|
|
class FoodControllerSpec
|
|
|
extends PlaySpec
|
|
|
@@ -30,27 +32,13 @@ class FoodControllerSpec
|
|
|
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")
|
|
|
- }
|
|
|
+ val exampleFood1: Food = Food("1", "Example Food", false, true, false, Map("iron" -> 1.2f, "B" -> 7f),
|
|
|
+ "Test Script", Seq("Breakfast", "Asian"), Mass(), 3.2f, 2.7f, 1237, Seq("Scrambled Eggs"))
|
|
|
|
|
|
- // 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()}
|
|
|
- // }
|
|
|
+ val exampleFood2: Food = Food("2", "Food Example", true, false, false, Map("copper" -> .2f, "D" -> 1f),
|
|
|
+ "Test Script", Seq("Dinner", "Southern"), Volume(), 1.2f, 2.0f, 73, Seq("Bacon"))
|
|
|
|
|
|
- "FoodController PUT /food" should {
|
|
|
- "return the resulting food object and insert into the DB" in {
|
|
|
- val body: JsValue = Json.parse(
|
|
|
+ val exampleJson1: JsValue = Json.parse(
|
|
|
"""{"name": "Example Food",
|
|
|
|"glutenFree": false,
|
|
|
|"vegitarian": true,
|
|
|
@@ -64,8 +52,71 @@ class FoodControllerSpec
|
|
|
|"price": 1237,
|
|
|
|"alternatives": ["Scrambled Eggs"]
|
|
|
|} """.stripMargin
|
|
|
- )
|
|
|
- val fakeRequ: FakeRequest[JsValue] = FakeRequest[JsValue](PUT, "/food", FakeHeaders(), body)
|
|
|
+ )
|
|
|
+
|
|
|
+ val strippedExampleJson1: 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 brokenExampleJson1: JsValue = Json.parse(
|
|
|
+ """{
|
|
|
+ |"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 exampleJson2: JsValue = Json.parse(
|
|
|
+ """{"name": "Food Example",
|
|
|
+ |"glutenFree": true,
|
|
|
+ |"vegitarian": false,
|
|
|
+ |"vegan": false,
|
|
|
+ |"nutrients": {"copper": 0.2, "D": 1},
|
|
|
+ |"source": "Test Script",
|
|
|
+ |"category": ["Dinner", "Southern"],
|
|
|
+ |"primaryMeasure": "volume",
|
|
|
+ |"density": 1.2,
|
|
|
+ |"mass_p_u": 2,
|
|
|
+ |"price": 73,
|
|
|
+ |"alternatives": ["Bacon"]
|
|
|
+ |} """.stripMargin
|
|
|
+ )
|
|
|
+
|
|
|
+ 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 initDB(coll: MongoCollection[Food], items: Seq[Food]): MongoCollection[Food] = {
|
|
|
+ val futures: Seq[Future[Completed]] = items.map(coll.insertOne(_).toFuture)
|
|
|
+ futures.map(Await.ready(_, 5 minutes))
|
|
|
+ coll
|
|
|
+ }
|
|
|
+
|
|
|
+ "FoodController PUT /food" should {
|
|
|
+ "return the resulting food object and insert into the DB" in {
|
|
|
+ val fakeRequ: FakeRequest[JsValue] = FakeRequest[JsValue](PUT, "/food", FakeHeaders(), exampleJson1)
|
|
|
.withHeaders(HeaderNames.CONTENT_TYPE -> "application/json")
|
|
|
withEmbedMongoFixture() { mongodProps: MongodProps =>
|
|
|
val coll: MongoCollection[Food] = getDBColl
|
|
|
@@ -74,22 +125,12 @@ class FoodControllerSpec
|
|
|
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]
|
|
|
+ resultJs - "_id" mustBe exampleJson1
|
|
|
+ Await.result(coll.countDocuments().toFuture, 5 minutes) mustBe 1
|
|
|
+ Await.result(coll.find().first().toFuture, 5 minutes) 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(), "")
|
|
|
@@ -99,69 +140,26 @@ class FoodControllerSpec
|
|
|
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
|
|
|
+ Await.result(coll.countDocuments().toFuture, 5 minutes) 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)
|
|
|
+ val fakeRequ = FakeRequest(PUT, "/food", FakeHeaders(), brokenExampleJson1)
|
|
|
.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
|
|
|
+ Await.result(coll.countDocuments().toFuture, 5 minutes) 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)
|
|
|
+ val fakeRequ = FakeRequest(PUT, "/food", FakeHeaders(), strippedExampleJson1)
|
|
|
.withHeaders(HeaderNames.CONTENT_TYPE -> "application/json")
|
|
|
withEmbedMongoFixture() { mongodProps: MongodProps =>
|
|
|
val coll: MongoCollection[Food] = getDBColl
|
|
|
@@ -170,10 +168,37 @@ class FoodControllerSpec
|
|
|
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]
|
|
|
+ resultJs - "_id" mustBe exampleJson1
|
|
|
+ Await.result(coll.countDocuments().toFuture, 5 minutes) mustBe 1
|
|
|
+ Await.result(coll.find().first().toFuture, 5 minutes) mustBe resultJs.as[Food]
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ "FoodController GET /food/:id" should {
|
|
|
+ "return the correct food object" in {
|
|
|
+ val fakeRequ: FakeRequest[JsValue] = FakeRequest[JsValue](GET, s"/food/${exampleFood1._id}", FakeHeaders(), JsString(""))
|
|
|
+ withEmbedMongoFixture() { mongodProps: MongodProps =>
|
|
|
+ val coll: MongoCollection[Food] = initDB(getDBColl, Seq(exampleFood1, exampleFood2))
|
|
|
+ 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.value.get("_id").toString mustBe exampleFood1._id
|
|
|
+ resultJs - "_id" mustBe exampleJson1
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ "return an error when given an empty request and not " +
|
|
|
+ "insert into the DB" in {
|
|
|
+ val fakeRequ: FakeRequest[JsValue] = FakeRequest[JsValue](GET, s"/food/0", FakeHeaders(), JsString(""))
|
|
|
+ withEmbedMongoFixture() { mongodProps: MongodProps =>
|
|
|
+ val coll: MongoCollection[Food] = getDBColl
|
|
|
+ val controller: FoodController = new FoodController(coll)
|
|
|
+ val result = controller.put().apply(fakeRequ)
|
|
|
+ status(result) mustBe Status.NOT_FOUND
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|