scala tapir test IO endpoint

i cant find anywere how to make a stub of IO tapir endpoint. In the docs https://tapir.softwaremill.com/en/latest/testing.html i can see only example for scala Future.

Basically i need to make a stub for tapir endpoint

endpoint
      .post
      .in("v1" / "payment")
      .securityIn(auth.basic[UsernamePassword]())
      .in(jsonBody[HttpPaymentRequest])
      .out(jsonBody[HttpPaymentResponse])
      .errorOut(oneOf[StatusError](
        oneOfVariant(statusCode(StatusCode.BadRequest) and jsonBody[Status400Error]),
        oneOfVariant(statusCode(StatusCode.InternalServerError) and jsonBody[Status500Error]),
        oneOfVariant(statusCode(StatusCode.Unauthorized) and jsonBody[Unauthorized])
      ))
      .serverSecurityLogic[Unit, IO](basicAuth(secretLocal))
      .serverLogic(_ => request => ???)

to test auth logic and server logic in 2 different tests

val backendStub: SttpBackend[Future, Any] = TapirStubInterpreter(SttpBackendStub.asynchronousFuture)
      .whenServerEndpoint(someServerEndpoint)
      .thenRunLogic()
      .backend()

this example is working only for Future

if anybody have an example for cats IO, please help

Upvotes: 0

Views: 59

Answers (1)

adamw
adamw

Reputation: 8606

Here's an example test using ScalaTest:

//> using dep com.softwaremill.sttp.tapir::tapir-cats-effect:1.11.2
//> using dep com.softwaremill.sttp.tapir::tapir-sttp-stub-server:1.11.2
//> using dep com.softwaremill.sttp.tapir::tapir-cats:1.11.2
//> using dep com.softwaremill.sttp.client3::core:3.9.7
//> using dep org.scalatest::scalatest:3.2.19

package sttp.tapir.examples.testing

import cats.effect.IO
import org.scalatest.flatspec.AsyncFlatSpec
import org.scalatest.matchers.should.Matchers
import sttp.client3.*
import sttp.client3.testing.SttpBackendStub
import sttp.tapir.*
import sttp.tapir.integ.cats.effect.CatsMonadError
import sttp.tapir.server.ServerEndpoint
import sttp.tapir.server.stub.TapirStubInterpreter

import scala.concurrent.Future

class CatsServerStubInterpreter extends AsyncFlatSpec with Matchers:
  it should "run greet users logic" in {
    // given
    // We need to pass an SttpBackendStub which is configured for the IO effect. One way to do it is to pass a
    // MonadError implementation, as here. Alternatively, you can use any sttp-client cats-effect backend, and obtain
    // the stub using its .stub method. For example: AsyncHttpClientCatsBackend.stub[IO]
    val stubBackend: SttpBackend[IO, Any] = TapirStubInterpreter(SttpBackendStub[IO, Any](CatsMonadError()))
      .whenServerEndpoint(UsersApi.greetUser)
      .thenRunLogic()
      .backend()

    // when
    val response = sttp.client3.basicRequest
      .get(uri"http://test.com/api/users/greet")
      .header("Authorization", "Bearer password")
      .send(stubBackend)

    // then
    // since we are using ScalaTest, we need to run the IO effect, here - synchronously. When using an IO-aware test
    // framework, this might get simplified.
    import cats.effect.unsafe.implicits.global
    response.unsafeRunSync().body shouldBe Right("hello user123")
  }

  // The API under test
  object UsersApi:
    val greetUser: ServerEndpoint[Any, IO] = endpoint.get
      .in("api" / "users" / "greet")
      .securityIn(auth.bearer[String]())
      .out(stringBody)
      .errorOut(stringBody)
      .serverSecurityLogic(token => IO(if token == "password" then Right("user123") else Left("unauthorized")))
      .serverLogic(user => _ => IO(Right(s"hello $user")))

Upvotes: 3

Related Questions