Skip to content

Commit a895fb1

Browse files
committed
Add tests
1 parent 22618cb commit a895fb1

File tree

5 files changed

+85
-7
lines changed

5 files changed

+85
-7
lines changed

build.sbt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ lazy val noPublish = List(
2424
lazy val Versions = new {
2525
val grpc = "1.68.1"
2626
val http4s = "0.23.29"
27+
val logback = "1.5.12"
2728
}
2829

2930
lazy val core = project
@@ -51,6 +52,8 @@ lazy val core = project
5152
"org.http4s" %% "http4s-client" % Versions.http4s % Test,
5253

5354
"org.scalatest" %% "scalatest" % "3.2.19" % Test,
55+
56+
"ch.qos.logback" % "logback-classic" % Versions.logback % Test,
5457
),
5558
)
5659

@@ -63,7 +66,7 @@ lazy val conformance = project
6366
libraryDependencies ++= Seq(
6467
"org.http4s" %% "http4s-ember-server" % Versions.http4s,
6568

66-
"ch.qos.logback" % "logback-classic" % "1.5.12" % Runtime,
69+
"ch.qos.logback" % "logback-classic" % Versions.logback % Runtime,
6770
),
6871
)
6972

core/src/main/scala/org/ivovk/connect_rpc_scala/ConnectRpcHttpRoutes.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import cats.Endo
44
import cats.effect.Async
55
import cats.effect.kernel.Resource
66
import cats.implicits.*
7+
import fs2.{Chunk, Stream}
78
import fs2.compression.Compression
89
import io.grpc.*
910
import io.grpc.MethodDescriptor.MethodType
@@ -19,6 +20,7 @@ import scalapb.grpc.ClientCalls
1920
import scalapb.json4s.{JsonFormat, Printer}
2021
import scalapb.{GeneratedMessage, GeneratedMessageCompanion, TextFormat}
2122

23+
import java.net.URLDecoder
2224
import java.util.concurrent.atomic.AtomicReference
2325
import scala.concurrent.duration.*
2426
import scala.util.chaining.*
@@ -71,7 +73,12 @@ object ConnectRpcHttpRoutes {
7173
case Some(entry) if entry.methodDescriptor.isSafe =>
7274
entry.methodDescriptor.getType match
7375
case MethodType.UNARY =>
74-
handleUnary(dsl, entry, req, ipChannel)
76+
val body = Stream.fromOption(req.uri.query.params.get("value"))
77+
.map(URLDecoder.decode(_, Charset.`UTF-8`.nioCharset))
78+
.flatMap(s => Stream.chunk(Chunk.array(s.getBytes)))
79+
val media = Media[F](body, req.headers)
80+
81+
handleUnary(dsl, entry, media, ipChannel)
7582
case unsupported =>
7683
NotImplemented(connectrpc.Error(
7784
code = io.grpc.Status.UNIMPLEMENTED.toConnectCode,
@@ -125,7 +132,7 @@ object ConnectRpcHttpRoutes {
125132
private def handleUnary[F[_] : Async](
126133
dsl: Http4sDsl[F],
127134
entry: RegistryEntry,
128-
req: Request[F],
135+
req: Media[F],
129136
channel: Channel
130137
)(using codec: MessageCodec[F]): F[Response[F]] = {
131138
import dsl.*

core/src/test/protobuf/TestService.proto

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ package org.ivovk.connect_rpc_scala.test;
44

55
service TestService {
66
rpc Add(AddRequest) returns (AddResponse) {}
7+
8+
// This method can be called using GET request
9+
rpc Get(GetRequest) returns (GetResponse) {
10+
option idempotency_level = NO_SIDE_EFFECTS;
11+
}
712
}
813

914
message AddRequest {
@@ -14,3 +19,11 @@ message AddRequest {
1419
message AddResponse {
1520
int32 sum = 1;
1621
}
22+
23+
message GetRequest {
24+
string key = 1;
25+
}
26+
27+
message GetResponse {
28+
string value = 1;
29+
}

core/src/test/resources/logback.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<!DOCTYPE configuration>
3+
4+
<configuration>
5+
6+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
7+
<encoder>
8+
<pattern>%-4relative %-5level %logger{35} -%kvp- %msg%n</pattern>
9+
</encoder>
10+
</appender>
11+
12+
<root level="WARN">
13+
<appender-ref ref="STDOUT"/>
14+
</root>
15+
<logger name="org.ivovk.connect_rpc_scala" level="TRACE"/>
16+
</configuration>

core/src/test/scala/org/ivovk/connect_rpc_scala/HttpTest.scala

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ import cats.effect.unsafe.implicits.global
55
import cats.syntax.all.*
66
import io.grpc.ServerServiceDefinition
77
import org.http4s.client.Client
8+
import org.http4s.dsl.io.Root
89
import org.http4s.headers.`Content-Type`
910
import org.http4s.implicits.*
1011
import org.http4s.{Method, *}
1112
import org.ivovk.connect_rpc_scala.test.TestService.TestServiceGrpc.TestService
12-
import org.ivovk.connect_rpc_scala.test.TestService.{AddRequest, AddResponse}
13+
import org.ivovk.connect_rpc_scala.test.TestService.{AddRequest, AddResponse, GetRequest, GetResponse}
1314
import org.scalatest.funsuite.AnyFunSuite
1415
import org.scalatest.matchers.should.Matchers
1516

17+
import java.net.URLEncoder
1618
import scala.concurrent.{ExecutionContext, Future}
1719
import scala.jdk.CollectionConverters.*
1820

@@ -21,12 +23,15 @@ class HttpTest extends AnyFunSuite, Matchers {
2123
object TestServiceImpl extends TestService {
2224
override def add(request: AddRequest): Future[AddResponse] =
2325
Future.successful(AddResponse(request.a + request.b))
26+
27+
override def get(request: GetRequest): Future[GetResponse] = {
28+
Future.successful(GetResponse("Key is: " + request.key))
29+
}
2430
}
2531

2632
// String-JSON encoder
27-
given [F[_]]: EntityEncoder[F, String] =
28-
EntityEncoder.stringEncoder[F]
29-
.withContentType(`Content-Type`(MediaType.application.json))
33+
given [F[_]]: EntityEncoder[F, String] = EntityEncoder.stringEncoder[F]
34+
.withContentType(`Content-Type`(MediaType.application.json))
3035

3136
test("basic") {
3237
val services: Seq[ServerServiceDefinition] = Seq(
@@ -55,4 +60,38 @@ class HttpTest extends AnyFunSuite, Matchers {
5560
.unsafeRunSync()
5661
}
5762

63+
test("GET requests") {
64+
val services: Seq[ServerServiceDefinition] = Seq(
65+
TestService.bindService(TestServiceImpl, ExecutionContext.global)
66+
)
67+
68+
ConnectRpcHttpRoutes.create[IO](services.toList)
69+
.flatMap { routes =>
70+
val client = Client.fromHttpApp(routes.orNotFound)
71+
72+
val requestJson = URLEncoder.encode("""{"key":"123"}""", Charset.`UTF-8`.nioCharset)
73+
74+
client.run(
75+
Request[IO](
76+
Method.GET,
77+
Uri(
78+
path = Root / "org.ivovk.connect_rpc_scala.test.TestService" / "Get",
79+
query = Query.fromPairs("encoding" -> "json", "value" -> requestJson)
80+
)
81+
)
82+
)
83+
}
84+
.use { response =>
85+
for {
86+
body <- response.as[String]
87+
status <- response.status.pure[IO]
88+
} yield {
89+
assert(body == """{"value":"Key is: 123"}""")
90+
assert(status == Status.Ok)
91+
assert(response.headers.get[`Content-Type`].map(_.mediaType).contains(MediaType.application.json))
92+
}
93+
}
94+
.unsafeRunSync()
95+
}
96+
5897
}

0 commit comments

Comments
 (0)