aboutsummaryrefslogblamecommitdiff
path: root/core/src/test/scala/com/softwaremill/sttp/testing/SttpBackendStubTests.scala
blob: 1299caeb427c16798ddee166f7cbf5ce5417d6b3 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                     
                                   

                                            



                                            
                                                         

                              
                                                                             
                                                                       





                                                               
                                
                                                                                    
                                                
                                                                                     
                                                   
      
                                                     
                                                                              
 
                                                               


                                                          
                            














                                                                  
                            

   







                                                          

                                                              
                                                      








                                                             
 










                                                                  


                                                                               
                                                








                                                                                
                                                




                                                          













                                                                  
                                            
                                                                               










                                            
                                                                               










                                            
                                                                               










                                            
                                                                               









                                   

                                                                         
                                           
 
                                                                      
                                  




                                                







                                               
                                                       
















                                                                                                





                                                                  
                                                                                  
                                       
                                                                                  








                                                                                   
                                                                                       

     
 
package com.softwaremill.sttp.testing

import java.io.ByteArrayInputStream
import java.util.concurrent.TimeoutException

import com.softwaremill.sttp._
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.{FlatSpec, Matchers}

import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

class SttpBackendStubTests extends FlatSpec with Matchers with ScalaFutures {
  private val testingStub = SttpBackendStub(HttpURLConnectionBackend())
    .whenRequestMatches(_.uri.path.startsWith(List("a", "b")))
    .thenRespondOk()
    .whenRequestMatches(_.uri.paramsMap.get("p").contains("v"))
    .thenRespond(10)
    .whenRequestMatches(_.method == Method.GET)
    .thenRespondServerError()
    .whenRequestMatchesPartial({
      case r if r.method == Method.POST && r.uri.path.endsWith(List("partial10")) =>
        Response(Right(10), 200, "OK", Nil, Nil)
      case r if r.method == Method.POST && r.uri.path.endsWith(List("partialAda")) =>
        Response(Right("Ada"), 200, "OK", Nil, Nil)
    })
    .whenRequestMatches(_.uri.port.exists(_ == 8080))
    .thenRespondWrapped(Response(Right("OK from monad"), 200, "OK", Nil, Nil))

