Add skipped rounds info

This commit is contained in:
Claude Brisson
2023-05-16 12:46:13 +02:00
parent e347183b56
commit 100d28e483
8 changed files with 95 additions and 40 deletions

19
test.sh
View File

@@ -1,19 +1,4 @@
#!/bin/bash
curl -s --header "Accept: application/json" --header "Content-Type: application/json" \
--request POST \
--data '{ "type":"INDIVIDUAL","name":"Mon Tournoi", "shortName": "mon-tournoi", "startDate": "2023-05-10", "endDate": "2023-05-12", "country": "FR", "location": "Marseille", "online": false, "timeSystem": { "type": "fisher", "mainTime": "1200", "increment": "10" }, "pairing": { "type": "ROUNDROBIN" } }' \
http://localhost:8080/api/tour
curl -s --header "Accept: application/json" http://localhost:8080/api/tour
curl -s --header "Accept: application/json" http://localhost:8080/api/tour/1
curl -s --header "Accept: application/json" --header "Content-Type: application/json" \
--request POST \
--data '{ "name": "Burma", "firstname": "Nestor", "rating": 1600, "rank": -2, "country": "FR", "club": "13Ma" }' \
http://localhost:8080/api/tour/1/part
curl -s --header "Accept: application/json" http://localhost:8080/api/tour/1/part
#mvn -Dorg.slf4j.simpleLogger.log.test=trace package verify
mvn package verify

View File

@@ -97,6 +97,11 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<classpathDependencyExcludes>
<classpathDependencyExclude>com.republicate:webapp-slf4j-logger</classpathDependencyExclude>
</classpathDependencyExcludes>
</configuration>
</plugin>
</plugins>
</build>
@@ -169,6 +174,12 @@
<version>3.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<!--
<dependency>
<groupId>org.slf4j</groupId>

View File

