Skip to content

Commit f18cbcb

Browse files
keynmoladamw
andauthored
Remove reliance of tests on Cats Effect, to enable Scala Native release for sttp client module (#4617)
Most tests operate within or via Future anyways, so we remove the level of indirection and express as much as possible in terms of Future, removing the need for CE dependency in a lot of places, but especially on Native. I got `sttpClient4Native4/test` to pass locally, which is all I care about really. Note that CE release on Native 0.5 is imminent, but if it's delayed, this might be _an_ option to get native builds of sttp modules out (and may be others?). Getting a native tapir-sttp-client module is my only goal here, so I don't care if it's achieved by any other means – you can consider this PR if you think it has merit on its own. not a Draft as I want to get a CI run out of it. --------- Co-authored-by: Adam Warski <[email protected]>
1 parent fd36a74 commit f18cbcb

File tree

42 files changed

+370
-368
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+370
-368
lines changed

build.sbt

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,23 @@ val clientTestServerSettings = Seq(
389389
})
390390
)
391391

392+
lazy val superMatrixSettings = Seq((Compile / unmanagedSourceDirectories) ++= {
393+
val allCombos = List("js", "jvm", "native").combinations(2).toList
394+
val dis =
395+
virtualAxes.value.collectFirst { case p: VirtualAxis.PlatformAxis =>
396+
p.directorySuffix
397+
}.get
398+
399+
allCombos
400+
.filter(_.contains(dis))
401+
.map { suff =>
402+
val suffixes = "scala" + suff.mkString("-", "-", "")
403+
404+
(Compile / sourceDirectory).value / suffixes
405+
}
406+
})
407+
408+
392409
lazy val clientTestServer = (projectMatrix in file("client/testserver"))
393410
.settings(commonSettings)
394411
.settings(
@@ -504,15 +521,23 @@ lazy val tests: ProjectMatrix = (projectMatrix in file("tests"))
504521
"io.circe" %%% "circe-generic" % Versions.circe,
505522
"com.softwaremill.common" %%% "tagging" % "2.3.5",
506523
scalaTest.value,
507-
"org.typelevel" %%% "cats-effect" % Versions.catsEffect,
508524
logback
509525
)
510526
)
511-
.jvmPlatform(scalaVersions = scala2And3Versions, settings = commonJvmSettings)
527+
.jvmPlatform(
528+
scalaVersions = scala2And3Versions,
529+
settings = commonJvmSettings ++ Seq(
530+
libraryDependencies += "org.typelevel" %%% "cats-effect" % Versions.catsEffect
531+
)
532+
)
512533
.jsPlatform(
513534
scalaVersions = scala2And3Versions,
514535
settings = commonJsSettings
515536
)
537+
.nativePlatform(
538+
scalaVersions = Seq(scala3),
539+
settings = commonNativeSettings
540+
)
516541
.dependsOn(core, files, circeJson, cats)
517542

518543
lazy val perfServerJavaOptions = List(
@@ -1936,7 +1961,9 @@ lazy val clientTests: ProjectMatrix = (projectMatrix in file("client/tests"))
19361961
)
19371962
.jvmPlatform(scalaVersions = scala2And3Versions, settings = commonJvmSettings)
19381963
.jsPlatform(scalaVersions = scala2And3Versions, settings = commonJsSettings)
1964+
.nativePlatform(scalaVersions = Seq(scala3), settings = commonNativeSettings)
19391965
.dependsOn(tests)
1966+
.settings(superMatrixSettings)
19401967

19411968
lazy val clientCore: ProjectMatrix = (projectMatrix in file("client/core"))
19421969
.settings(commonSettings)
@@ -2059,6 +2086,15 @@ lazy val sttpClient4: ProjectMatrix = (projectMatrix in file("client/sttp-client
20592086
)
20602087
)
20612088
)
2089+
.nativePlatform(
2090+
scalaVersions = Seq(scala3),
2091+
settings = commonNativeSettings ++ Seq(
2092+
libraryDependencies ++= Seq(
2093+
"io.github.cquiroz" %%% "scala-java-time" % Versions.nativeScalaJavaTime,
2094+
"io.github.cquiroz" %%% "scala-java-time-tzdb" % Versions.nativeScalaJavaTime % Test
2095+
)
2096+
)
2097+
)
20622098
.dependsOn(clientCore, clientTests % Test)
20632099

20642100
lazy val playClient: ProjectMatrix = (projectMatrix in file("client/play-client"))