  "backend stub" should "use the first rule if it matches" in {
    implicit val b = testingStub
    val r = sttp.get(uri"http://example.org/a/b/c").send()
    r.is200 should be(true)
    r.body should be('right)
  }

  it should "use subsequent rules if the first doesn't match" in {
    implicit val b = testingStub
    val r = sttp
      .get(uri"http://example.org/d?p=v")
      .response(asString.map(_.toInt))
      .send()
    r.body should be(Right(10))
  }

  it should "use the first specified rule if multiple match" in {
    implicit val b = testingStub
    val r = sttp.get(uri"http://example.org/a/b/c?p=v").send()
    r.is200 should be(true)
    r.body should be('right)
  }

  it should "respond with monad with set response" in {
    implicit val b = testingStub
    val r = sttp.post(uri"http://example.org:8080").send()
    r.is200 should be(true)
    r.body should be('right)
    r.body.right.get should be("OK from monad")
  }

  it should "use the default response if no rule matches" in {
    implicit val b = testingStub
    val r = sttp.put(uri"http://example.org/d").send()
    r.code should be(404)
  }

  it should "wrap responses in the desired monad" in {
    import scala.concurrent.ExecutionContext.Implicits.global
    implicit val b = SttpBackendStub(new FutureMonad())
    val r = sttp.post(uri"http://example.org").send()
    r.futureValue.code should be(404)
  }

  it should "use rules in partial function" in {
    implicit val s = testingStub
    val r = sttp.post(uri"http://example.org/partial10").send()
    r.is200 should be(true)
    r.body should be(Right(10))

    val ada = sttp.post(uri"http://example.org/partialAda").send()
    ada.is200 should be(true)
    ada.body should be(Right("Ada"))
  }

  it should "handle exceptions thrown instead of a response (synchronous)" in {
    implicit val s = SttpBackendStub(HttpURLConnectionBackend())
      .whenRequestMatches(_ => true)
      .thenRespond(throw new TimeoutException())

    a[TimeoutException] should be thrownBy {
      sttp.get(uri"http://example.org").send()
    }
  }

  it should "handle exceptions thrown instead of a response (asynchronous)" in {
    implicit val s = SttpBackendStub(new FutureMonad())
      .whenRequestMatches(_ => true)
      .thenRespond(throw new TimeoutException())

    val result = sttp.get(uri"http://example.org").send()
    result.failed.futureValue shouldBe a[TimeoutException]
  }

  it should "try to convert a basic response to a mapped one" in {
    implicit val s = SttpBackendStub(HttpURLConnectionBackend())
      .whenRequestMatches(_ => true)
      .thenRespond("10")

    val result = sttp
      .get(uri"http://example.org")
      .mapResponse(_.toInt)
      .mapResponse(_ * 2)
      .send()

    result.body should be(Right(20))
  }

  it should "handle a 201 as a success" in {
    implicit val s = SttpBackendStub(HttpURLConnectionBackend()).whenAnyRequest
      .thenRespondWithCode(201)

    val result = sttp
      .get(uri"http://example.org")
      .send()

    result.body should be(Right(""))

  }

  it should "handle a 300 as a failure" in {
    implicit val s = SttpBackendStub(HttpURLConnectionBackend()).whenAnyRequest
      .thenRespondWithCode(300)

    val result = sttp
      .get(uri"http://example.org")
      .send()

    result.body should be(Left(""))

  }

  it should "handle a 400 as a failure" in {
    implicit val s = SttpBackendStub(HttpURLConnectionBackend()).whenAnyRequest
      .thenRespondWithCode(400)

    val result = sttp
      .get(uri"http://example.org")
      .send()

    result.body should be(Left(""))

  }

  it should "handle a 500 as a failure" in {
    implicit val s = SttpBackendStub(HttpURLConnectionBackend()).whenAnyRequest
      .thenRespondWithCode(500)

    val result = sttp
      .get(uri"http://example.org")
      .send()

    result.body should be(Left(""))

  }

  it should "not hold the calling thread when passed a future monad" in {
    val LongTimeMillis = 10000L
    val before = System.currentTimeMillis()

    implicit val s = SttpBackendStub(new FutureMonad()).whenAnyRequest
      .thenRespondWrapped(Future {
        Thread.sleep(LongTimeMillis)
        Response(Right("OK"), 200, "", Nil, Nil)
      })

    sttp
      .get(uri"http://example.org")
      .send()

    val after = System.currentTimeMillis()

    (after - before) should be < LongTimeMillis
  }

  private val testingStubWithFallback = SttpBackendStub
    .withFallback(testingStub)
    .whenRequestMatches(_.uri.path.startsWith(List("c")))
    .thenRespond("ok")

  "backend stub with fallback" should "use the stub when response for a request is defined" in {
    implicit val b = testingStubWithFallback

    val r = sttp.post(uri"http://example.org/c").send()
    r.body should be(Right("ok"))
  }

  it should "delegate to the fallback for unhandled requests" in {
    implicit val b = testingStubWithFallback

    val r = sttp.post(uri"http://example.org/a/b").send()
    r.is200 should be(true)
  }

  private val s = "Hello, world!"
  private val adjustTestData = List[(Any, ResponseAs[_, _], Any)](
    (s, IgnoreResponse, Some(())),
    (s, ResponseAsString(Utf8), Some(s)),
    (s.getBytes(Utf8), ResponseAsString(Utf8), Some(s)),
    (new ByteArrayInputStream(s.getBytes(Utf8)), ResponseAsString(Utf8), Some(s)),
    (10, ResponseAsString(Utf8), None),
    ("10", MappedResponseAs(ResponseAsString(Utf8), (_: String).toInt), Some(10)),
    (10, MappedResponseAs(ResponseAsString(Utf8), (_: String).toInt), None)
  )

  behavior of "tryAdjustResponseBody"

  for {
    (body, responseAs, expectedResult) <- adjustTestData
  } {
    it should s"adjust $body to $expectedResult when specified as $responseAs" in {
      SttpBackendStub.tryAdjustResponseBody(responseAs, body) should be(expectedResult)
    }
  }
}