Compare commits

...

6 commits
v0.1.5 ... dev

Author SHA1 Message Date
Augusto Dwenger dfcd3cd35a Merge branch '7-feature-ranking-command' into 'dev'
Resolve "[FEATURE] "ranking" command"

Closes #7

See merge request hamburghammer/playtime!14
2020-02-08 22:00:37 +00:00
Augusto Dwenger ad2afd85d5 Resolve "[FEATURE] "ranking" command" 2020-02-08 22:00:37 +00:00
Augusto Dwenger e8fff7b200 implement coverage report with jacoco 2020-02-08 21:54:28 +00:00
Augusto Dwenger 0895514469 Implements the getTopPlayer method to get all player sorted by there playtime 2020-02-06 19:49:15 +00:00
Augusto Dwenger f73fcb8bba release candidate v0.2.0 2020-01-12 18:22:25 +01:00
Augusto Dwenger 789f4981b6 Merging 4-feature-view-the-playtime-of-another-player in to dev with redesigned PlayTime api and File db 2020-01-12 16:12:31 +00:00
32 changed files with 1109 additions and 490 deletions

View file

@ -32,10 +32,16 @@ unit-test:
artifacts:
expire_in: 3 days
paths:
- build/reports/tests/test/index.html
- build/reports/tests/test/
reports:
junit: build/test-results/test/TEST-*.xml
coverage:
stage: analyse
script:
- "./gradlew $GRADLE_OPTS clean jacocoTestReport"
- "cat build/reports/jacoco/test/html/index.html"
lint:
stage: analyse
script:

View file

