Skip to content

Commit

Permalink
Merge pull request #1101 from softwaremill/yaml-test-cleanup
Browse files Browse the repository at this point in the history
Yaml test cleanup
  • Loading branch information
adamw authored Mar 25, 2021
2 parents ad285aa + 59359cc commit c7cbf0c
Show file tree
Hide file tree
Showing 48 changed files with 645 additions and 579 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package sttp.tapir.docs.openapi

import io.circe.Codec
import io.circe.generic.auto._
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

import sttp.tapir.{Schema, Validator}
import sttp.tapir._
import sttp.tapir.docs.openapi.VerifyYamlCoproductTest._
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe.jsonBody
import sttp.tapir.openapi.Info
import sttp.tapir.openapi.circe.yaml._
import io.circe.Codec
import VerifyYamlCoproductTest._
import sttp.tapir.tests.{Entity, Organization, Person}

class VerifyYamlCoproductTest extends AnyFunSuite with Matchers {
test("should match expected yaml for coproduct with enum field") {
Expand All @@ -23,14 +25,111 @@ class VerifyYamlCoproductTest extends AnyFunSuite with Matchers {

val endpoint = sttp.tapir.endpoint.get.out(jsonBody[Shape])

val expectedYaml = load("expected_coproduct_discriminator_with_enum_circe.yml")
val expectedYaml = load("coproduct/expected_coproduct_discriminator_with_enum_circe.yml")
val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(endpoint, "My Bookshop", "1.0").toYaml

noIndentation(actualYaml) shouldBe expectedYaml
}

test("should match the expected yaml when using coproduct types") {
val expectedYaml = load("coproduct/expected_coproduct.yml")

val endpoint_wit_sealed_trait: Endpoint[Unit, Unit, Entity, Any] = endpoint
.out(jsonBody[Entity])

val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(endpoint_wit_sealed_trait, Info("Fruits", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

test("should match the expected yaml when using coproduct types with discriminator") {
val sPerson = implicitly[Schema[Person]]
val sOrganization = implicitly[Schema[Organization]]
implicit val sEntity: Schema[Entity] =
Schema.oneOfUsingField[Entity, String](_.name, _.toString)("john" -> sPerson, "sml" -> sOrganization)

val expectedYaml = load("coproduct/expected_coproduct_discriminator.yml")
val endpoint_wit_sealed_trait: Endpoint[Unit, Unit, Entity, Any] = endpoint
.out(jsonBody[Entity])
val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(endpoint_wit_sealed_trait, Info("Fruits", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

test("should match the expected yaml when using nested coproduct types") {
val expectedYaml = load("coproduct/expected_coproduct_nested.yml")

val endpoint_wit_sealed_trait: Endpoint[Unit, Unit, NestedEntity, Any] = endpoint
.out(jsonBody[NestedEntity])

val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(endpoint_wit_sealed_trait, Info("Fruits", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

test("should match the expected yaml when using nested coproduct types with discriminator") {
val sPerson = implicitly[Schema[Person]]
val sOrganization = implicitly[Schema[Organization]]
implicit val sEntity: Schema[Entity] =
Schema.oneOfUsingField[Entity, String](_.name, _.toString)("john" -> sPerson, "sml" -> sOrganization)

val expectedYaml = load("coproduct/expected_coproduct_discriminator_nested.yml")
val endpoint_wit_sealed_trait: Endpoint[Unit, Unit, NestedEntity, Any] = endpoint
.out(jsonBody[NestedEntity])
val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(endpoint_wit_sealed_trait, Info("Fruits", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

test("should unfold coproducts from unfolded arrays") {
val expectedYaml = load("coproduct/expected_unfolded_coproduct_unfolded_array.yml")

val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(endpoint.out(jsonBody[List[Entity]]), Info("Entities", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("should differentiate when a generic coproduct type is used multiple times") {
val expectedYaml = load("coproduct/expected_generic_coproduct.yml")

val actualYaml = OpenAPIDocsInterpreter
.toOpenAPI(
List(endpoint.in("p1" and jsonBody[GenericEntity[String]]), endpoint.in("p2" and jsonBody[GenericEntity[Int]])),
Info("Fruits", "1.0")
)
.toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

test("support recursive coproducts") {
val expectedYaml = load("coproduct/expected_recursive_coproducts.yml")
val actualYaml = OpenAPIDocsInterpreter
.toOpenAPI(
endpoint.post.in(jsonBody[Clause]),
Info("Entities", "1.0")
)
.toYaml

val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}
}

object VerifyYamlCoproductTest {
sealed trait GenericEntity[T]
case class GenericPerson[T](data: T) extends GenericEntity[T]

sealed trait Clause
case class Expression(v: String) extends Clause
case class Not(not: Clause) extends Clause
case class NestedEntity(entity: Entity)

object Color extends Enumeration {
type Color = Value

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package sttp.tapir.docs.openapi

import io.circe.generic.auto._
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import sttp.tapir.EndpointIO.Example
import sttp.tapir.docs.openapi.dtos.{Author, Book, Country, Genre}
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe._
import sttp.tapir.openapi.Info
import sttp.tapir.openapi.circe.yaml._
import sttp.tapir.tests.{Person, _}
import sttp.tapir.{endpoint, _}

import java.time.ZoneOffset.UTC
import java.time.ZonedDateTime

class VerifyYamlExampleTest extends AnyFunSuite with Matchers {

test("support example of list and not-list types") {
val expectedYaml = load("example/expected_examples_of_list_and_not_list_types.yml")
val actualYaml = OpenAPIDocsInterpreter
.toOpenAPI(
endpoint.post
.in(query[List[String]]("friends").example(List("bob", "alice")))
.in(query[String]("current-person").example("alan"))
.in(jsonBody[Person].example(Person("bob", 23))),
Info("Entities", "1.0")
)
.toYaml

val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("support multiple examples with explicit names") {
val expectedYaml = load("example/expected_multiple_examples_with_names.yml")
val actualYaml = OpenAPIDocsInterpreter
.toOpenAPI(
endpoint.post
.out(
jsonBody[Entity].examples(
List(
Example.of(Person("michal", 40), Some("Michal"), Some("Some summary")),
Example.of(Organization("acme"), Some("Acme"))
)
)
),
Info("Entities", "1.0")
)
.toYaml

val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("support multiple examples with default names") {
val expectedYaml = load("example/expected_multiple_examples_with_default_names.yml")
val actualYaml = OpenAPIDocsInterpreter
.toOpenAPI(
endpoint.post
.in(jsonBody[Person].example(Person("bob", 23)).example(Person("matt", 30))),
Info("Entities", "1.0")
)
.toYaml

val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("support example name even if there is a single example") {
val expectedYaml = load("example/expected_single_example_with_name.yml")
val actualYaml = OpenAPIDocsInterpreter
.toOpenAPI(
endpoint.post
.out(
jsonBody[Entity].example(
Example(Person("michal", 40), Some("Michal"), Some("Some summary"))
)
),
Info("Entities", "1.0")
)
.toYaml

val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("support multiple examples with both explicit and default names ") {
val expectedYaml = load("example/expected_multiple_examples_with_explicit_and_default_names.yml")
val actualYaml = OpenAPIDocsInterpreter
.toOpenAPI(
endpoint.post
.in(jsonBody[Person].examples(List(Example.of(Person("bob", 23), name = Some("Bob")), Example.of(Person("matt", 30))))),
Info("Entities", "1.0")
)
.toYaml

val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("support examples in different IO params") {
val expectedYaml = load("example/expected_multiple_examples.yml")
val actualYaml = OpenAPIDocsInterpreter
.toOpenAPI(
endpoint.post
.in(path[String]("country").example("Poland").example("UK"))
.in(query[String]("current-person").example("alan").example("bob"))
.in(jsonBody[Person].example(Person("bob", 23)).example(Person("alan", 50)))
.in(header[String]("X-Forwarded-User").example("user1").example("user2"))
.in(cookie[String]("cookie-param").example("cookie1").example("cookie2"))
.out(jsonBody[Entity].example(Person("michal", 40)).example(Organization("acme"))),
Info("Entities", "1.0")
)
.toYaml

val actualYamlNoIndent = noIndentation(actualYaml)
actualYamlNoIndent shouldBe expectedYaml
}

test("automatically add example for fixed header") {
val expectedYaml = load("example/expected_fixed_header_example.yml")

val e = endpoint.in(header("Content-Type", "application/json"))
val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(e, Info("Examples", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

test("should match the expected yaml when using schema with custom example") {
val expectedYaml = load("example/expected_schema_example.yml")

val expectedDateTime = ZonedDateTime.of(2021, 1, 1, 1, 1, 1, 1, UTC)
val expectedBook = Book("title", Genre("name", "desc"), 2021, Author("name", Country("country")))

implicit val testSchemaZonedDateTime: Schema[ZonedDateTime] = Schema.schemaForZonedDateTime.encodedExample(expectedDateTime)
implicit val testSchemaBook = implicitly[Schema[Book]].encodedExample(circeCodec[Book].encode(expectedBook))

val endpoint_with_dateTimes = endpoint.post.in(jsonBody[ZonedDateTime]).out(jsonBody[Book])

val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(endpoint_with_dateTimes, Info("Examples", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package sttp.tapir.docs.openapi

import io.circe.generic.auto._
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers
import sttp.model.StatusCode
import sttp.tapir.docs.openapi.VerifyYamlOneOfTest._
import sttp.tapir.generic.auto._
import sttp.tapir.json.circe.jsonBody
import sttp.tapir.openapi.Info
import sttp.tapir.openapi.circe.yaml._
import sttp.tapir.{Codec, CodecFormat, Schema, SchemaType, endpoint, header, plainBody, statusCode, statusDefaultMapping, statusMapping}

class VerifyYamlOneOfTest extends AnyFunSuite with Matchers {

test("should support multiple status codes") {
val expectedYaml = load("oneOf/expected_status_codes.yml")

// work-around for #10: unsupported sealed trait families
implicit val schemaForErrorInfo: Schema[ErrorInfo] = Schema[ErrorInfo](SchemaType.SProduct(SchemaType.SObjectInfo("ErrorInfo"), Nil))

val e = endpoint.errorOut(
sttp.tapir.oneOf(
statusMapping(StatusCode.NotFound, jsonBody[NotFound].description("not found")),
statusMapping(StatusCode.Unauthorized, jsonBody[Unauthorized].description("unauthorized")),
statusDefaultMapping(jsonBody[Unknown].description("unknown"))
)
)

val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(e, Info("Fruits", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

test("should support multiple the same status codes") {
val expectedYaml = load("oneOf/expected_the_same_status_codes.yml")

implicit val unauthorizedTextPlainCodec: Codec[String, Unauthorized, CodecFormat.TextPlain] =
Codec.string.map(Unauthorized.apply _)(_.realm)

val e = endpoint.out(
sttp.tapir.oneOf(
statusMapping(StatusCode.Ok, jsonBody[NotFound].description("not found")),
statusMapping(StatusCode.Ok, plainBody[Unauthorized]),
statusMapping(StatusCode.NoContent, jsonBody[Unknown].description("unknown"))
)
)

val actualYaml = OpenAPIDocsInterpreter.toOpenAPI(e, Info("Fruits", "1.0")).toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

test("use status codes declared with description") {
val expectedYaml = load("oneOf/expected_one_of_status_codes.yml")

val actualYaml = OpenAPIDocsInterpreter
.toOpenAPI(
endpoint
.out(header[String]("Location"))
.errorOut(statusCode.description(StatusCode.NotFound, "entity not found").description(StatusCode.BadRequest, "")),
Info("Entities", "1.0")
)
.toYaml
val actualYamlNoIndent = noIndentation(actualYaml)

actualYamlNoIndent shouldBe expectedYaml
}

}

object VerifyYamlOneOfTest {
sealed trait ErrorInfo
case class NotFound(what: String) extends ErrorInfo
case class Unauthorized(realm: String) extends ErrorInfo
case class Unknown(code: Int, msg: String) extends ErrorInfo
}
Loading

0 comments on commit c7cbf0c

Please sign in to comment.