From 864c81085bc468f9597c27917c112dd4f9678de8 Mon Sep 17 00:00:00 2001 From: Yann Simon Date: Mon, 6 May 2024 10:11:03 +0200 Subject: [PATCH] harden tests --- .../federation/v2/FederationSpec.scala | 170 ++++++++++++++---- 1 file changed, 133 insertions(+), 37 deletions(-) diff --git a/core/src/test/scala/sangria/federation/v2/FederationSpec.scala b/core/src/test/scala/sangria/federation/v2/FederationSpec.scala index 130cba9..169a18f 100644 --- a/core/src/test/scala/sangria/federation/v2/FederationSpec.scala +++ b/core/src/test/scala/sangria/federation/v2/FederationSpec.scala @@ -299,15 +299,10 @@ class FederationSpec extends AsyncFreeSpec { } """) - val args: Json = parse(""" { "representations": [{ "__typename": "State", "id": 1 }] } """) - .getOrElse(Json.Null) - import sangria.marshalling.queryAst.queryAstResultMarshaller "should succeed on federated unmarshaller" in { - implicit val um = Federation.upgrade(sangria.marshalling.circe.CirceInputUnmarshaller) - val args: Json = parse(""" { "representations": [{ "__typename": "State", "id": 1 }] } """) .getOrElse(Json.Null) @@ -324,9 +319,7 @@ class FederationSpec extends AsyncFreeSpec { } "should fail on regular unmarshaller" in { - implicit val um = sangria.marshalling.circe.CirceInputUnmarshaller - val args: Json = parse(""" { "representations": [{ "__typename": "State", "id": 1 }] } """) .getOrElse(Json.Null) @@ -336,10 +329,42 @@ class FederationSpec extends AsyncFreeSpec { } } - "should fetch several entities" in { - + "should fetch several entities of same type in one call" in { implicit val um = Federation.upgrade(sangria.marshalling.circe.CirceInputUnmarshaller) + val args: Json = parse(""" + { + "representations": [ + { "__typename": "State", "id": 1 }, + { "__typename": "State", "id": 2 }, + { "__typename": "State", "id": 20 }, + { "__typename": "State", "id": 5 } + ] + } + """).getOrElse(Json.Null) + Executor + .execute(FederationSpec.Schema.schema, query, variables = args) + .map(QueryRenderer.renderPretty(_) should be("""{ + | data: { + | _entities: [{ + | id: 1 + | value: "mock state 1" + | }, { + | id: 2 + | value: "mock state 2" + | }, { + | id: 20 + | value: "mock state 20" + | }, { + | id: 5 + | value: "mock state 5" + | }] + | } + |}""".stripMargin)) + } + + "should fetch several entities of different types in one call" in { + implicit val um = Federation.upgrade(sangria.marshalling.circe.CirceInputUnmarshaller) val args: Json = parse(""" { "representations": [ @@ -360,29 +385,86 @@ class FederationSpec extends AsyncFreeSpec { variables = args, deferredResolver = FederationSpec.Schema.deferredReviewResolver) .map(QueryRenderer.renderPretty(_) should be("""{ - | data: { - | _entities: [{ - | id: 1 - | value: "mock state 1" - | }, { - | id: 2 - | value: "mock state 2" - | }, { - | id: 2 - | value: "mock review 2" - | }, { - | id: 20 - | value: "mock state 20" - | }, { - | id: 5 - | value: "mock state 5" - | }, { - | id: 1 - | value: "mock review 1" - | }] - | } - |}""".stripMargin)) + | data: { + | _entities: [{ + | id: 1 + | value: "mock state 1" + | }, { + | id: 2 + | value: "mock state 2" + | }, { + | id: 2 + | value: "mock review 2" + | }, { + | id: 20 + | value: "mock state 20" + | }, { + | id: 5 + | value: "mock state 5" + | }, { + | id: 1 + | value: "mock review 1" + | }] + | } + |}""".stripMargin)) } + + "handles non found entities" in { + implicit val um = Federation.upgrade(sangria.marshalling.circe.CirceInputUnmarshaller) + val args: Json = parse(""" + { + "representations": [ + { "__typename": "State", "id": 1 }, + { "__typename": "State", "id": 42 }, + { "__typename": "State", "id": 20 } + ] + } + """).getOrElse(Json.Null) + + Executor + .execute(FederationSpec.Schema.schema, query, variables = args) + .map(QueryRenderer.renderPretty(_) should be("""{ + | data: { + | _entities: [{ + | id: 1 + | value: "mock state 1" + | }, null, { + | id: 20 + | value: "mock state 20" + | }] + | } + |}""".stripMargin)) + } + +// "handles entities using same arg" in { +// implicit val um = Federation.upgrade(sangria.marshalling.circe.CirceInputUnmarshaller) +// val args: Json = parse(""" +// { +// "representations": [ +// { "__typename": "Review", "id": 1 }, +// { "__typename": "Review2", "id": 1 }, +// { "__typename": "Review2", "id": 2 } +// ] +// } +// """).getOrElse(Json.Null) +// +// Executor +// .execute(FederationSpec.Schema.schema, query, variables = args) +// .map(QueryRenderer.renderPretty(_) should be("""{ +// | data: { +// | _entities: [{ +// | id: 1 +// | value: "mock review 1" +// | }, { +// | id: 1 +// | value2: "mock review2 1" +// | }, { +// | id: 2 +// | value2: "mock review2 2" +// | }] +// | } +// |}""".stripMargin)) +// } } } } @@ -390,18 +472,18 @@ class FederationSpec extends AsyncFreeSpec { object FederationSpec { object Schema { private case class State(id: Int, value: String) - private case class StateArg(id: Int) - private val StateType = ObjectType( "State", fields[Unit, State]( Field("id", IntType, resolve = _.value.id), Field("value", OptionType(StringType), resolve = _.value.value))).withDirective(Key("id")) + + private case class StateArg(id: Int) private implicit val stateArgDecoder: Decoder[Json, StateArg] = deriveDecoder[StateArg].decodeJson(_) private val stateResolver = EntityResolver[Any, Json, State, StateArg]( __typeName = "State", - (arg, _) => Some(State(arg.id, s"mock state ${arg.id}"))) + (arg, _) => if (arg.id == 42) None else Some(State(arg.id, s"mock state ${arg.id}"))) private case class Review(id: Int, value: String) private case class DeferredReview(id: Int) extends Deferred[Option[Review]] @@ -418,12 +500,13 @@ object FederationSpec { } } val deferredReviewResolver: DeferredReviewResolver = DeferredReviewResolver() - private case class ReviewArg(id: Int) private val ReviewType = ObjectType( "Review", fields[Unit, Review]( Field("id", IntType, resolve = _.value.id), Field("value", OptionType(StringType), resolve = _.value.value))).withDirective(Key("id")) + + private case class ReviewArg(id: Int) private implicit val reviewArgDecoder: Decoder[Json, ReviewArg] = deriveDecoder[ReviewArg].decodeJson(_) private val reviewResolver = EntityResolver[Any, Json, Review, ReviewArg]( @@ -431,17 +514,30 @@ object FederationSpec { (arg, _) => DeferredValue(DeferredReview(arg.id)) ) + private case class Review2(id: Int, value2: String) + + private val Review2Type = ObjectType( + "Review2", + fields[Unit, Review2]( + Field("id", IntType, resolve = _.value.id), + Field("value2", OptionType(StringType), resolve = _.value.value2))).withDirective(Key("id")) + // review2 uses the same arg as review + private val review2Resolver = EntityResolver[Any, Json, Review2, ReviewArg]( + __typeName = Review2Type.name, + (arg, _) => Some(Review2(arg.id, s"mock review2 ${arg.id}"))) + private val Query = ObjectType( "Query", fields[Unit, Any]( Field(name = "states", fieldType = ListType(StateType), resolve = _ => Nil), - Field(name = "reviews", fieldType = ListType(ReviewType), resolve = _ => Nil) + Field(name = "reviews", fieldType = ListType(ReviewType), resolve = _ => Nil), + Field(name = "reviews2", fieldType = ListType(Review2Type), resolve = _ => Nil) ) ) val schema: Schema[Any, Any] = Federation.extend( sangria.schema.Schema(Query), - stateResolver :: reviewResolver :: Nil + List(stateResolver, reviewResolver, review2Resolver) ) } }