@@ -8,7 +8,7 @@ import kotlin.math.roundToInt
// Pairable
sealed class Pairable(val id: Int, val name: String, open val rating: Double, open val rank: Int) {
sealed class Pairable(val id: Int, val name: String, open val rating: Int, open val rank: Int) {
abstract fun toJson(): Json.Object
val skip = mutableSetOf<Int>() // skipped rounds
}
@@ -40,7 +40,7 @@ class Player(
id: Int,
name: String,
var firstname: String,
rating: Double,
rating: Int,
rank: Int,
var country: String,
var club: String
@@ -48,7 +48,7 @@ class Player(
companion object
// used to store external IDs ("FFG" => FFG ID, "EGF" => EGF PIN, "AGA" => AGA ID ...)
val externalIds = mutableMapOf<String, String>()
override fun toJson() = Json.Object(
override fun toJson(): Json.Object = Json.MutableObject(
"id" to id,
"name" to name,
"firstname" to firstname,
@@ -56,25 +56,32 @@ class Player(
"rank" to rank,
"country" to country,
"club" to club
)
).also {
if (skip.isNotEmpty()) it["skip"] = Json.Array(skip)
}
}
fun Player.Companion.fromJson(json: Json.Object, default: Player? = null) = Player(
id = json.getInt("id") ?: default?.id ?: Store.nextPlayerId,
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
firstname = json.getString("firstname") ?: default?.firstname ?: badRequest("missing firstname"),
rating = json.getDouble("rating") ?: default?.rating ?: badRequest("missing rating"),
rating = json.getInt("rating") ?: default?.rating ?: badRequest("missing rating"),
rank = json.getInt("rank") ?: default?.rank ?: badRequest("missing rank"),
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
club = json.getString("club") ?: default?.club ?: badRequest("missing club")
)
).also { player ->
player.skip.clear()
json.getArray("skip")?.let {
if (it.isNotEmpty()) player.skip.addAll(it.map { id -> (id as Number).toInt() })
}
}
// Team
class Team(id: Int, name: String): Pairable(id, name, 0.0, 0) {
class Team(id: Int, name: String): Pairable(id, name, 0, 0) {
companion object {}
val players = mutableSetOf<Player>()
override val rating: Double get() = if (players.isEmpty()) super.rating else players.sumOf { player -> player.rating } / players.size
override val rating: Int get() = if (players.isEmpty()) super.rating else (players.sumOf { player -> player.rating.toDouble() } / players.size).roundToInt()
override val rank: Int get() = if (players.isEmpty()) super.rank else (players.sumOf { player -> player.rank.toDouble() } / players.size).roundToInt()
override fun toJson() = Json.Object(
"id" to id,

View File

@@ -114,9 +114,9 @@ class ApiServlet : HttpServlet() {
builder.append(response.status).append(' ')
.append(reason)
if (response.status == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
logger.info(red(">> {}"), builder.toString())
logger.trace(red(">> {}"), builder.toString())
} else {
logger.info(green(">> {}"), builder.toString())
logger.trace(green(">> {}"), builder.toString())
}
// CB TODO - should be bufferized and asynchronously written in synchronous chunks

View File

@@ -25,7 +25,7 @@ fun Logger.logRequest(req: HttpServletRequest, logHeaders: Boolean = false) {
}
// builder.append(' ').append(req.getProtocol());
info(blue("<< {}"), builder.toString())
if (logHeaders) {
if (isTraceEnabled && logHeaders) {
// CB TODO - should be bufferized and asynchronously written in synchronous chunks
// so that header lines from parallel requests are not mixed up in the logs ;
// synchronizing the whole request log is not desirable
@@ -33,7 +33,7 @@ fun Logger.logRequest(req: HttpServletRequest, logHeaders: Boolean = false) {
while (headerNames.hasMoreElements()) {
val name = headerNames.nextElement()
val value = req.getHeader(name)
info(blue("<< {}: {}"), name, value)
trace(blue("<< {}: {}"), name, value)
}
}
}

View File

@@ -11,7 +11,7 @@ import kotlin.test.assertTrue
@TestMethodOrder(Alphanumeric::class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class BasicTests() {
class BasicTests: TestBase() {
val aTournament = Json.Object(
"type" to "INDIVIDUAL",
@@ -32,20 +32,47 @@ class BasicTests() {
)
)
val aPlayer = Json.Object(
"name" to "Burma",
"firstname" to "Nestor",
"rating" to 1600,
"rank" to -2,
"country" to "FR",
"club" to "13Ma"
)
@Test
fun `001 create tournament`() {
val resp = TestAPI.post("/api/tour", aTournament)
assertTrue(resp.isObject, "Json object expected")
assertTrue(resp.asObject().getBoolean("success") == true, "expecting success")
val resp = TestAPI.post("/api/tour", aTournament) as Json.Object
assertTrue(resp.getBoolean("success") == true, "expecting success")
}
@Test
fun `002 get tournament`() {
val resp = TestAPI.get("/api/tour/1")
assertTrue(resp.isObject, "Json object expected")
assertEquals(1, resp.asObject().getInt("id"), "First tournament should have id #1")
val resp = TestAPI.get("/api/tour/1") as Json.Object
assertEquals(1, resp.getInt("id"), "First tournament should have id #1")
// filter out "id", and also "komi", "rules" and "gobanSize" which were provided by default
val cmp = Json.Object(*resp.asObject().entries.filter { it.key !in listOf("id", "komi", "rules", "gobanSize") }.map { Pair(it.key, it.value) }.toTypedArray())
val cmp = Json.Object(*resp.entries.filter { it.key !in listOf("id", "komi", "rules", "gobanSize") }.map { Pair(it.key, it.value) }.toTypedArray())
assertEquals(aTournament.toString(), cmp.toString(), "tournament differs")
}
@Test
fun `003 register user`() {
val resp = TestAPI.post("/api/tour/1/part", aPlayer) as Json.Object
assertTrue(resp.getBoolean("success") == true, "expecting success")
val players = TestAPI.get("/api/tour/1/part") as Json.Array
val player = players[0] as Json.Object
assertEquals(1, player.getInt("id"), "First player should have id #1")
// filter out "id"
val cmp = Json.Object(*player.entries.filter { it.key != "id" }.map { Pair(it.key, it.value) }.toTypedArray())
assertEquals(aPlayer.toString(), cmp.toString(), "player differs")
}
@Test
fun `004 modify user`() {
val resp = TestAPI.put("/api/tour/1/part/1", Json.Object("skip" to Json.Array(1))) as Json.Object
assertTrue(resp.getBoolean("success") == true, "expecting success")
val player = TestAPI.get("/api/tour/1/part/1") as Json.Object
assertEquals("[1]", player.getArray("skip").toString(), "First player should have id #1")
}
}

View File

@@ -0,0 +1,25 @@
package org.jeudego.pairgoth.test
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.TestInfo
import org.slf4j.LoggerFactory
abstract class TestBase {
companion object {
val logger = LoggerFactory.getLogger("test")
private var testClassName: String? = null
@BeforeAll
@JvmStatic
fun prepare() {
testClassName = this::class.simpleName
}
}
@BeforeEach
fun before(testInfo: TestInfo) {
val testName = testInfo.displayName.removeSuffix("()")
logger.info("===== Running $testClassName.$testName =====")
}
}

View File

@@ -40,9 +40,9 @@ object TestAPI {
on { setAttribute(eq(ApiHandler.SELECTOR_KEY), selector.capture()) } doAnswer {}
on { setAttribute(eq(ApiHandler.SUBSELECTOR_KEY), subSelector.capture()) } doAnswer {}
on { setAttribute(eq(ApiHandler.PAYLOAD_KEY), reqPayload.capture()) } doAnswer {}
on { getAttribute(ApiHandler.SELECTOR_KEY) } doAnswer { selector.lastValue }
on { getAttribute(ApiHandler.SUBSELECTOR_KEY) } doAnswer { subSelector.lastValue }
on { getAttribute(ApiHandler.PAYLOAD_KEY) } doAnswer { reqPayload.lastValue }
on { getAttribute(ApiHandler.SELECTOR_KEY) } doAnswer { selector.allValues.lastOrNull() }
on { getAttribute(ApiHandler.SUBSELECTOR_KEY) } doAnswer { subSelector.allValues.lastOrNull() }
on { getAttribute(ApiHandler.PAYLOAD_KEY) } doAnswer { reqPayload.allValues.lastOrNull() }
on { reader } doReturn myReader
on { scheme } doReturn "http"
on { localName } doReturn "pairgoth"