diff options
author | Zach Smith <zach@driver.xyz> | 2018-03-16 18:47:30 +0100 |
---|---|---|
committer | Zach Smith <zach@driver.xyz> | 2018-03-16 20:03:44 +0100 |
commit | ce099ac3ba85f71adeba0bb8398b69dc7cd2e8d1 (patch) | |
tree | 772e000647aeaf59bd8b22cd22661d3998f22f7e /src/test/scala | |
parent | 96aa1fbf7608e3b9cd1ba06c57ab1f356409733d (diff) | |
download | driver-core-ce099ac3ba85f71adeba0bb8398b69dc7cd2e8d1.tar.gz driver-core-ce099ac3ba85f71adeba0bb8398b69dc7cd2e8d1.tar.bz2 driver-core-ce099ac3ba85f71adeba0bb8398b69dc7cd2e8d1.zip |
Add PatchSupport trait and tests
Diffstat (limited to 'src/test/scala')
-rw-r--r-- | src/test/scala/xyz/driver/core/rest/PatchSupportTest.scala | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/src/test/scala/xyz/driver/core/rest/PatchSupportTest.scala b/src/test/scala/xyz/driver/core/rest/PatchSupportTest.scala new file mode 100644 index 0000000..dcb3a93 --- /dev/null +++ b/src/test/scala/xyz/driver/core/rest/PatchSupportTest.scala @@ -0,0 +1,77 @@ +package xyz.driver.core.rest + +import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport +import akka.http.scaladsl.model.{ContentTypes, HttpEntity, RequestEntity, StatusCodes} +import akka.http.scaladsl.server.{Directives, Route} +import akka.http.scaladsl.testkit.ScalatestRouteTest +import org.scalatest.{FlatSpec, Matchers} +import spray.json._ +import xyz.driver.core.{Id, Name} +import xyz.driver.core.json._ + +import scala.concurrent.Future + +class PatchSupportTest + extends FlatSpec with Matchers with ScalatestRouteTest with SprayJsonSupport with DefaultJsonProtocol + with Directives with PatchSupport { + case class Bar(name: Name[Bar], size: Int) + case class Foo(id: Id[Foo], name: Name[Foo], rank: Int, bar: Option[Bar]) + implicit val barFormat: RootJsonFormat[Bar] = jsonFormat2(Bar) + implicit val fooFormat: RootJsonFormat[Foo] = jsonFormat4(Foo) + + val testFoo: Foo = Foo(Id("1"), Name(s"Foo"), 1, Some(Bar(Name("Bar"), 10))) + + def route(implicit patchRetrievable: PatchRetrievable[Foo]): Route = + Route.seal(path("api" / "v1" / "foos" / IdInPath[Foo]) { fooId => + patch(as[Foo], fooId) { patchedFoo => + complete(patchedFoo) + } + }) + + def jsonEntity(json: String): RequestEntity = HttpEntity(ContentTypes.`application/json`, json) + + "PatchSupport" should "allow partial updates to an existing object" in { + implicit val fooPatchable: PatchRetrievable[Foo] = id => Future.successful(Some(testFoo.copy(id = id))) + + Patch("/api/v1/foos/1", jsonEntity("""{"rank": 4}""")) ~> route ~> check { + handled shouldBe true + responseAs[Foo] shouldBe testFoo.copy(rank = 4) + } + } + + it should "merge deeply nested objects" in { + implicit val fooPatchable: PatchRetrievable[Foo] = id => Future.successful(Some(testFoo.copy(id = id))) + + Patch("/api/v1/foos/1", jsonEntity("""{"rank": 4, "bar": {"name": "My Bar"}}""")) ~> route ~> check { + handled shouldBe true + responseAs[Foo] shouldBe testFoo.copy(rank = 4, bar = Some(Bar(Name("My Bar"), 10))) + } + } + + it should "return a 404 if the object is not found" in { + implicit val fooPatchable: PatchRetrievable[Foo] = _ => Future.successful(Option.empty[Foo]) + + Patch("/api/v1/foos/1", jsonEntity("""{"rank": 4}""")) ~> route ~> check { + handled shouldBe true + status shouldBe StatusCodes.NotFound + } + } + + it should "handle nulls on optional values correctly" in { + implicit val fooPatchable: PatchRetrievable[Foo] = id => Future.successful(Some(testFoo.copy(id = id))) + + Patch("/api/v1/foos/1", jsonEntity("""{"bar": null}""")) ~> route ~> check { + handled shouldBe true + responseAs[Foo] shouldBe testFoo.copy(bar = None) + } + } + + it should "return a 400 for nulls on non-optional values" in { + implicit val fooPatchable: PatchRetrievable[Foo] = id => Future.successful(Some(testFoo.copy(id = id))) + + Patch("/api/v1/foos/1", jsonEntity("""{"rank": null}""")) ~> route ~> check { + handled shouldBe true + status shouldBe StatusCodes.BadRequest + } + } +} |