@ -1,4 +1,6 @@
# PlayTime
[![pipeline status](https://gitlab.com/hamburghammer/playtime/badges/dev/pipeline.svg)](https://gitlab.com/hamburghammer/playtime/-/commits/dev)
[![coverage report](https://gitlab.com/hamburghammer/playtime/badges/dev/coverage.svg)](https://gitlab.com/hamburghammer/playtime/-/commits/dev)
It's a plugin for a [Spigot](https://www.spigotmc.org) Minecraft server to log the time players spend on the server and returns it to them.
@ -17,9 +19,14 @@ It's a plugin for a [Spigot](https://www.spigotmc.org) Minecraft server to log t
`playtime` shows the total play time on the server (Format DD:HH:MM)
`playtimeof <player>` shows the total play time of the player (Format DD:HH:MM)
`toptime` shows the top 5 player with the highest total playtime
## Development
The plugin gets build with gradle. It is important to run the shadowJar task to include all dependencies.
It's entirely written in Kotlin and for testing it uses [Mockk](https://mockk.io/) and [Junit5](https://junit.org/junit5/).
This plugin uses the [linter from Pinterest for Kotlin](https://www.kotlinresources.com/library/ktlint/)

View file

@ -3,10 +3,11 @@ plugins {
id("org.jlleitschuh.gradle.ktlint") version "9.0.0"
id("com.github.johnrengelman.shadow") version "5.2.0"
id("org.jetbrains.dokka") version "0.10.0"
jacoco
}
group = "de.augustodwenger"
version = "0.1.5-SNAPSHOT"
version = "0.2.0-SNAPSHOT"
repositories {
mavenCentral()
@ -45,11 +46,23 @@ tasks {
test {
useJUnitPlatform()
testLogging {
events("passed", "skipped", "failed")
events("skipped", "failed")
// events("passed", "skipped", "failed")
}
extensions.configure(JacocoTaskExtension::class) {
classDumpDir = file("$buildDir/jacoco/classpathdumps")
}
}
dokka {
outputFormat = "html"
outputDirectory = "$buildDir/dokka"
}
jacocoTestReport {
reports {
xml.isEnabled = false
csv.isEnabled = false
html.isEnabled = true
}
dependsOn("test")
}
}

View file

@ -1,7 +1,8 @@
import commands.PlayTime
import commands.PlayTimeOfCommand
import commands.TestCommand
import commands.TopTime
import commands.UpTime
import db.FilePlayerTimeDB
import listener.JoinListener
import listener.QuitListener
import org.bukkit.plugin.java.JavaPlugin
@ -11,12 +12,12 @@ import tasks.PersistState
/**
* Entrypoint of the plugin
*
* @property playerService A singleton of the playerService with FilePlayerTimeDB
* @property playerTime A singleton of the playerService with FilePlayerTimeDB
* @property scheduler the scheduler to manage schedules
*/
class Main : JavaPlugin() {
private val playerService = PlayerService(FilePlayerTimeDB())
private val playerTime: PlayerTime = PlayerTimeService
private val scheduler: BukkitScheduler = server.scheduler
/**
@ -28,13 +29,15 @@ class Main : JavaPlugin() {
super.onEnable()
this.getCommand("test")?.setExecutor(TestCommand())
this.getCommand("playtime")?.setExecutor(PlayTime(playerService))
this.getCommand("playtime")?.setExecutor(PlayTime())
this.getCommand("uptime")?.setExecutor(UpTime())
this.getCommand("playtimeof")?.setExecutor(PlayTimeOfCommand())
this.getCommand("toptime")?.setExecutor(TopTime())
server.pluginManager.registerEvents(JoinListener(playerService), this)
server.pluginManager.registerEvents(QuitListener(playerService), this)
server.pluginManager.registerEvents(JoinListener(), this)
server.pluginManager.registerEvents(QuitListener(), this)
scheduler.scheduleSyncRepeatingTask(this, PersistState(playerService), 0, minutesToTicks(10))
scheduler.scheduleSyncRepeatingTask(this, PersistState(), 0, minutesToTicks(10))
}
/**
@ -43,7 +46,7 @@ class Main : JavaPlugin() {
*/
override fun onDisable() {
super.onDisable()
playerService.persistAllPlayer()
PersistState().run()
}
private fun secondsToTicks(seconds: Int): Long {

View file

@ -1,116 +0,0 @@
import db.PlayerTimeDB
import java.time.Duration
import java.time.LocalDateTime
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap
import models.Player
/**
* A service to hold the all player that are currently playing
* It should only be used as Singleton @see Kotlin objects
*
* @property db the DB implementation which should be used to persist the state
* @property playerMap Holds all online players
*/
open class PlayerService(private val db: PlayerTimeDB) {
private val playerMap: ConcurrentMap<UUID, Player> = ConcurrentHashMap()
/**
* Adds the player to the playerMap.
* It creates an new player if it's no persisted and adds/refreshes the join time
*
* @param uuid the actual uuid of a player
* @param playerName for debugging because with the new api playerNames are not longer unique
*/
fun addPlayer(uuid: UUID, playerName: String) {
val player: Player = if (db.findByID(uuid)) {
val player = db.readPlayer(uuid)
player.joinTime = LocalDateTime.now()
player
} else {
val player = Player(uuid, playerName)
db.createPlayer(player)
player
}
playerMap[uuid] = player
}
/**
* Removes a player from the map and persists its status
*
*@param uuid players uuid
*/
fun removePlayer(uuid: UUID) {
persistPlayer(uuid)
playerMap.remove(uuid)
}
/**
* Wrapper for the map to prevent directly exposition
*
* @return status of the inner map
*/
fun containsPlayer(uuid: UUID): Boolean {
return playerMap.containsKey(uuid)
}
/**
* Wrapper for the map to prevent directly exposition
*
* @return status of the inner map
*/
fun isEmpty(): Boolean {
return playerMap.isEmpty()
}
/**
* Updates and writes the current status to the db
*
* @param uuid player uuid
*/
fun persistPlayer(uuid: UUID) {
val duration = timePlayed(uuid)
val player = getPlayer(uuid)
player.playTime = Duration.from(duration)
player.lastSave = LocalDateTime.now()
db.writePlayer(player)
}
/**
* Persists all player
*/
fun persistAllPlayer() {
for ((uuid, _) in playerMap) {
persistPlayer(uuid)
}
}
/**
* Returns the player with that uuid
*
* @param uuid player uuid
*/
fun getPlayer(uuid: UUID): Player {
return playerMap[uuid]!!
}
/**
* Returns the playtime including the old time
* if the stats got save it should use the save time instead of the join time
*
* @param uuid player uuid
*
* @return the total playtime
*/
fun timePlayed(uuid: UUID): Duration {
val player = getPlayer(uuid)
val playTime: Duration = if ((player.lastSave != null) && Duration.between(player.lastSave, player.joinTime).isNegative) {
Duration.between(player.lastSave, LocalDateTime.now())
} else {
Duration.between(player.joinTime, LocalDateTime.now())
}
return Duration.from(player.playTime).plus(playTime)
}
}

View file

@ -0,0 +1,54 @@
import java.time.Duration
import java.util.UUID
import models.Player
interface PlayerTime {
/**
* Adds the player to the playerMap.
* It creates an new player if it's no persisted and adds/refreshes the join time
*
* @param uuid the actual uuid of a player
* @param playerName for updating or to create a new player
*/
fun playerJoin(uuid: UUID, playerName: String)
/**
* Updates the playtime and saves it
*
* @param uuid UUID
*/
fun updatePlayTime(uuid: UUID)
/**
* Returns the playtime including the old time
* if the stats got save it should use the save time instead of the join time
*
* @param uuid player uuid
*
* @return the total playtime
*/
fun timePlayed(uuid: UUID): Duration
/**
* Search for the players with the highest playtime
*
* @return List<Player>
*/
fun getTopPlayers(): List<Player>
/**
* Formats the duration to a simple String
*
* @param duration Duration
* @return String (DD:HH:MM)
*/
fun timeToString(duration: Duration): String {
val days: String =
if (duration.toDaysPart() < 10) """0${duration.toDaysPart()}""" else """${duration.toDaysPart()}"""
val hours: String =
if (duration.toHoursPart() < 10) """0${duration.toHoursPart()}""" else """${duration.toHoursPart()}"""
val minutes: String =
if (duration.toMinutesPart() < 10) """0${duration.toMinutesPart()}""" else """${duration.toMinutesPart()}"""
return """$days:$hours:$minutes"""
}
}

View file

@ -0,0 +1,3 @@
import db.FilePlayerTimeDB
object PlayerTimeService : PlayerTime by SimplePlayerTime(FilePlayerTimeDB())

View file

@ -0,0 +1,85 @@
import db.PlayerTimeDB
import java.time.Duration
import java.time.LocalDateTime
import java.util.UUID
import models.Player
/**
* A service to hold the all player that are currently playing
* It should only be used as Singleton @see Kotlin objects
*
* @property db the DB implementation which should be used to persist the state
*/
class SimplePlayerTime(private val db: PlayerTimeDB) : PlayerTime {
/**
* Adds the player to the [db].
* It creates an new player if it's no persisted and adds/refreshes the join time
*
* @param uuid the actual uuid of a player
* @param playerName for updating or to create a new player
*/
override fun playerJoin(uuid: UUID, playerName: String) {
val player: Player = if (db.existsById(uuid)) {
val dbPlayer = db.findById(uuid)
dbPlayer.joinTime = LocalDateTime.now()
dbPlayer.playerName = playerName
dbPlayer
} else {
val player = Player(uuid, playerName)
db.create(player)
player
}
db.save(player)
}
/**
* Updates the playtime and saves it from the player with the [uuid]
*
* @param uuid UUID
*/
override fun updatePlayTime(uuid: UUID) {
val player = db.findById(uuid)
player.playTime = calculatePlaytime(player)
db.save(player)
}
/**
* Returns the playtime of the player with the the [uuid]
*
* @param uuid player uuid
*
* @return the total playtime
*/
override fun timePlayed(uuid: UUID): Duration {
val player = db.findById(uuid)
return calculatePlaytime(player)
}
/**
* Search for the players with the highest playtime
*
* @return List<Player> in a descending order by playtime
*/
override fun getTopPlayers(): List<Player> {
val playerList: List<Player> = db.findAll().onEach { player -> player.playTime = calculatePlaytime(player) }
return playerList.sortedByDescending { player -> player.playTime }
}
/**
* Calculates the time based on the join and on the last save time of the player
* if the playtime got saved it should use the saved time instead of the join time
*
* @param player Player
* @return Duration the playtime of the [player]
*/
private fun calculatePlaytime(player: Player): Duration {
val playTime: Duration =
if ((player.lastSave != null) && Duration.between(player.lastSave, player.joinTime).isNegative) {
Duration.between(player.lastSave, LocalDateTime.now())
} else {
Duration.between(player.joinTime, LocalDateTime.now())
}
return Duration.from(player.playTime).plus(playTime)
}
}

View file

@ -1,6 +1,7 @@
package commands
import PlayerService
import PlayerTime
import PlayerTimeService
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
@ -9,9 +10,9 @@ import org.bukkit.entity.Player
/**
* The command executor for the playtime
*
* @property playerService the service which holds the current status
* @property playerTime the service which holds the current status
*/
class PlayTime(private val playerService: PlayerService) : CommandExecutor {
class PlayTime(private val playerTime: PlayerTime = PlayerTimeService) : CommandExecutor {
/**
* It sends the playtime of and to the CommandSender
@ -26,16 +27,11 @@ class PlayTime(private val playerService: PlayerService) : CommandExecutor {
sender.sendMessage("This command can only be executed as a player")
false
} else {
val duration = playerService.timePlayed(sender.uniqueId)
val duration = playerTime.timePlayed(sender.uniqueId)
val days: String =
if (duration.toDaysPart() < 10) """0${duration.toDaysPart()}""" else """${duration.toDaysPart()}"""
val hours: String =
if (duration.toHoursPart() < 10) """0${duration.toHoursPart()}""" else """${duration.toHoursPart()}"""
val minutes: String =
if (duration.toMinutesPart() < 10) """0${duration.toMinutesPart()}""" else """${duration.toMinutesPart()}"""
val playTime = playerTime.timeToString(duration)
sender.sendMessage("""Your play time: $days:$hours:$minutes""")
sender.sendMessage("""Your play time: $playTime""")
true
}
}

View file

@ -0,0 +1,39 @@
package commands
import PlayerTime
import PlayerTimeService
import db.FilePlayerTimeDB
import db.PlayerFinder
import db.PlayerNotFoundException
import org.bukkit.Bukkit
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
class PlayTimeOfCommand(private val playerTime: PlayerTime = PlayerTimeService, private val playerTimeDB: PlayerFinder = FilePlayerTimeDB()) :
CommandExecutor {
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<String>): Boolean {
if (args.size != 1) {
return false
} else {
val playerName = args.first()
val player = try {
playerTimeDB.findByName(playerName)
} catch (e: PlayerNotFoundException) {
sender.sendMessage("""There is no player with the name $playerName""")
return false
}
val playerIsOnline = Bukkit.getPlayer(player.uuid)?.isOnline ?: false
val time = if (playerIsOnline) {
playerTime.timeToString(playerTime.timePlayed(player.uuid))
} else {
playerTime.timeToString(player.playTime)
}
sender.sendMessage("""The play time from ${player.playerName} is $time""")
return true
}
}
}

View file

@ -0,0 +1,28 @@
package commands
import PlayerTime
import PlayerTimeService
import java.util.stream.Collectors
import org.bukkit.command.Command
import org.bukkit.command.CommandExecutor
import org.bukkit.command.CommandSender
/**
* Command to get the top players with the most playtime
*
* @property playerTime PlayerTime
* @property playerListSize Long default = 5
* @constructor
*/
class TopTime(private val playerTime: PlayerTime = PlayerTimeService, private val playerListSize: Long = 5) : CommandExecutor {
override fun onCommand(sender: CommandSender, command: Command, label: String, args: Array<out String>): Boolean {
val playerTopList = playerTime.getTopPlayers().stream().limit(playerListSize).collect(Collectors.toList()).toList()
val sb = StringBuilder().appendln("Top list")
playerTopList.forEach { sb.appendln("${it.playerName}: ${playerTime.timeToString(it.playTime)}") }
sender.sendMessage(sb.toString())
return true
}
}

View file

@ -1,9 +1,12 @@
package db
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import java.io.File
import java.time.LocalDateTime
import java.util.UUID
import models.Player
import mu.KotlinLogging
/**
* FileDB manages the file based PlayerTime DB and provides basic functionality to interact with it
@ -13,6 +16,8 @@ import models.Player
*/
class FilePlayerTimeDB(private val path: File = File("./plugins/PlayTime/")) : PlayerTimeDB {
val logger = KotlinLogging.logger {}
private val gson = Gson()
/**
@ -29,20 +34,39 @@ class FilePlayerTimeDB(private val path: File = File("./plugins/PlayTime/")) : P
*
* @return if the file is present
*/
override fun findByID(uuid: UUID): Boolean {
override fun existsById(uuid: UUID): Boolean {
return File(path, uuid.toString()).exists()
}
/**
* It should look if a player exists by iterating over all saved players
*
* @param name the id of the player
*
* @return if the player is present
*/
override fun existsByName(name: String): Boolean {
val fileList = path.listFiles() ?: return false
if (fileList.isEmpty()) return false
for (file in fileList) {
val player = gson.fromJson<Player>(file.readText(), Player::class.java)
if (player.playerName == name) {
return true
}
}
return false
}
/**
* Creates a player file
*
* @param player the player that should be saved to the new file
*
*/
override fun createPlayer(player: Player) {
override fun create(player: Player) {
val file = File(path, player.uuid.toString())
if (!file.exists()) file.createNewFile()
writePlayer(player)
save(player)
}
/**
@ -51,20 +75,62 @@ class FilePlayerTimeDB(private val path: File = File("./plugins/PlayTime/")) : P
* @param uuid the id of the player that should be read
*
* @return Player
*
* @throws PlayerNotFoundException
*/
override fun readPlayer(uuid: UUID): Player {
override fun findById(uuid: UUID): Player {
val file = File(path, uuid.toString())
if (!file.exists()) throw PlayerNotFoundException("""The player with the id $uuid was not found""")
return gson.fromJson(file.readText(), Player::class.java)
}
/**
* Reads the player out of a file by finding the right one
*
* @param name the name of the player that should be read
*
* @return Player? if present
*/
override fun findByName(name: String): Player {
val fileList =
path.listFiles() ?: throw PlayerNotFoundException("""The player with the name $name was not found""")
if (fileList.isEmpty()) throw PlayerNotFoundException("""The player with the name $name was not found""")
for (file in fileList) {
val player = gson.fromJson<Player>(file.readText(), Player::class.java)
if (player.playerName == name) {
return player
}
}
throw PlayerNotFoundException("""The player with the name $name was not found""")
}
/**
* Should read all player and return theme
*
* @return List<Player>
*/
override fun findAll(): List<Player> {
val playerList = mutableListOf<Player>()
val fileList = path.listFiles() ?: return emptyList()
fileList.forEach { file: File ->
try {
playerList.add(gson.fromJson(file.readText(), Player::class.java))
} catch (e: JsonSyntaxException) {
logger.error { "The player with the ID ${{ file.name }} could not be read out of the files!" }
}
}
return playerList.toList()
}
/**
* Writs a player into an existing file
* To create a new player file @see createPlayer(player: Player)
*
* @param player that should be written
*/
override fun writePlayer(player: Player) {
override fun save(player: Player) {
val file = File(path, player.uuid.toString())
player.lastSave = LocalDateTime.now()
file.writeText(gson.toJson(player))
}
}

View file

@ -0,0 +1,55 @@
package db
import java.util.UUID
import models.Player
interface PlayerFinder {
/**
* It should look if a player exists
*
* @param uuid the id of the player
*
* @return if the player is present
*/
fun existsById(uuid: UUID): Boolean
/**
* It should look if a player exists
* If uuid of the player available use @see db.PlayerTimeDB.findByID due to the option to change the username
*
* @param name the name of the player
*
* @return if the player is present
*/
fun existsByName(name: String): Boolean
/**
* Should read the player
*
* @param uuid the uuid of the player that should be read
*
* @return the actual player
*
* @throws PlayerNotFoundException
*/
fun findById(uuid: UUID): Player
/**
* Should read the player
*
* @param name the name of the player that should be read
*
* @return the actual player
*
* @throws PlayerNotFoundException
*/
fun findByName(name: String): Player
/**
* Should read all player and return theme
*
* @return List<Player>
*/
fun findAll(): List<Player>
}

View file

@ -0,0 +1,5 @@
package db
import java.lang.Exception
class PlayerNotFoundException(message: String) : Exception(message)

View file

@ -1,40 +1,21 @@
package db
import java.util.UUID
import models.Player
interface PlayerTimeDB {
/**
* It should look if a player exists
*
* @param uuid the id of the player
*
* @return if the file is present
*/
fun findByID(uuid: UUID): Boolean
interface PlayerTimeDB : PlayerFinder {
/**
* Should create a player record for persisting
*
* @param player the player that should be save
*/
fun createPlayer(player: Player)
/**
* Should read the player
*
* @param uuid the uuid of the player that should be read
*
* @return the actual player
*/
fun readPlayer(uuid: UUID): Player
fun create(player: Player)
/**
* Should write a player into persistence
* To create a new player file @see createPlayer(player: Player)
* To create a new player file @see db.PlayerTimeDB.createPlayer
*
* @param player that should be written
*/
fun writePlayer(player: Player)
fun save(player: Player)
}

View file

@ -1,6 +1,7 @@
package listener
import PlayerService
import PlayerTime
import PlayerTimeService
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerJoinEvent
@ -8,9 +9,9 @@ import org.bukkit.event.player.PlayerJoinEvent
/**
* The listener for the PlayerJoinEvent
*
* @property playerService the playService to add player to it
* @property playerTimeService the playService to add player to it
*/
class JoinListener(private val playerService: PlayerService) : Listener {
class JoinListener(private val playerTimeService: PlayerTime = PlayerTimeService) : Listener {
/**
* Registers as handler for the PlayerJoinEvent
@ -21,6 +22,6 @@ class JoinListener(private val playerService: PlayerService) : Listener {
@EventHandler
fun onPlayerJoin(event: PlayerJoinEvent) {
val player = event.player
playerService.addPlayer(player.uniqueId, player.name)
playerTimeService.playerJoin(player.uniqueId, player.name)
}
}

View file

@ -1,6 +1,7 @@
package listener
import PlayerService
import PlayerTime
import PlayerTimeService
import org.bukkit.event.EventHandler
import org.bukkit.event.Listener
import org.bukkit.event.player.PlayerQuitEvent
@ -8,9 +9,9 @@ import org.bukkit.event.player.PlayerQuitEvent
/**
* The listener for the PlayerQuitEvent
*
* @property playerService the playService to remove player from it
* @property playerTimeService the playService to remove player from it
*/
class QuitListener(private val playerService: PlayerService) : Listener {
class QuitListener(private val playerTimeService: PlayerTime = PlayerTimeService) : Listener {
/**
* Registers as handler for the PlayerQuitEvent
@ -21,6 +22,6 @@ class QuitListener(private val playerService: PlayerService) : Listener {
@EventHandler
fun onPlayerQuit(event: PlayerQuitEvent) {
val player = event.player
playerService.removePlayer(player.uniqueId)
playerTimeService.updatePlayTime(player.uniqueId)
}
}

View file

@ -15,7 +15,7 @@ import java.util.UUID
*/
data class Player(
val uuid: UUID,
val playerName: String,
var playerName: String,
var playTime: Duration = Duration.ZERO,
var joinTime: LocalDateTime = LocalDateTime.now(),
var lastSave: LocalDateTime? = null

View file

@ -1,10 +1,12 @@
package tasks
import PlayerService
import PlayerTime
import PlayerTimeService
import org.bukkit.Bukkit
class PersistState(private val playerService: PlayerService) : Runnable {
class PersistState(private val playerTime: PlayerTime = PlayerTimeService) : Runnable {
override fun run() {
playerService.persistAllPlayer()
Bukkit.getOnlinePlayers().forEach { player -> playerTime.updatePlayTime(player.uniqueId) }
}
}

View file

@ -1,6 +1,6 @@
name: "PlayTime"
author: "hamburghammer"
version: "0.1.5"
version: "0.2.0"
description: "allows player to see how much they have played for."
main: Main
@ -10,8 +10,13 @@ commands:
description: "Test command for foo"
playtime:
usage: /<command>
# aliases: [timeplayed, pt]
description: "Shows the playtime of a player!"
uptime:
usage: /<command>
description: "Shows the up time of the server"
description: "Shows the up time of the server"
playtimeof:
usage: /<command> <player>
description: "Shows the playtime of the player"
toptime:
usage: /<command>
description: "Shows top players with the highest playtime"

View file

@ -0,0 +1,23 @@
import java.time.Duration
import java.util.UUID
import models.Player
class FakePlayerTime : PlayerTime {
override fun playerJoin(uuid: UUID, playerName: String) {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun updatePlayTime(uuid: UUID) {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun timePlayed(uuid: UUID): Duration {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
override fun getTopPlayers(): List<Player> {
TODO("not implemented") // To change body of created functions use File | Settings | File Templates.
}
}

View file

@ -0,0 +1,24 @@
import java.time.Duration
import kotlin.test.Test
import kotlin.test.assertEquals
class PlayTimeTest {
@Test
fun `right day formatting`() {
assertEquals("02:00:00", FakePlayerTime().timeToString(Duration.ofDays(2)))
assertEquals("20:00:00", FakePlayerTime().timeToString(Duration.ofDays(20)))
}
@Test
fun `right hour formatting`() {
assertEquals("00:02:00", FakePlayerTime().timeToString(Duration.ofHours(2)))
assertEquals("00:20:00", FakePlayerTime().timeToString(Duration.ofHours(20)))
}
@Test
fun `right minutes formatting`() {
assertEquals("00:00:02", FakePlayerTime().timeToString(Duration.ofMinutes(2)))
assertEquals("00:00:20", FakePlayerTime().timeToString(Duration.ofMinutes(20)))
}
}

View file

@ -1,270 +0,0 @@
import db.PlayerTimeDB
import io.mockk.Runs
import io.mockk.confirmVerified
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import java.time.Duration
import java.time.LocalDateTime
import java.util.UUID
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import models.Player
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
class PlayerServiceTest {
@Nested
inner class AddPlayerTest {
@Test
fun `should add new player`() {
val uuid = UUID.randomUUID()
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(uuid) } returns false
every { mockDB.createPlayer(any()) } just Runs
val playerService = PlayerService(mockDB)
assertTrue(playerService.isEmpty())
playerService.addPlayer(uuid, "playerName")
assertFalse(playerService.isEmpty())
assertTrue(playerService.containsPlayer(uuid))
verify(exactly = 1) { mockDB.findByID(uuid) }
verify(exactly = 1) { mockDB.createPlayer(any()) }
confirmVerified(mockDB)
}
@Test
fun `should add existing player`() {
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(any()) } returns player
val playerService = PlayerService(mockDB)
assertTrue(playerService.isEmpty())
playerService.addPlayer(uuid, "playerName")
assertFalse(playerService.isEmpty())
assertTrue(playerService.containsPlayer(uuid))
verify(exactly = 1) { mockDB.findByID(any()) }
verify(exactly = 1) { mockDB.readPlayer(any()) }
confirmVerified(mockDB)
}
@Test
fun `should change join time by existing player`() {
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
player.joinTime = LocalDateTime.now().minusDays(1)
val originalPlayer = player.copy()
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(any()) } returns player
val playerService = PlayerService(mockDB)
playerService.addPlayer(uuid, "playerName")
val extract = playerService.getPlayer(uuid)
assertNotEquals(originalPlayer.joinTime, extract.joinTime)
}
}
@Test
fun `remove a player from the map`() {
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(any()) } returns player
every { mockDB.writePlayer(any()) } just Runs
val playerService = PlayerService(mockDB)
playerService.addPlayer(uuid, "playerName")
assertFalse(playerService.isEmpty())
playerService.removePlayer(uuid)
assertTrue(playerService.isEmpty())
}
@Nested
inner class PersistPlayerTest {
@Test
fun `persist a player from the map to the db`() {
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(any()) } returns player
every { mockDB.writePlayer(any()) } just Runs
val playerService = PlayerService(mockDB)
playerService.addPlayer(uuid, "playerName")
playerService.persistPlayer(uuid)
verify(exactly = 1) { mockDB.writePlayer(any()) }
}
@Test
fun `persist a player from the map to the db with updated playtime`() {
val captureList = mutableListOf<Player>()
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(any()) } returns player
every { mockDB.writePlayer(capture(captureList)) } just Runs
val playerService = PlayerService(mockDB)
playerService.addPlayer(uuid, "playerName")
playerService.persistPlayer(uuid)
assertEquals(1, captureList.size)
assertNotEquals(Duration.ZERO, captureList.first().playTime)
verify(exactly = 1) { mockDB.writePlayer(any()) }
}
@Test
fun `persist all player from the map`() {
val uuid = UUID.randomUUID()
val uuid2 = UUID.randomUUID()
val player = Player(uuid, "playerName")
val player2 = Player(uuid, "playerName")
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(uuid) } returns player
every { mockDB.readPlayer(uuid2) } returns player2
every { mockDB.writePlayer(any()) } just Runs
val playerService = PlayerService(mockDB)
playerService.addPlayer(uuid, "playerName")
playerService.addPlayer(uuid2, "playerName")
assertFalse(playerService.isEmpty())
playerService.persistAllPlayer()
verify(exactly = 2) { mockDB.writePlayer(any()) }
}
}
@Nested
inner class GetPlayerTest {
@Test
fun `get player`() {
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(any()) } returns player
val playerService = PlayerService(mockDB)
playerService.addPlayer(uuid, "playerName")
assertFalse(playerService.isEmpty())
val extractedPlayer = playerService.getPlayer(uuid)
assertEquals(player.uuid, extractedPlayer.uuid)
assertEquals(player.playerName, extractedPlayer.playerName)
assertEquals(player.playTime, extractedPlayer.playTime)
}
}
@Nested
inner class TimePlayedTest {
@Test
fun `get played time and update the field`() {
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(any()) } returns player
val playerService = PlayerService(mockDB)
playerService.addPlayer(uuid, "playerName")
assertFalse(playerService.isEmpty())
val playTime = playerService.timePlayed(uuid)
assertNotEquals(Duration.ZERO, playTime)
assertFalse(playTime.isNegative)
assertEquals(Duration.ZERO, playerService.getPlayer(uuid).playTime)
}
@Test
fun `get played time after the time was saved`() {
val uuid = UUID.randomUUID()
val mockPlayer = Player(uuid, "playerName", lastSave = LocalDateTime.now().plusMinutes(10))
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(any()) } returns mockPlayer
val playerService = PlayerService(mockDB)
playerService.addPlayer(uuid, "playerName")
assertFalse(playerService.isEmpty())
val playTime = playerService.timePlayed(uuid)
assertTrue(playTime.toMinutes() <= -9, "the result is negative because it is a synthetic test")
println(playTime.toMinutes())
}
@Test
fun `get played time from the join when the save time earlier than the join time`() {
val uuid = UUID.randomUUID()
val mockPlayer = Player(uuid, "playerName", lastSave = LocalDateTime.now().minusMinutes(10))
val mockDB = mockk<PlayerTimeDB>()
every { mockDB.findByID(any()) } returns true
every { mockDB.readPlayer(any()) } returns mockPlayer
val playerService = PlayerService(mockDB)
playerService.addPlayer(uuid, "playerName")
assertFalse(playerService.isEmpty())
val playTime = playerService.timePlayed(uuid)
assertNotEquals(Duration.ZERO, playTime)
assertFalse(playTime.isNegative)
println(playTime.toMinutes())
}
}
}

View file

@ -0,0 +1,115 @@
package commands
import FakePlayerTime
import SimplePlayerTime
import db.PlayerNotFoundException
import db.PlayerTimeDB
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import java.time.Duration
import java.util.UUID
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import models.Player
import org.bukkit.Bukkit
import org.bukkit.command.CommandSender
import org.junit.jupiter.api.Assertions.assertFalse
internal class PlayTimeOfCommandTest {
@AfterTest
fun cleanUp() {
unmockkAll()
}
@Test
fun `the there is no arg`() {
val mockPlayerService = mockk<SimplePlayerTime>()
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
val playTimeOfCommand = PlayTimeOfCommand(mockPlayerService, mockPlayerTimeDB)
assertFalse(playTimeOfCommand.onCommand(mockk(), mockk(), "", arrayOf()))
}
@Test
fun `the player can not be read`() {
val nonExistingPlayerName = "foo"
val captureList = mutableListOf<String>()
val mockPlayerService = mockk<SimplePlayerTime>()
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.findByName(nonExistingPlayerName) } throws (PlayerNotFoundException(""))
val playTimeOfCommand = PlayTimeOfCommand(mockPlayerService, mockPlayerTimeDB)
val mockCommandSender = mockk<CommandSender>()
every { mockCommandSender.sendMessage(capture(captureList)) } just Runs
assertFalse(playTimeOfCommand.onCommand(mockCommandSender, mockk(), "", arrayOf(nonExistingPlayerName)))
assertEquals(1, captureList.size)
assertEquals("""There is no player with the name $nonExistingPlayerName""", captureList.first())
}
@Test
fun `player found online`() {
val captureList = mutableListOf<String>()
val playerName = "foo"
val uuid = UUID.randomUUID()
val player = Player(uuid, playerName)
val duration = Duration.ZERO
val timeToString = FakePlayerTime().timeToString(duration)
val mockPlayerService = mockk<SimplePlayerTime>()
every { mockPlayerService.timePlayed(uuid) } returns duration
every { mockPlayerService.timeToString(duration) } returns timeToString
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.findByName(playerName) } returns player
val playTimeOfCommand = PlayTimeOfCommand(mockPlayerService, mockPlayerTimeDB)
val mockCommandSender = mockk<CommandSender>()
every { mockCommandSender.sendMessage(capture(captureList)) } just Runs
mockkStatic(Bukkit::class)
every { Bukkit.getPlayer(uuid)!!.isOnline } returns true
assertTrue(playTimeOfCommand.onCommand(mockCommandSender, mockk(), "", arrayOf(playerName)))
assertEquals(1, captureList.size)
assertEquals("""The play time from $playerName is $timeToString""", captureList.first())
}
@Test
fun `player found not online`() {
val captureList = mutableListOf<String>()
val playerName = "foo"
val uuid = UUID.randomUUID()
val player = Player(uuid, playerName)
val duration = Duration.ZERO
val timeToString = FakePlayerTime().timeToString(duration)
val mockPlayerService = mockk<SimplePlayerTime>()
every { mockPlayerService.timeToString(duration) } returns timeToString
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.findByName(playerName) } returns player
val playTimeOfCommand = PlayTimeOfCommand(mockPlayerService, mockPlayerTimeDB)
val mockCommandSender = mockk<CommandSender>()
every { mockCommandSender.sendMessage(capture(captureList)) } just Runs
mockkStatic(Bukkit::class)
every { Bukkit.getPlayer(uuid)!!.isOnline } returns false
assertTrue(playTimeOfCommand.onCommand(mockCommandSender, mockk(), "", arrayOf(playerName)))
assertEquals(1, captureList.size)
assertEquals("""The play time from $playerName is $timeToString""", captureList.first())
}
}

View file

@ -1,6 +1,7 @@
package commands
import PlayerService
import FakePlayerTime
import SimplePlayerTime
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
@ -20,8 +21,10 @@ internal class PlayTimeTest {
fun `get the current play time`() {
val list = mutableListOf<String>()
val mockedPlayerService = mockk<PlayerService>()
every { mockedPlayerService.timePlayed(any()) } returns Duration.ofMinutes(10)
val mockedPlayerService = mockk<SimplePlayerTime>()
val duration = Duration.ofMinutes(10)
every { mockedPlayerService.timePlayed(any()) } returns duration
every { mockedPlayerService.timeToString(any()) } returns FakePlayerTime().timeToString(duration)
val playTime = PlayTime(mockedPlayerService)
@ -39,7 +42,7 @@ internal class PlayTimeTest {
fun `error if not send by player`() {
val list = mutableListOf<String>()
val mockedPlayerService = mockk<PlayerService>()
val mockedPlayerService = mockk<SimplePlayerTime>()
every { mockedPlayerService.timePlayed(any()) } returns Duration.ofMinutes(10)
val playTime = PlayTime(mockedPlayerService)

View file

@ -0,0 +1,105 @@
package commands
import SimplePlayerTime
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.unmockkAll
import java.time.Duration
import java.util.UUID
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import models.Player
import org.bukkit.command.CommandSender
import org.junit.jupiter.api.Assertions.assertNotEquals
import org.junit.jupiter.api.Assertions.assertTrue
class TopTimeTest {
@BeforeTest
fun setUp() {
unmockkAll()
}
@Test
fun `send string`() {
val captureList = mutableListOf<String>()
val uuid1 = UUID.randomUUID()
val uuid2 = UUID.randomUUID()
val uuid3 = UUID.randomUUID()
val player1 = Player(uuid1, "player1", playTime = Duration.ZERO)
val player2 = Player(uuid2, "player2", playTime = Duration.ofMinutes(10))
val player3 = Player(uuid3, "player3", playTime = Duration.ofHours(2))
val mockPlayTime = mockk<SimplePlayerTime>()
every { mockPlayTime.getTopPlayers() } returns listOf(player3, player2, player1)
every { mockPlayTime.timeToString(any()) } returns "00:00:00"
val topTime = TopTime(mockPlayTime)
val mockCommandSender = mockk<CommandSender>()
every { mockCommandSender.sendMessage(capture(captureList)) } just Runs
assertTrue(topTime.onCommand(mockCommandSender, mockk(), "", arrayOf()))
assertTrue(captureList.isNotEmpty())
assertNotEquals("", captureList.first())
}
@Test
fun `correct formatted string output`() {
val captureList = mutableListOf<String>()
val uuid1 = UUID.randomUUID()
val uuid2 = UUID.randomUUID()
val uuid3 = UUID.randomUUID()
val player1 = Player(uuid1, "player1", playTime = Duration.ZERO)
val player2 = Player(uuid2, "player2", playTime = Duration.ofMinutes(10))
val player3 = Player(uuid3, "player3", playTime = Duration.ofHours(2))
val mockPlayTime = mockk<SimplePlayerTime>()
every { mockPlayTime.getTopPlayers() } returns listOf(player3, player2, player1)
every { mockPlayTime.timeToString(any()) } returns "00:00:00"
val mockCommandSender = mockk<CommandSender>()
every { mockCommandSender.sendMessage(capture(captureList)) } just Runs
val topTime = TopTime(mockPlayTime)
assertTrue(topTime.onCommand(mockCommandSender, mockk(), "", arrayOf()))
assertEquals(
"""Top list
player3: 00:00:00
player2: 00:00:00
player1: 00:00:00
""", captureList.first().toString()
)
}
@Test
fun `show only top 6 player`() {
val playerListSize = 6
val defaultNewLines = 2
val captureList = mutableListOf<String>()
val playerList = mutableListOf<Player>()
for (i in 1..7) {
playerList.add(Player(UUID.randomUUID(), "$i", playTime = Duration.ZERO))
}
val mockPlayTime = mockk<SimplePlayerTime>()
every { mockPlayTime.getTopPlayers() } returns playerList.toList()
every { mockPlayTime.timeToString(any()) } returns "00:00:00"
val mockCommandSender = mockk<CommandSender>()
every { mockCommandSender.sendMessage(capture(captureList)) } just Runs
val topTime = TopTime(mockPlayTime, playerListSize.toLong())
assertTrue(topTime.onCommand(mockCommandSender, mockk(), "", arrayOf()))
assertEquals(playerListSize + defaultNewLines, captureList.first().split("\n").count())
}
}

View file

@ -7,11 +7,14 @@ import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNotNull
import models.Player
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Nested
internal class FilePlayerTimeDBTest {
class FilePlayerTimeDBTest {
private var dbDir = createTempDir()
@ -30,10 +33,10 @@ internal class FilePlayerTimeDBTest {
val uuid = UUID.randomUUID()
val fileDB = FilePlayerTimeDB(dbDir)
assertFalse(fileDB.findByID(uuid))
assertFalse(fileDB.existsById(uuid))
File(dbDir, uuid.toString()).createNewFile()
assertTrue(fileDB.findByID(uuid))
assertTrue(fileDB.existsById(uuid))
}
@Test
@ -42,25 +45,64 @@ internal class FilePlayerTimeDBTest {
val fileDB = FilePlayerTimeDB(dbDir)
val player = Player(uuid, "username")
assertFalse(fileDB.findByID(uuid))
assertFalse(fileDB.existsById(uuid))
fileDB.createPlayer(player)
fileDB.create(player)
assertTrue(fileDB.findByID(uuid))
assertTrue(fileDB.existsById(uuid))
val writtenFile = File(dbDir, uuid.toString())
assertEquals(player, Gson().fromJson(writtenFile.readText(), Player::class.java))
}
@Test
fun `read player from file`() {
val uuid = UUID.randomUUID()
val fileDB = FilePlayerTimeDB(dbDir)
val player = Player(uuid, "username")
@Nested
inner class ReadPlayer {
File(dbDir, uuid.toString()).writeText(Gson().toJson(player))
@Test
fun `read player from file`() {
val uuid = UUID.randomUUID()
val fileDB = FilePlayerTimeDB(dbDir)
val player = Player(uuid, "username")
assertEquals(player, fileDB.readPlayer(uuid))
File(dbDir, uuid.toString()).writeText(Gson().toJson(player))
assertEquals(player, fileDB.findById(uuid))
}
@Test
fun `read player from file through player name`() {
val playerName = "playerName"
val fileDB = FilePlayerTimeDB(dbDir)
val uuid = UUID.randomUUID()
val player = Player(uuid, playerName)
val file = File(dbDir, uuid.toString())
assertTrue(file.createNewFile())
file.writeText(Gson().toJson(player))
val readPlayer = fileDB.findByName(playerName)
assertNotNull(readPlayer)
assertEquals(player, readPlayer)
}
@Test
fun `read not existing player from file through player name`() {
val fileDB = FilePlayerTimeDB(dbDir)
val playerName = "foo"
val exception = assertFailsWith<PlayerNotFoundException> { fileDB.findByName(playerName) }
assertEquals("""The player with the name $playerName was not found""", exception.message)
}
@Test
fun `read not existing player from file through player id`() {
val fileDB = FilePlayerTimeDB(dbDir)
val uuid = UUID.randomUUID()
val exception = assertFailsWith<PlayerNotFoundException> { fileDB.findById(uuid) }
assertEquals("""The player with the id $uuid was not found""", exception.message)
}
}
@Test
@ -71,7 +113,83 @@ internal class FilePlayerTimeDBTest {
val file = File(dbDir, uuid.toString())
assertTrue(file.createNewFile())
fileDB.writePlayer(player)
fileDB.save(player)
assertEquals(player, Gson().fromJson(file.readText(), Player::class.java))
}
@Nested
inner class FindPlayerByName {
@Test
fun `find player over playerName`() {
val playerName = "playerName"
val fileDB = FilePlayerTimeDB(dbDir)
val uuid = UUID.randomUUID()
val player = Player(uuid, playerName)
val file = File(dbDir, uuid.toString())
assertTrue(file.createNewFile())
file.writeText(Gson().toJson(player))
assertTrue(fileDB.existsByName(playerName))
}
@Test
fun `return false if there is no file`() {
val playerName = "playerName"
val fileDB = FilePlayerTimeDB(dbDir)
assertFalse(fileDB.existsByName(playerName))
}
@Test
fun `return false if there is no file matching the name`() {
val playerName = "playerName"
val fileDB = FilePlayerTimeDB(dbDir)
val uuid = UUID.randomUUID()
val player = Player(uuid, playerName)
val file = File(dbDir, uuid.toString())
assertTrue(file.createNewFile())
file.writeText(Gson().toJson(player))
assertFalse(fileDB.existsByName("foo"))
}
}
@Test
fun `find all players`() {
val playerName = "playerName"
val fileDB = FilePlayerTimeDB(dbDir)
val uuid = UUID.randomUUID()
val uuid2 = UUID.randomUUID()
val player = Player(uuid, playerName)
val player2 = Player(uuid2, playerName)
File(dbDir, uuid.toString()).writeText(Gson().toJson(player))
File(dbDir, uuid2.toString()).writeText(Gson().toJson(player2))
val playerList = fileDB.findAll()
assertFalse(playerList.isEmpty())
assertEquals(2, playerList.size)
assertTrue(playerList.contains(player))
assertTrue(playerList.contains(player2))
}
@Test
fun `find all players if there is no one`() {
val fileDB = FilePlayerTimeDB(dbDir)
assertTrue(fileDB.findAll().isEmpty())
}
@Test
fun `error by deserialization`() {
val fileDB = FilePlayerTimeDB(dbDir)
val uuid = UUID.randomUUID()
File(dbDir, uuid.toString()).writeText("Foo")
assertTrue(fileDB.findAll().isEmpty())
}
}

View file

@ -0,0 +1,243 @@
package db
import SimplePlayerTime
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import java.time.Duration
import java.time.LocalDateTime
import java.util.UUID
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import models.Player
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertNotEquals
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Nested
class SimplePlayerTimeTest {
private var dbDir = createTempDir()
@BeforeTest
fun setUp() {
dbDir = createTempDir()
}
@AfterTest
fun cleanUp() {
dbDir.delete()
}
@Nested
inner class AddPlayerTest {
@Test
fun `should add new player`() {
val uuid = UUID.randomUUID()
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.existsById(uuid) } returns false
every { mockPlayerTimeDB.create(any()) } just Runs
every { mockPlayerTimeDB.save(any()) } just Runs
val playerService = SimplePlayerTime(mockPlayerTimeDB)
playerService.playerJoin(uuid, "playerName")
verify { mockPlayerTimeDB.save(any()) }
verify { mockPlayerTimeDB.create(any()) }
}
@Test
fun `should add existing player`() {
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.existsById(uuid) } returns true
every { mockPlayerTimeDB.findById(uuid) } returns player
every { mockPlayerTimeDB.save(any()) } just Runs
val playerService = SimplePlayerTime(mockPlayerTimeDB)
playerService.playerJoin(uuid, "playerName")
verify { mockPlayerTimeDB.save(any()) }
verify { mockPlayerTimeDB.findById(uuid) }
verify { mockPlayerTimeDB.existsById(uuid) }
}
@Test
fun `should add existing player and update the username`() {
val capturedPlayer = mutableListOf<Player>()
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
val updateName = "updateName"
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.existsById(uuid) } returns true
every { mockPlayerTimeDB.findById(any()) } returns player
every { mockPlayerTimeDB.save(capture(capturedPlayer)) } just Runs
val playerService = SimplePlayerTime(mockPlayerTimeDB)
playerService.playerJoin(uuid, updateName)
assertEquals(1, capturedPlayer.size)
assertEquals(updateName, capturedPlayer.first().playerName)
}
@Test
fun `should change join time by existing player`() {
val capturedPlayer = mutableListOf<Player>()
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
player.joinTime = LocalDateTime.now().minusDays(1)
val originalPlayer = player.copy()
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.existsById(uuid) } returns true
every { mockPlayerTimeDB.findById(uuid = any()) } returns player
every { mockPlayerTimeDB.save(capture(capturedPlayer)) } just Runs
val playerService = SimplePlayerTime(mockPlayerTimeDB)
playerService.playerJoin(uuid, "playerName")
assertEquals(1, capturedPlayer.size)
assertNotEquals(originalPlayer.joinTime, capturedPlayer.first().joinTime)
}
}
@Test
fun `update the play time and save`() {
val capturedPlayers = mutableListOf<Player>()
val uuid = UUID.randomUUID()
val player = Player(uuid, "", playTime = Duration.ZERO)
val mockDBPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockDBPlayerTimeDB.findById(uuid) } returns player
every { mockDBPlayerTimeDB.save(capture(capturedPlayers)) } just Runs
val simplePlayerTime = SimplePlayerTime(mockDBPlayerTimeDB)
simplePlayerTime.updatePlayTime(uuid)
assertEquals(1, capturedPlayers.size)
assertNotEquals(Duration.ZERO, capturedPlayers.first().playTime)
}
@Nested
inner class TimePlayedTest {
@Test
fun `get played time and update the field`() {
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName")
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.existsById(any()) } returns true
every { mockPlayerTimeDB.findById(uuid) } returns player
every { mockPlayerTimeDB.save(any()) } just Runs
val playerService = SimplePlayerTime(mockPlayerTimeDB)
playerService.playerJoin(uuid, "playerName")
// assertFalse(playerService.isEmpty())
val playTime = playerService.timePlayed(uuid)
assertNotEquals(Duration.ZERO, playTime)
assertFalse(playTime.isNegative)
// assertEquals(Duration.ZERO, playerService.getPlayer(uuid).playTime)
}
@Test
fun `get played time after the time was saved`() {
val uuid = UUID.randomUUID()
val player = Player(uuid, "playerName", lastSave = LocalDateTime.now().plusMinutes(10))
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.existsById(any()) } returns true
every { mockPlayerTimeDB.findById(uuid) } returns player
every { mockPlayerTimeDB.save(any()) } just Runs
val playerService = SimplePlayerTime(mockPlayerTimeDB)
playerService.playerJoin(uuid, "playerName")
val playTime = playerService.timePlayed(uuid)
assertTrue(
playTime.toMinutes() <= -9,
"the result is negative because it is a synthetic test. The playtime is " + playTime.toMinutes()
)
}
@Test
fun `get played time from the join when the save time earlier than the join time`() {
val uuid = UUID.randomUUID()
val mockPlayer = Player(uuid, "playerName", lastSave = LocalDateTime.now().minusMinutes(10))
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.existsById(any()) } returns true
every { mockPlayerTimeDB.findById(uuid) } returns mockPlayer
every { mockPlayerTimeDB.save(any()) } just Runs
val playerService = SimplePlayerTime(mockPlayerTimeDB)
playerService.playerJoin(uuid, "playerName")
// assertFalse(playerService.isEmpty())
val playTime = playerService.timePlayed(uuid)
assertNotEquals(Duration.ZERO, playTime)
assertFalse(playTime.isNegative)
println(playTime.toMinutes())
}
}
@Nested
inner class TopPlayer {
@Test
fun `get list with players`() {
val player1 = Player(UUID.randomUUID(), "player1", playTime = Duration.ZERO)
val player2 = Player(UUID.randomUUID(), "player2", playTime = Duration.ofMinutes(10))
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.findAll() } returns listOf(player1, player2)
val playerService = SimplePlayerTime(mockPlayerTimeDB)
val topPlayer = playerService.getTopPlayers()
assertFalse(topPlayer.isEmpty())
assertEquals(2, topPlayer.size)
}
@Test
fun `get list with players sorted by most time`() {
val player1 = Player(UUID.randomUUID(), "player1", playTime = Duration.ZERO)
val player2 = Player(UUID.randomUUID(), "player2", playTime = Duration.ofMinutes(10))
val player3 = Player(UUID.randomUUID(), "player3", playTime = Duration.ofHours(2))
val mockPlayerTimeDB = mockk<PlayerTimeDB>()
every { mockPlayerTimeDB.findAll() } returns listOf(player1, player2, player3)
val playerService = SimplePlayerTime(mockPlayerTimeDB)
val topPlayer = playerService.getTopPlayers()
assertEquals(player3, topPlayer[0])
assertEquals(player2, topPlayer[1])
assertEquals(player1, topPlayer[2])
}
}
}

View file

@ -1,8 +1,7 @@
package listener
import PlayerService
import PlayerTime
import io.mockk.Runs
import io.mockk.confirmVerified
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
@ -16,20 +15,21 @@ class JoinListenerTest {
@Test
fun `should call to add a player`() {
val uuid = UUID.randomUUID()
val mockPlayer = mockk<Player>()
every { mockPlayer.uniqueId } returns UUID.randomUUID()
every { mockPlayer.uniqueId } returns uuid
every { mockPlayer.name } returns ""
val mockPlayerJoinEvent = mockk<PlayerJoinEvent>()
every { mockPlayerJoinEvent.player } returns mockPlayer
val mockPlayerService = mockk<PlayerService>()
every { mockPlayerService.addPlayer(any(), any()) } just Runs
val mockPlayerDB = mockk<PlayerTime>()
every { mockPlayerDB.playerJoin(any(), any()) } just Runs
val joinListener = JoinListener(mockPlayerService)
val joinListener = JoinListener(mockPlayerDB)
joinListener.onPlayerJoin(mockPlayerJoinEvent)
verify(exactly = 1) { mockPlayerService.addPlayer(any(), any()) }
confirmVerified(mockPlayerService)
verify { mockPlayerDB.playerJoin(any(), any()) }
}
}

View file

@ -1,8 +1,7 @@
package listener
import PlayerService
import PlayerTime
import io.mockk.Runs
import io.mockk.confirmVerified
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
@ -16,20 +15,20 @@ class QuitListenerTest {
@Test
fun `should call to add a player`() {
val uuid = UUID.randomUUID()
val mockPlayer = mockk<Player>()
every { mockPlayer.uniqueId } returns UUID.randomUUID()
every { mockPlayer.uniqueId } returns uuid
every { mockPlayer.name } returns ""
val mockPlayerQuitEvent = mockk<PlayerQuitEvent>()
every { mockPlayerQuitEvent.player } returns mockPlayer
val mockPlayerService = mockk<PlayerService>()
every { mockPlayerService.removePlayer(any()) } just Runs
val mockPlayerDB = mockk<PlayerTime>()
every { mockPlayerDB.updatePlayTime(any()) } just Runs
val quitListener = QuitListener(mockPlayerService)
val quitListener = QuitListener(mockPlayerDB)
quitListener.onPlayerQuit(mockPlayerQuitEvent)
verify(exactly = 1) { mockPlayerService.removePlayer(any()) }
confirmVerified(mockPlayerService)
verify { mockPlayerDB.updatePlayTime(any()) }
}
}

View file

@ -1,25 +1,50 @@
package tasks
import PlayerService
import SimplePlayerTime
import io.mockk.Runs
import io.mockk.confirmVerified
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkAll
import io.mockk.verify
import java.util.UUID
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertEquals
import org.bukkit.Bukkit
import org.bukkit.entity.Player
class PersistStateTest {
@AfterTest
fun cleanUp() {
unmockkAll()
}
@Test
fun `should persist all players`() {
val mockPlayerService = mockk<PlayerService>()
every { mockPlayerService.persistAllPlayer() } just Runs
val captureList = mutableListOf<UUID>()
val uuids = listOf<UUID>(UUID.randomUUID(), UUID.randomUUID())
val player1 = mockk<Player>()
val player2 = mockk<Player>()
every { player1.uniqueId } returns uuids[0]
every { player2.uniqueId } returns uuids[1]
mockkStatic(Bukkit::class)
every { Bukkit.getOnlinePlayers() } returns listOf(player1, player2)
val mockPlayerService = mockk<SimplePlayerTime>()
every { mockPlayerService.updatePlayTime(capture(captureList)) } just Runs
val persistState = PersistState(mockPlayerService)
persistState.run()
verify(exactly = 1) { mockPlayerService.persistAllPlayer() }
assertEquals(2, captureList.size)
verify(exactly = 2) { mockPlayerService.updatePlayTime(any()) }
confirmVerified(mockPlayerService)
}
}

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
VERSION=0.1.5
VERSION=0.2.0
sh ./gradlew shadowJar