Add skipped rounds info
This commit is contained in:
19
test.sh
19
test.sh
@@ -1,19 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
curl -s --header "Accept: application/json" --header "Content-Type: application/json" \
|
#mvn -Dorg.slf4j.simpleLogger.log.test=trace package verify
|
||||||
--request POST \
|
mvn package verify
|
||||||
--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
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -97,6 +97,11 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-plugin</artifactId>
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<classpathDependencyExcludes>
|
||||||
|
<classpathDependencyExclude>com.republicate:webapp-slf4j-logger</classpathDependencyExclude>
|
||||||
|
</classpathDependencyExcludes>
|
||||||
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
@@ -169,6 +174,12 @@
|
|||||||
<version>3.0</version>
|
<version>3.0</version>
|
||||||
<scope>runtime</scope>
|
<scope>runtime</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<version>${slf4j.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<!--
|
<!--
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
@@ -8,7 +8,7 @@ import kotlin.math.roundToInt
|
|||||||
|
|
||||||
// Pairable
|
// 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
|
abstract fun toJson(): Json.Object
|
||||||
val skip = mutableSetOf<Int>() // skipped rounds
|
val skip = mutableSetOf<Int>() // skipped rounds
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ class Player(
|
|||||||
id: Int,
|
id: Int,
|
||||||
name: String,
|
name: String,
|
||||||
var firstname: String,
|
var firstname: String,
|
||||||
rating: Double,
|
rating: Int,
|
||||||
rank: Int,
|
rank: Int,
|
||||||
var country: String,
|
var country: String,
|
||||||
var club: String
|
var club: String
|
||||||
@@ -48,7 +48,7 @@ class Player(
|
|||||||
companion object
|
companion object
|
||||||
// used to store external IDs ("FFG" => FFG ID, "EGF" => EGF PIN, "AGA" => AGA ID ...)
|
// used to store external IDs ("FFG" => FFG ID, "EGF" => EGF PIN, "AGA" => AGA ID ...)
|
||||||
val externalIds = mutableMapOf<String, String>()
|
val externalIds = mutableMapOf<String, String>()
|
||||||
override fun toJson() = Json.Object(
|
override fun toJson(): Json.Object = Json.MutableObject(
|
||||||
"id" to id,
|
"id" to id,
|
||||||
"name" to name,
|
"name" to name,
|
||||||
"firstname" to firstname,
|
"firstname" to firstname,
|
||||||
@@ -56,25 +56,32 @@ class Player(
|
|||||||
"rank" to rank,
|
"rank" to rank,
|
||||||
"country" to country,
|
"country" to country,
|
||||||
"club" to club
|
"club" to club
|
||||||
)
|
).also {
|
||||||
|
if (skip.isNotEmpty()) it["skip"] = Json.Array(skip)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Player.Companion.fromJson(json: Json.Object, default: Player? = null) = Player(
|
fun Player.Companion.fromJson(json: Json.Object, default: Player? = null) = Player(
|
||||||
id = json.getInt("id") ?: default?.id ?: Store.nextPlayerId,
|
id = json.getInt("id") ?: default?.id ?: Store.nextPlayerId,
|
||||||
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
|
name = json.getString("name") ?: default?.name ?: badRequest("missing name"),
|
||||||
firstname = json.getString("firstname") ?: default?.firstname ?: badRequest("missing firstname"),
|
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"),
|
rank = json.getInt("rank") ?: default?.rank ?: badRequest("missing rank"),
|
||||||
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
|
country = json.getString("country") ?: default?.country ?: badRequest("missing country"),
|
||||||
club = json.getString("club") ?: default?.club ?: badRequest("missing club")
|
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
|
// 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 {}
|
companion object {}
|
||||||
val players = mutableSetOf<Player>()
|
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 val rank: Int get() = if (players.isEmpty()) super.rank else (players.sumOf { player -> player.rank.toDouble() } / players.size).roundToInt()
|
||||||
override fun toJson() = Json.Object(
|
override fun toJson() = Json.Object(
|
||||||
"id" to id,
|
"id" to id,
|
||||||
|
@@ -114,9 +114,9 @@ class ApiServlet : HttpServlet() {
|
|||||||
builder.append(response.status).append(' ')
|
builder.append(response.status).append(' ')
|
||||||
.append(reason)
|
.append(reason)
|
||||||
if (response.status == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
|
if (response.status == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
|
||||||
logger.info(red(">> {}"), builder.toString())
|
logger.trace(red(">> {}"), builder.toString())
|
||||||
} else {
|
} else {
|
||||||
logger.info(green(">> {}"), builder.toString())
|
logger.trace(green(">> {}"), builder.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
// CB TODO - should be bufferized and asynchronously written in synchronous chunks
|
// CB TODO - should be bufferized and asynchronously written in synchronous chunks
|
||||||
|
@@ -25,7 +25,7 @@ fun Logger.logRequest(req: HttpServletRequest, logHeaders: Boolean = false) {
|
|||||||
}
|
}
|
||||||
// builder.append(' ').append(req.getProtocol());
|
// builder.append(' ').append(req.getProtocol());
|
||||||
info(blue("<< {}"), builder.toString())
|
info(blue("<< {}"), builder.toString())
|
||||||
if (logHeaders) {
|
if (isTraceEnabled && logHeaders) {
|
||||||
// CB TODO - should be bufferized and asynchronously written in synchronous chunks
|
// 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 ;
|
// so that header lines from parallel requests are not mixed up in the logs ;
|
||||||
// synchronizing the whole request log is not desirable
|
// synchronizing the whole request log is not desirable
|
||||||
@@ -33,7 +33,7 @@ fun Logger.logRequest(req: HttpServletRequest, logHeaders: Boolean = false) {
|
|||||||
while (headerNames.hasMoreElements()) {
|
while (headerNames.hasMoreElements()) {
|
||||||
val name = headerNames.nextElement()
|
val name = headerNames.nextElement()
|
||||||
val value = req.getHeader(name)
|
val value = req.getHeader(name)
|
||||||
info(blue("<< {}: {}"), name, value)
|
trace(blue("<< {}: {}"), name, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@ import kotlin.test.assertTrue
|
|||||||
|
|
||||||
@TestMethodOrder(Alphanumeric::class)
|
@TestMethodOrder(Alphanumeric::class)
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class BasicTests() {
|
class BasicTests: TestBase() {
|
||||||
|
|
||||||
val aTournament = Json.Object(
|
val aTournament = Json.Object(
|
||||||
"type" to "INDIVIDUAL",
|
"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
|
@Test
|
||||||
fun `001 create tournament`() {
|
fun `001 create tournament`() {
|
||||||
val resp = TestAPI.post("/api/tour", aTournament)
|
val resp = TestAPI.post("/api/tour", aTournament) as Json.Object
|
||||||
assertTrue(resp.isObject, "Json object expected")
|
assertTrue(resp.getBoolean("success") == true, "expecting success")
|
||||||
assertTrue(resp.asObject().getBoolean("success") == true, "expecting success")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `002 get tournament`() {
|
fun `002 get tournament`() {
|
||||||
val resp = TestAPI.get("/api/tour/1")
|
val resp = TestAPI.get("/api/tour/1") as Json.Object
|
||||||
assertTrue(resp.isObject, "Json object expected")
|
assertEquals(1, resp.getInt("id"), "First tournament should have id #1")
|
||||||
assertEquals(1, resp.asObject().getInt("id"), "First tournament should have id #1")
|
|
||||||
// filter out "id", and also "komi", "rules" and "gobanSize" which were provided by default
|
// 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")
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
25
webapp/src/test/kotlin/TestBase.kt
Normal file
25
webapp/src/test/kotlin/TestBase.kt
Normal 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 =====")
|
||||||
|
}
|
||||||
|
}
|
@@ -40,9 +40,9 @@ object TestAPI {
|
|||||||
on { setAttribute(eq(ApiHandler.SELECTOR_KEY), selector.capture()) } doAnswer {}
|
on { setAttribute(eq(ApiHandler.SELECTOR_KEY), selector.capture()) } doAnswer {}
|
||||||
on { setAttribute(eq(ApiHandler.SUBSELECTOR_KEY), subSelector.capture()) } doAnswer {}
|
on { setAttribute(eq(ApiHandler.SUBSELECTOR_KEY), subSelector.capture()) } doAnswer {}
|
||||||
on { setAttribute(eq(ApiHandler.PAYLOAD_KEY), reqPayload.capture()) } doAnswer {}
|
on { setAttribute(eq(ApiHandler.PAYLOAD_KEY), reqPayload.capture()) } doAnswer {}
|
||||||
on { getAttribute(ApiHandler.SELECTOR_KEY) } doAnswer { selector.lastValue }
|
on { getAttribute(ApiHandler.SELECTOR_KEY) } doAnswer { selector.allValues.lastOrNull() }
|
||||||
on { getAttribute(ApiHandler.SUBSELECTOR_KEY) } doAnswer { subSelector.lastValue }
|
on { getAttribute(ApiHandler.SUBSELECTOR_KEY) } doAnswer { subSelector.allValues.lastOrNull() }
|
||||||
on { getAttribute(ApiHandler.PAYLOAD_KEY) } doAnswer { reqPayload.lastValue }
|
on { getAttribute(ApiHandler.PAYLOAD_KEY) } doAnswer { reqPayload.allValues.lastOrNull() }
|
||||||
on { reader } doReturn myReader
|
on { reader } doReturn myReader
|
||||||
on { scheme } doReturn "http"
|
on { scheme } doReturn "http"
|
||||||
on { localName } doReturn "pairgoth"
|
on { localName } doReturn "pairgoth"
|
||||||
|
Reference in New Issue
Block a user