1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
package com.softwaremill.sttp
import io.circe._
import org.scalatest._
import scala.language.higherKinds
class CirceTests extends FlatSpec with Matchers with EitherValues {
import circe._
"The circe module" should "encode arbitrary bodies given an encoder" in {
val body = Outer(Inner(42, true, "horses"), "cats")
val expected = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
val req = sttp.body(body)
extractBody(req) shouldBe expected
}
it should "decode arbitrary bodies given a decoder" in {
val body = """{"foo":{"a":42,"b":true,"c":"horses"},"bar":"cats"}"""
val expected = Outer(Inner(42, true, "horses"), "cats")
val responseAs = asJson[Outer]
runJsonResponseAs(responseAs)(body).right.value shouldBe expected
}
it should "fail to decode invalid json" in {
val body = """not valid json"""
val responseAs = asJson[Outer]
runJsonResponseAs(responseAs)(body).left.value shouldBe an[io.circe.Error]
}
it should "encode and decode back to the same thing" in {
val outer = Outer(Inner(42, true, "horses"), "cats")
val encoded = extractBody(sttp.body(outer))
val decoded = runJsonResponseAs(asJson[Outer])(encoded)
decoded.right.value shouldBe outer
}
it should "set the content type" in {
val body = Outer(Inner(42, true, "horses"), "cats")
val req = sttp.body(body)
val ct = req.headers.toMap.get("Content-Type")
ct shouldBe Some(contentTypeWithEncoding(ApplicationJsonContentType, Utf8))
}
it should "only set the content type if it was not set earlier" in {
val body = Outer(Inner(42, true, "horses"), "cats")
val req = sttp.contentType("horses/cats").body(body)
val ct = req.headers.toMap.get("Content-Type")
ct shouldBe Some("horses/cats")
}
case class Inner(a: Int, b: Boolean, c: String)
object Inner {
implicit val encoder: Encoder[Inner] =
Encoder.forProduct3("a", "b", "c")(i => (i.a, i.b, i.c))
implicit val decoder: Decoder[Inner] =
Decoder.forProduct3("a", "b", "c")(Inner.apply)
}
case class Outer(foo: Inner, bar: String)
object Outer {
implicit val encoder: Encoder[Outer] =
Encoder.forProduct2("foo", "bar")(o => (o.foo, o.bar))
implicit val decoder: Decoder[Outer] =
Decoder.forProduct2("foo", "bar")(Outer.apply)
}
def extractBody[A[_], B, C](request: RequestT[A, B, C]): String =
request.body match {
case StringBody(body, "utf-8", Some(ApplicationJsonContentType)) =>
body
case wrongBody =>
fail(
s"Request body does not serialize to correct StringBody: $wrongBody")
}
def runJsonResponseAs[A](responseAs: ResponseAs[A, Nothing]): String => A =
responseAs match {
case responseAs: MappedResponseAs[_, A, Nothing] =>
responseAs.raw match {
case ResponseAsString("utf-8") =>
responseAs.g
case ResponseAsString(encoding) =>
fail(
s"MappedResponseAs wraps a ResponseAsString with wrong encoding: $encoding")
case _ =>
fail("MappedResponseAs does not wrap a ResponseAsString")
}
case _ => fail("ResponseAs is not a MappedResponseAs")
}
}
|