client/http4s-client/src/test/scala/sttp/tapir/client/http4s/Http4ClientStreamingTests.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import cats.effect.IO
55
import fs2.text
66
import sttp.capabilities.fs2.Fs2Streams
77
import sttp.tapir.client.tests.ClientStreamingTests
8+
import cats.effect.unsafe.IORuntime
89

910
class Http4ClientStreamingTests extends Http4sClientTests[Fs2Streams[IO]] with ClientStreamingTests[Fs2Streams[IO]] {
11+
private implicit val ioRT: IORuntime = cats.effect.unsafe.implicits.global
1012
override val streams: Fs2Streams[IO] = Fs2Streams[IO]
1113
override def mkStream(s: String): streams.BinaryStream = fs2.Stream(s).through(text.utf8.encode)
1214
override def rmStream(s: streams.BinaryStream): String = s.through(text.utf8.decode).compile.string.unsafeRunSync()
Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package sttp.tapir.client.http4s
22

3-
import cats.effect.IO
43
import org.http4s.blaze.client.BlazeClientBuilder
54
import org.http4s.{Request, Response, Uri}
65
import sttp.tapir.client.tests.ClientTests
76
import sttp.tapir.{DecodeResult, Endpoint}
87
import scala.concurrent.ExecutionContext.global
8+
import scala.concurrent.Future
9+
import cats.effect._
10+
import cats.effect.unsafe.IORuntime
911

1012
abstract class Http4sClientTests[R] extends ClientTests[R] {
1113
override def send[A, I, E, O](
@@ -14,7 +16,7 @@ abstract class Http4sClientTests[R] extends ClientTests[R] {
1416
securityArgs: A,
1517
args: I,
1618
scheme: String = "http"
17-
): IO[Either[E, O]] = {
19+
): Future[Either[E, O]] = {
1820
val (request, parseResponse) =
1921
Http4sClientInterpreter[IO]()
2022
.toSecureRequestThrowDecodeFailures(e, Some(Uri.unsafeFromString(s"http://localhost:$port")))
@@ -24,7 +26,12 @@ abstract class Http4sClientTests[R] extends ClientTests[R] {
2426
sendAndParseResponse(request, parseResponse)
2527
}
2628

27-
override def safeSend[A, I, E, O](e: Endpoint[A, I, E, O, R], port: Port, securityArgs: A, args: I): IO[DecodeResult[Either[E, O]]] = {
29+
override def safeSend[A, I, E, O](
30+
e: Endpoint[A, I, E, O, R],
31+
port: Port,
32+
securityArgs: A,
33+
args: I
34+
): Future[DecodeResult[Either[E, O]]] = {
2835
val (request, parseResponse) =
2936
Http4sClientInterpreter[IO]()
3037
.toSecureRequest(e, Some(Uri.unsafeFromString(s"http://localhost:$port")))
@@ -34,8 +41,12 @@ abstract class Http4sClientTests[R] extends ClientTests[R] {
3441
sendAndParseResponse(request, parseResponse)
3542
}
3643

44+
private implicit val ioRT: IORuntime = cats.effect.unsafe.implicits.global
45+
3746
private def sendAndParseResponse[Result](request: Request[IO], parseResponse: Response[IO] => IO[Result]) =
38-
BlazeClientBuilder[IO](global).resource.use { client =>
39-
client.run(request).use(parseResponse)
40-
}
47+
BlazeClientBuilder[IO](global).resource
48+
.use { client =>
49+
client.run(request).use(parseResponse)
50+
}
51+
.unsafeToFuture()(ioRT)
4152
}

client/play-client/src/test/scala/sttp/tapir/client/play/PlayClientTests.scala

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,20 @@ abstract class PlayClientTests[R] extends ClientTests[R] {
2222
securityArgs: A,
2323
args: I,
2424
scheme: String = "http"
25-
): IO[Either[E, O]] = {
26-
def response: Future[Either[E, O]] = {
25+
): Future[Either[E, O]] = {
2726
val (req, responseParser) =
2827
PlayClientInterpreter().toSecureRequestThrowDecodeFailures(e, s"http://localhost:$port").apply(securityArgs).apply(args)
2928
req.execute().map(responseParser)
30-
}
31-
IO.fromFuture(IO(response))
3229
}
3330

3431
override def safeSend[A, I, E, O](
3532
e: Endpoint[A, I, E, O, R],
3633
port: Port,
3734
securityArgs: A,
3835
args: I
39-
): IO[DecodeResult[Either[E, O]]] = {
40-
def response: Future[DecodeResult[Either[E, O]]] = {
36+
): Future[DecodeResult[Either[E, O]]] = {
4137
val (req, responseParser) = PlayClientInterpreter().toSecureRequest(e, s"http://localhost:$port").apply(securityArgs).apply(args)
4238
req.execute().map(responseParser)
43-
}
44-
IO.fromFuture(IO(response))
4539
}
4640

4741
}

client/play29-client/src/test/scala/sttp/tapir/client/play/PlayClientTests.scala

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,20 @@ abstract class PlayClientTests[R] extends ClientTests[R] {
2222
securityArgs: A,
2323
args: I,
2424
scheme: String = "http"
25-
): IO[Either[E, O]] = {
26-
def response: Future[Either[E, O]] = {
27-
val (req, responseParser) =
28-
PlayClientInterpreter().toSecureRequestThrowDecodeFailures(e, s"http://localhost:$port").apply(securityArgs).apply(args)
29-
req.execute().map(responseParser)
30-
}
31-
IO.fromFuture(IO(response))
25+
): Future[Either[E, O]] = {
26+
val (req, responseParser) =
27+
PlayClientInterpreter().toSecureRequestThrowDecodeFailures(e, s"http://localhost:$port").apply(securityArgs).apply(args)
28+
req.execute().map(responseParser)
3229
}
3330

3431
override def safeSend[A, I, E, O](
3532
e: Endpoint[A, I, E, O, R],
3633
port: Port,
3734
securityArgs: A,
3835
args: I
39-
): IO[DecodeResult[Either[E, O]]] = {
40-
def response: Future[DecodeResult[Either[E, O]]] = {
41-
val (req, responseParser) = PlayClientInterpreter().toSecureRequest(e, s"http://localhost:$port").apply(securityArgs).apply(args)
42-
req.execute().map(responseParser)
43-
}
44-
IO.fromFuture(IO(response))
36+
): Future[DecodeResult[Either[E, O]]] = {
37+
val (req, responseParser) = PlayClientInterpreter().toSecureRequest(e, s"http://localhost:$port").apply(securityArgs).apply(args)
38+
req.execute().map(responseParser)
4539
}
4640

4741
}

client/sttp-client/src/test/scalajs/sttp/tapir/client/sttp/SttpClientTests.scala

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package sttp.tapir.client.sttp
22

3-
import cats.effect.IO
4-
53
import scala.concurrent.Future
64
import sttp.tapir.{DecodeResult, Endpoint}
75
import sttp.tapir.client.tests.ClientTests
@@ -17,33 +15,29 @@ abstract class SttpClientTests[R >: Any] extends ClientTests[R] {
1715
securityArgs: A,
1816
args: I,
1917
scheme: String = "http"
20-
): IO[Either[E, O]] = {
18+
): Future[Either[E, O]] = {
2119
implicit val wst: WebSocketToPipe[R] = wsToPipe
22-
val response: Future[Either[E, O]] =
23-
SttpClientInterpreter()
24-
.toSecureRequestThrowDecodeFailures(e, Some(uri"$scheme://localhost:$port"))
25-
.apply(securityArgs)
26-
.apply(args)
27-
.send(backend)
28-
.map(_.body)
29-
IO.fromFuture(IO(response))
20+
SttpClientInterpreter()
21+
.toSecureRequestThrowDecodeFailures(e, Some(uri"$scheme://localhost:$port"))
22+
.apply(securityArgs)
23+
.apply(args)
24+
.send(backend)
25+
.map(_.body)
3026
}
3127

3228
override def safeSend[A, I, E, O](
3329
e: Endpoint[A, I, E, O, R],
3430
port: Port,
3531
securityArgs: A,
3632
args: I
37-
): IO[DecodeResult[Either[E, O]]] = {
33+
): Future[DecodeResult[Either[E, O]]] = {
3834
implicit val wst: WebSocketToPipe[R] = wsToPipe
39-
def response: Future[DecodeResult[Either[E, O]]] =
40-
SttpClientInterpreter()
41-
.toSecureRequest(e, Some(uri"http://localhost:$port"))
42-
.apply(securityArgs)
43-
.apply(args)
44-
.send(backend)
45-
.map(_.body)
46-
IO.fromFuture(IO(response))
35+
SttpClientInterpreter()
36+
.toSecureRequest(e, Some(uri"http://localhost:$port"))
37+
.apply(securityArgs)
38+
.apply(args)
39+
.send(backend)
40+
.map(_.body)
4741
}
4842

4943
override protected def afterAll(): Unit = {
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
package sttp.tapir.client.sttp
22

3-
import cats.effect.IO
43
import sttp.capabilities.WebSockets
54
import sttp.capabilities.zio.ZioStreams
65
import sttp.tapir.client.sttp.ws.zio._
76
import sttp.tapir.client.tests.ClientWebSocketTests
87
import zio.stream.{Stream, ZStream}
8+
import concurrent.Future
99

1010
class SttpClientWebSocketZioTests extends SttpClientZioTests[WebSockets with ZioStreams] with ClientWebSocketTests[ZioStreams] {
1111
override val streams: ZioStreams = ZioStreams
@@ -15,13 +15,11 @@ class SttpClientWebSocketZioTests extends SttpClientZioTests[WebSockets with Zio
1515
p: Stream[Throwable, A] => Stream[Throwable, B],
1616
receiveCount: Port,
1717
as: List[A]
18-
): IO[List[B]] = IO.fromFuture(
19-
IO.delay {
20-
unsafeToFuture(
21-
ZStream(as: _*).viaFunction(p).take(receiveCount).runCollect.map(_.toList)
22-
).future
23-
}
24-
)
18+
): Future[List[B]] = {
19+
unsafeToFuture(
20+
ZStream(as: _*).viaFunction(p).take(receiveCount).runCollect.map(_.toList)
21+
).future
22+
}
2523

2624
webSocketTests()
2725
}

client/sttp-client/src/test/scalajs/sttp/tapir/client/sttp/SttpClientZioTests.scala

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package sttp.tapir.client.sttp
22

3-
import cats.effect.IO
43
import sttp.capabilities.WebSockets
54
import sttp.capabilities.zio.ZioStreams
65
import sttp.client3._
@@ -9,6 +8,7 @@ import sttp.tapir.client.tests.ClientTests
98
import sttp.tapir.{DecodeResult, Endpoint}
109
import zio.Runtime.default
1110
import zio.{CancelableFuture, Task, Unsafe}
11+
import concurrent.Future
1212

1313
abstract class SttpClientZioTests[R >: WebSockets with ZioStreams] extends ClientTests[R] {
1414
private val runtime: default.UnsafeAPI = zio.Runtime.default.unsafe
@@ -21,34 +21,32 @@ abstract class SttpClientZioTests[R >: WebSockets with ZioStreams] extends Clien
2121
securityArgs: A,
2222
args: I,
2323
scheme: String = "http"
24-
): IO[Either[E, O]] =
25-
IO.fromFuture(IO.delay {
26-
implicit val wst: WebSocketToPipe[R] = wsToPipe
27-
val send = SttpClientInterpreter()
28-
.toSecureRequestThrowDecodeFailures(e, Some(uri"$scheme://localhost:$port"))
29-
.apply(securityArgs)
30-
.apply(args)
31-
.send(backend)
32-
.map(_.body)
33-
unsafeToFuture(send).future
34-
})
24+
): Future[Either[E, O]] = {
25+
implicit val wst: WebSocketToPipe[R] = wsToPipe
26+
val send = SttpClientInterpreter()
27+
.toSecureRequestThrowDecodeFailures(e, Some(uri"$scheme://localhost:$port"))
28+
.apply(securityArgs)
29+
.apply(args)
30+
.send(backend)
31+
.map(_.body)
32+
unsafeToFuture(send).future
33+
}
3534

3635
override def safeSend[A, I, E, O](
3736
e: Endpoint[A, I, E, O, R],
3837
port: Port,
3938
securityArgs: A,
4039
args: I
41-
): IO[DecodeResult[Either[E, O]]] =
42-
IO.fromFuture(IO.delay {
43-
implicit val wst: WebSocketToPipe[R] = wsToPipe
44-
val send = SttpClientInterpreter()
45-
.toSecureRequest(e, Some(uri"http://localhost:$port"))
46-
.apply(securityArgs)
47-
.apply(args)
48-
.send(backend)
49-
.map(_.body)
50-
unsafeToFuture(send).future
51-
})
40+
): Future[DecodeResult[Either[E, O]]] = {
41+
implicit val wst: WebSocketToPipe[R] = wsToPipe
42+
val send = SttpClientInterpreter()
43+
.toSecureRequest(e, Some(uri"http://localhost:$port"))
44+
.apply(securityArgs)
45+
.apply(args)
46+
.send(backend)
47+
.map(_.body)
48+
unsafeToFuture(send).future
49+
}
5250

5351
override protected def afterAll(): Unit = {
5452
unsafeRun(backend.close())

0 commit comments

Comments
 (0)