Skip to content

Commit

Permalink
Implement compatibility test
Browse files Browse the repository at this point in the history
  • Loading branch information
lenguyenthanh committed May 9, 2024
1 parent e2272d1 commit 27b2f38
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 17 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,13 @@ jobs:
distribution: temurin
java-version: 21
- run: sbt scalafmtCheckAll

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 21
- run: sbt test
4 changes: 4 additions & 0 deletions .scalafmt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ fileOverride {
runner.dialect = scala3
}

"glob:**/modules/e2e/**" {
runner.dialect = scala3
}

}
12 changes: 11 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,24 @@ lazy val app = (project in file("modules/app"))
http4sEmberClient,
cirisCore,
cirisHtt4s,
log4Cats,
logbackX
),
Compile / run / fork := true
)
.enablePlugins(JavaAppPackaging)
.dependsOn(api, core)

val e2e = (project in file("modules/e2e"))
.settings(
commonSettings,
libraryDependencies ++= Seq(
weaver
)
)
.dependsOn(client, app)

lazy val root = project
.in(file("."))
.settings(publish := {}, publish / skip := true)
.aggregate(core, play, api, app, client)
.aggregate(core, play, api, app, client, e2e)
2 changes: 1 addition & 1 deletion modules/app/src/main/scala/app.rersources.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.sksamuel.elastic4s.http.JavaClient
import com.sksamuel.elastic4s.cats.effect.instances.*
import com.sksamuel.elastic4s.{ ElasticClient, ElasticProperties }

class AppResources private (val esClient: ESClient[IO])
class AppResources(val esClient: ESClient[IO])

object AppResources:

Expand Down
14 changes: 11 additions & 3 deletions modules/client/src/main/scala/PlayClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,29 @@ class PlayClient(client: StandaloneWSClient, baseUrl: String)(using ExecutionCon
import implicits.given

override def count(query: Query): Future[CountResponse] =
request(s"$baseUrl/count", query)
request(s"$baseUrl/count", SearchInput(query))

override def search(query: Query, from: Int, size: Int): Future[SearchResponse] =
request(s"$baseUrl/search/{from}/{int}", query)
request(s"$baseUrl/search/$from/$size", SearchInput(query))

private def request[D: Schema, R: Schema](url: String, data: D): Future[R] =
client
.url(url)
.post(data)
.flatMap:
case res if res.status == 200 => Future(res.body[R])
case res => Future.failed(Exception(s"$url ${res.status}"))
case res => Future.failed(Exception(s"$url ${res.status} ${res.body}"))

final case class SearchInput(query: Query)

object implicits:

import smithy4s.schema.Schema.struct

given Schema[SearchInput] = struct(
Query.schema.required[SearchInput]("query", _.query)
)(SearchInput.apply)

given [A](using JsonCodec[A]): BodyWritable[A] =
BodyWritable(a => InMemoryBody(ByteString.fromArrayUnsafe(writeToArray(a))), "application/json")

Expand Down
21 changes: 9 additions & 12 deletions modules/core/src/main/scala/ESClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ package lila.search
import com.sksamuel.elastic4s.ElasticDsl.{ RichFuture => _, _ }
import com.sksamuel.elastic4s.fields.ElasticField
import com.sksamuel.elastic4s.{ ElasticClient, ElasticDsl, Index => ESIndex, Response }
import com.sksamuel.elastic4s.requests.indexes.IndexResponse
import com.sksamuel.elastic4s.requests.delete.DeleteResponse
import com.sksamuel.elastic4s.requests.bulk.BulkResponse
import com.sksamuel.elastic4s.{ Executor, Functor }
import cats.syntax.all.*
import cats.MonadThrow
Expand All @@ -20,10 +17,10 @@ trait ESClient[F[_]] {

def search[A](index: Index, query: A, from: From, size: Size)(implicit q: Queryable[A]): F[SearchResponse]
def count[A](index: Index, query: A)(implicit q: Queryable[A]): F[CountResponse]
def store(index: Index, id: Id, obj: JsonObject): F[Response[IndexResponse]]
def store(index: Index, id: Id, obj: JsonObject): F[Unit]
def storeBulk(index: Index, objs: List[(String, JsonObject)]): F[Unit]
def deleteOne(index: Index, id: Id): F[Response[DeleteResponse]]
def deleteMany(index: Index, ids: List[Id]): F[Response[BulkResponse]]
def deleteOne(index: Index, id: Id): F[Unit]
def deleteMany(index: Index, ids: List[Id]): F[Unit]
def putMapping(index: Index, fields: Seq[ElasticField]): F[Unit]
def refreshIndex(index: Index): F[Unit]

Expand Down Expand Up @@ -51,8 +48,8 @@ object ESClient {
.flatMap(toResult)
.map(CountResponse.apply)

def store(index: Index, id: Id, obj: JsonObject): F[Response[IndexResponse]] =
client.execute(indexInto(index.name).source(obj.json).id(id.value))
def store(index: Index, id: Id, obj: JsonObject): F[Unit] =
client.execute(indexInto(index.name).source(obj.json).id(id.value)).void

def storeBulk(index: Index, objs: List[(String, JsonObject)]): F[Unit] =
if (objs.isEmpty) ().pure[F]
Expand All @@ -65,17 +62,17 @@ object ESClient {
}
}.void

def deleteOne(index: Index, id: Id): F[Response[DeleteResponse]] =
client.execute(deleteById(index.toES, id.value))
def deleteOne(index: Index, id: Id): F[Unit] =
client.execute(deleteById(index.toES, id.value)).void

def deleteMany(index: Index, ids: List[Id]): F[Response[BulkResponse]] =
def deleteMany(index: Index, ids: List[Id]): F[Unit] =
client.execute {
ElasticDsl.bulk {
ids.map { id =>
deleteById(index.toES, id.value)
}
}
}
}.void

def putMapping(index: Index, fields: Seq[ElasticField]): F[Unit] =
dropIndex(index) >> client.execute {
Expand Down
70 changes: 70 additions & 0 deletions modules/e2e/src/test/scala/CompatSuite.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package lila.search
package test

import cats.effect.{ IO, Resource }
import com.sksamuel.elastic4s.fields.ElasticField
import org.typelevel.log4cats.Logger
import org.typelevel.log4cats.noop.NoOpLogger
import lila.search.app.AppResources
import lila.search.app.SearchApp
import lila.search.client.PlayClient
import lila.search.app.{ AppConfig, ElasticConfig, HttpServerConfig }
import com.comcast.ip4s.*
import akka.actor.ActorSystem
import play.api.libs.ws.*
import play.api.libs.ws.ahc.*
import lila.search.spec.Query
import scala.concurrent.ExecutionContext.Implicits.*

object CompatSuite extends weaver.IOSuite:

given Logger[IO] = NoOpLogger[IO]

override type Res = PlayClient

override def sharedResource: Resource[IO, Res] =
val res = AppResources(fakeClient)
SearchApp(res, testAppConfig)
.run()
.flatMap(_ => wsClient)
.map(PlayClient(_, "http://localhost:9999"))

test("search endpoint"): client =>
val query = Query.Forum("foo")
IO.fromFuture(IO(client.search(query, 0, 10))).map(expect.same(_, lila.search.spec.SearchResponse(Nil)))

test("count endpoint"): client =>
val query = Query.Team("foo")
IO.fromFuture(IO(client.count(query))).map(expect.same(_, lila.search.spec.CountResponse(0)))

def testAppConfig = AppConfig(
server = HttpServerConfig(ip"0.0.0.0", port"9999", shutdownTimeout = 1),
elastic = ElasticConfig("http://0.0.0.0:9200")
)

def fakeClient: ESClient[IO] = new ESClient[IO]:

override def putMapping(index: Index, fields: Seq[ElasticField]): IO[Unit] = IO.unit

override def refreshIndex(index: Index): IO[Unit] = IO.unit

override def storeBulk(index: Index, objs: List[(String, JsonObject)]): IO[Unit] = IO.unit

override def store(index: Index, id: Id, obj: JsonObject): IO[Unit] = IO.unit

override def deleteOne(index: Index, id: Id): IO[Unit] = IO.unit

override def deleteMany(index: Index, ids: List[Id]): IO[Unit] = IO.unit

override def count[A](index: Index, query: A)(implicit q: Queryable[A]): IO[CountResponse] =
IO.pure(CountResponse(0))

override def search[A](index: Index, query: A, from: From, size: Size)(implicit
q: Queryable[A]
): IO[SearchResponse] = IO.pure(SearchResponse(Nil))

given system: ActorSystem = ActorSystem()

def wsClient = Resource.make(IO(StandaloneAhcWSClient()))(x =>
IO(x.close()).flatMap(_ => IO.fromFuture(IO(system.terminate())).void)
)

0 comments on commit 27b2f38

Please sign in to comment.