Skip to content

Instantly share code, notes, and snippets.

@dacr
Last active April 5, 2025 07:36
Show Gist options
  • Save dacr/4471ee84b22d6af75bac81efd8a574e1 to your computer and use it in GitHub Desktop.
Save dacr/4471ee84b22d6af75bac81efd8a574e1 to your computer and use it in GitHub Desktop.
Chronos API tests / published by https://github.com/dacr/code-examples-manager #91025af9-38bd-4b7a-8b6c-7997162400fc/7c606d3a76f46c2073fd66037d6a53567ac1361a
#!/usr/bin/env -S scala-cli test -S 3
// summary : Chronos API tests
// keywords : scala, cats, sttp, chronos, test
// publish : gist
// authors : David Crosson
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
// id : 91025af9-38bd-4b7a-8b6c-7997162400fc
// created-on : 2025-01-11T00:22:40+01:00
// managed-by : https://github.com/dacr/code-examples-manager
// run-with : scala-cli test $file
// test-with : curl http://127.0.0.1:8080/chronos
// ---------------------
//> using scala 3.6.4
//> using objectWrapper
////> using platform native
////> using nativeVersion 0.5.7
//> using dep com.softwaremill.sttp.client4::circe:4.0.0-RC3
//> using dep com.softwaremill.sttp.client4::cats:4.0.0-RC3
//> using dep org.typelevel::cats-effect:3.6.0
//> using dep io.circe::circe-generic:0.14.12
//> using dep org.typelevel::munit-cats-effect-3:1.0.7
// ---------------------
import io.circe.generic.auto.*
import io.circe.*
import io.circe.parser.*
import io.circe.syntax.*
import sttp.client4.*
import sttp.client4.circe.*
import sttp.client4.httpclient.cats.HttpClientCatsBackend
import HttpClientCatsBackend.resource
import cats.effect.{IO, SyncIO}
import munit.CatsEffectSuite
import java.time.{Instant, OffsetDateTime}
import java.util.UUID
object Model {
type CompetitorId = UUID
type ChronosId = UUID
// -------------------------------------------------------------------------------------------------------------------
case class ChronosSpec(
name: String,
description: String
)
// -------------------------------------------------------------------------------------------------------------------
case class CompetitorSpec(
firstName: String,
lastName: String,
birthDate: OffsetDateTime
)
// -------------------------------------------------------------------------------------------------------------------
case class TimerSpec(
name: String
)
// -------------------------------------------------------------------------------------------------------------------
case class Chronos(
id: ChronosId,
name: String,
description: String,
created: Instant
)
// -------------------------------------------------------------------------------------------------------------------
case class Competitor(
id: CompetitorId,
firstName: String,
lastName: String,
birthDate: OffsetDateTime
)
// -------------------------------------------------------------------------------------------------------------------
enum TimingCategory {
case Start
case Step
case End
}
case class Timing(
category: TimingCategory,
timestamp: Instant
)
// -------------------------------------------------------------------------------------------------------------------
}
class ClassicTests extends CatsEffectSuite {
import Model.*
given Encoder[TimingCategory] = Encoder[String].contramap(_.toString)
given Decoder[TimingCategory] = Decoder[String].emap(s => Right(TimingCategory.valueOf(s))) // TODO manage left case
val baseAPI = "http://127.0.0.1:8080"
// -----------------------------------------------------------------------------------------------
test("List chronos") {
resource[IO]().use { backend =>
for {
chronos <-
quickRequest
.get(uri"$baseAPI/chronos")
.response(asJson[List[Chronos]])
.send(backend)
} yield assertEquals(chronos.code.code, 200)
}
}
// -----------------------------------------------------------------------------------------------
test("Create, read, update, delete a chronos") {
resource[IO]().use { backend =>
for {
randName <- IO.randomUUID.map(uuid => s"Chronos $uuid")
chronos <-
quickRequest
.post(uri"$baseAPI/chronos")
.body(ChronosSpec(randName, s"$randName description").asJson.noSpaces)
.response(asJson[Chronos])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
gottenChronos <-
quickRequest
.get(uri"$baseAPI/chronos/${chronos.id}")
.response(asJson[Chronos])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
_ <-
quickRequest
.put(uri"$baseAPI/chronos/${chronos.id}")
.body(ChronosSpec(randName, s"$randName updated description").asJson.noSpaces)
.send(backend)
updatedChronos <-
quickRequest
.get(uri"$baseAPI/chronos/${chronos.id}")
.response(asJson[Chronos])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
responseDelete <-
quickRequest
.delete(uri"$baseAPI/chronos/${chronos.id}")
.send(backend)
resultAfterDelete <-
quickRequest
.get(uri"$baseAPI/chronos/${chronos.id}")
.response(asJson[Chronos])
.send(backend)
} yield {
assertEquals(chronos, gottenChronos)
assertEquals(updatedChronos.description, s"$randName updated description")
assert(resultAfterDelete.code.code == 404)
}
}
}
// -----------------------------------------------------------------------------------------------
test("List Competitors") {
resource[IO]().use { backend =>
for {
randName <- IO.randomUUID.map(uuid => s"Chronos $uuid")
chronos <-
quickRequest
.post(uri"$baseAPI/chronos")
.body(ChronosSpec(randName, s"$randName description").asJson.noSpaces)
.response(asJson[Chronos])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
competitors <-
quickRequest
.get(uri"$baseAPI/chronos/${chronos.id}/competitor")
.response(asJson[List[Competitor]])
.send(backend)
} yield assertEquals(competitors.code.code, 200)
}
}
// -----------------------------------------------------------------------------------------------
test("Create, read, update, delete a competitor") {
resource[IO]().use { backend =>
for {
randName <- IO.randomUUID.map(uuid => s"Chronos $uuid")
randFirstName <- IO.randomUUID.map(uuid => s"Joe-$uuid")
chronos <-
quickRequest
.post(uri"$baseAPI/chronos")
.body(ChronosSpec(randName, s"$randName description").asJson.noSpaces)
.response(asJson[Chronos])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
competitor <-
quickRequest
.post(uri"$baseAPI/chronos/${chronos.id}/competitor")
.body(CompetitorSpec(randFirstName, "doe", OffsetDateTime.parse("1942-01-01T00:00:00Z")).asJson.noSpaces)
.response(asJson[Competitor])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
gottenCompetitor <-
quickRequest
.get(uri"$baseAPI/chronos/${chronos.id}/competitor/${competitor.id}")
.response(asJson[Competitor])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
_ <-
quickRequest
.put(uri"$baseAPI/chronos/${chronos.id}/competitor/${competitor.id}")
.body(CompetitorSpec(s"$randFirstName-updated", competitor.lastName, competitor.birthDate).asJson.noSpaces)
.send(backend)
updatedCompetitor <-
quickRequest
.get(uri"$baseAPI/chronos/${chronos.id}/competitor/${competitor.id}")
.response(asJson[Competitor])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
responseDelete <-
quickRequest
.delete(uri"$baseAPI/chronos/${chronos.id}/competitor/${competitor.id}")
.send(backend)
resultAfterDelete <-
quickRequest
.get(uri"$baseAPI/chronos/${chronos.id}/competitor/${competitor.id}")
.response(asJson[Competitor])
.send(backend)
} yield {
assertEquals(competitor, gottenCompetitor)
assert(updatedCompetitor.firstName.contains("updated"))
assert(resultAfterDelete.code.code == 404)
}
}
}
// -----------------------------------------------------------------------------------------------
test("Take timings of a competitor ") {
resource[IO]().use { backend =>
for {
randName <- IO.randomUUID.map(uuid => s"Chronos $uuid")
chronos <-
quickRequest
.post(uri"$baseAPI/chronos")
.body(ChronosSpec(randName, s"$randName description").asJson.noSpaces)
.response(asJson[Chronos])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
competitor <-
quickRequest
.post(uri"$baseAPI/chronos/${chronos.id}/competitor")
.body(CompetitorSpec("john", "doe", OffsetDateTime.parse("1942-01-01T00:00:00Z")).asJson.noSpaces)
.response(asJson[Competitor])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
timingStart <-
quickRequest
.put(uri"$baseAPI/chronos/${chronos.id}/competitor/${competitor.id}/timings?Category=${TimingCategory.Start}")
.response(asJson[Timing])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
timingEnd <-
quickRequest
.put(uri"$baseAPI/chronos/${chronos.id}/competitor/${competitor.id}/timings?Category=${TimingCategory.End}")
.response(asJson[Timing])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
timings <-
quickRequest
.get(uri"$baseAPI/chronos/${chronos.id}/competitor/${competitor.id}/timings")
.response(asJson[List[Timing]])
.send(backend)
.flatMap(response => IO.fromEither(response.body))
} yield {
assert(timings.size == 2)
}
}
}
// -----------------------------------------------------------------------------------------------
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment