Skip to content

Commit

Permalink
Implement FishnetRoutesTest and fix json api
Browse files Browse the repository at this point in the history
  • Loading branch information
lenguyenthanh committed Nov 21, 2023
1 parent 381ff3b commit 1f3964b
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 14 deletions.
10 changes: 5 additions & 5 deletions app/src/main/scala/Executor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ object Executor:
ref.flatModify: coll =>
coll get workId match
case None =>
coll -> notFound(workId, data.key)
case Some(move) if move.isAcquiredBy(data.key) =>
data.move.uci match
coll -> notFound(workId, data.fishnet.apikey)
case Some(move) if move.isAcquiredBy(data.fishnet.apikey) =>
data.move.bestmove.uci match
case Some(uci) =>
coll - move.id -> (success(move) >> client.send(Lila.Move(
move.request.id,
Expand All @@ -71,9 +71,9 @@ object Executor:
)))
case _ =>
updateOrGiveUp(coll, move.invalid) ->
failure(move, data.key, new Exception("Missing move"))
failure(move, data.fishnet.apikey, new Exception("Missing move"))
case Some(move) =>
coll -> notAcquired(move, data.key)
coll -> notAcquired(move, data.fishnet.apikey)

def clean(since: Instant): IO[Unit] =
ref.update: coll =>
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/scala/http/FishnetRoutes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ final class FishnetRoutes(executor: Executor) extends Http4sDsl[IO]:
req
.decode[Fishnet.PostMove]: move =>
executor.move(id, move)
>> executor.acquire(move.key)
>> executor.acquire(move.fishnet.apikey)
.map(_.map(_.toResponse))
.flatMap(_.fold(NoContent())(Ok(_)))
.recoverWith:
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/scala/http/HttpApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ final class HttpApi(executor: Executor, healthCheck: HealthCheck):
private val middleware = autoSlash andThen timeout

private val loggers: HttpApp[IO] => HttpApp[IO] =
RequestLogger.httpApp[IO](true, true) andThen
ResponseLogger.httpApp[IO, Request[IO]](true, true)
RequestLogger.httpApp[IO](false, true) andThen
ResponseLogger.httpApp[IO, Request[IO]](false, true)

val httpApp: HttpApp[IO] = loggers(middleware(routes).orNotFound)
3 changes: 2 additions & 1 deletion app/src/main/scala/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ object Fishnet:

case class Acquire(fishnet: Fishnet) derives Codec.AsObject
case class Fishnet(version: String, apikey: ClientKey) derives Codec.AsObject
case class PostMove(key: ClientKey, move: BestMove) derives Codec.AsObject
case class PostMove(fishnet: Fishnet, move: Move) derives Codec.AsObject
case class Move(bestmove: BestMove)

case class Work(id: WorkId, level: Int, clock: Option[Lila.Clock], `type`: String = "move")
derives Encoder.AsObject
Expand Down
7 changes: 3 additions & 4 deletions app/src/test/scala/ExecutorTest.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package lila.fishnet

import weaver.*
import weaver.scalacheck.Checkers
import cats.effect.IO
import cats.effect.kernel.Ref
import java.time.Instant

object ExecutorTest extends SimpleIOSuite with Checkers:
object ExecutorTest extends SimpleIOSuite:

val request: Lila.Request = Lila.Request(
id = GameId("id"),
Expand All @@ -19,8 +18,8 @@ object ExecutorTest extends SimpleIOSuite with Checkers:

val key = ClientKey("key")

val validMove = Fishnet.PostMove(key, BestMove("e2e4"))
val invalidMove = Fishnet.PostMove(key, BestMove("ee4"))
val validMove = Fishnet.PostMove(Fishnet.Fishnet("v1", key), Fishnet.Move(BestMove("e2e4")))
val invalidMove = Fishnet.PostMove(Fishnet.Fishnet("v1", key), Fishnet.Move(BestMove("2e4")))

test("acquire when there is no work should return none"):
for
Expand Down
93 changes: 93 additions & 0 deletions app/src/test/scala/http/FishnetRoutesTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package lila.fishnet
package http

import cats.effect.IO
import cats.syntax.all.*
import io.circe.*
import io.circe.literal.*
import java.time.Instant
import org.http4s.*
import org.http4s.implicits.*
import org.http4s.circe.*
import weaver.*

object FishnetRoutesTest extends SimpleIOSuite:

val acqurieRequestBody = json"""{
"fishnet": {
"version": "1.0.0",
"apikey": "apikey"
}
}"""

val postMoveRequestBody = json"""{
"fishnet": {
"version": "1.0.0",
"apikey": "apikey"
},
"move": {
"bestmove": "e2e4"
}
}"""

val workResponse: Json = json"""{
"work": {
"id": "workid",
"level": 1,
"clock": {
"wtime": 600,
"btime": 600,
"inc": 0
},
"type": "move"
},
"game_id": "gameid",
"position": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR",
"moves": "",
"variant": "Standard"
}"""

val requestWithId = Work.RequestWithId(
id = WorkId("workid"),
request = Lila.Request(
id = GameId("gameid"),
initialFen = chess.format.Fen.Epd("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR"),
moves = "",
variant = chess.variant.Standard,
level = 1,
clock = Some(Lila.Clock(wtime = 600, btime = 600, inc = 0)),
),
)

test("POST /fishnet/acquire should return work response"):
val executor = createExecutor()
val routes = createRoutes(executor)
val req = Request[IO](Method.POST, uri"/fishnet/acquire").withEntity(acqurieRequestBody)
exepectHttpBodyAndStatus(routes, req)(expectedBody = workResponse, expectedStatus = Status.Ok)

test("POST /fishnet/move should also return work response"):
val executor = createExecutor()
val routes = createRoutes(executor)
val req = Request[IO](Method.POST, uri"/fishnet/move/workid").withEntity(postMoveRequestBody)
exepectHttpBodyAndStatus(routes, req)(expectedBody = workResponse, expectedStatus = Status.Ok)

def exepectHttpBodyAndStatus(routes: HttpRoutes[IO], req: Request[IO])(
expectedBody: Json,
expectedStatus: Status,
) =
routes.run(req).value.flatMap:
case Some(resp) => resp.asJson.map:
expect.same(_, expectedBody) `and` expect.same(resp.status, expectedStatus)
case _ => IO.pure(failure("expected response but not found"))

def createRoutes(executor: Executor): HttpRoutes[IO] =
FishnetRoutes(executor).routes

def createExecutor(): Executor =
new Executor:
def acquire(key: ClientKey) = IO.pure(requestWithId.some)
def move(id: WorkId, move: Fishnet.PostMove): IO[Unit] =
if id == requestWithId.id then IO.unit
else IO.raiseError(new Exception("invalid work id"))
def add(request: Lila.Request): IO[Unit] = IO.unit
def clean(before: Instant) = IO.unit
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ lazy val app = project
munitScalacheck,
weaver,
weaverScalaCheck,
http4sClient,
circeLiteral,
),
)

Expand Down
3 changes: 2 additions & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ object Dependencies {
val circeCore = circe("core")
val circeParser = circe("parser")
val circeGeneric = circe("generic")
val circeLiteral = circe("literal") % Test

val cirisCore = "is.cir" %% "ciris" % V.ciris
val cirisHtt4s = "is.cir" %% "ciris-http4s" % V.ciris
Expand All @@ -40,7 +41,7 @@ object Dependencies {

val http4sDsl = http4s("dsl")
val http4sServer = http4s("ember-server")
val http4sClient = http4s("ember-client")
val http4sClient = http4s("ember-client") % Test
val http4sCirce = http4s("circe")

val log4Cats = "org.typelevel" %% "log4cats-slf4j" % "2.6.0"
Expand Down

0 comments on commit 1f3964b

Please sign in to comment.