some tests

This commit is contained in:
Evgenii Alekseev 2019-10-24 08:20:03 +03:00
parent d1001ffb8e
commit b228595a1b
28 changed files with 551 additions and 47 deletions

View File

@ -1,28 +1,5 @@
name := "ffxivbis"
version := "0.9.0"
scalaVersion := "2.13.1"
scalacOptions ++= Seq("-deprecation", "-feature")
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2"
libraryDependencies += "com.typesafe.akka" %% "akka-http" % "10.1.10"
libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % "10.1.10"
libraryDependencies += "com.typesafe.akka" %% "akka-stream" % "2.5.23"
libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.0.4"
libraryDependencies += "javax.ws.rs" % "javax.ws.rs-api" % "2.1.1"
libraryDependencies += "io.spray" %% "spray-json" % "1.3.5"
libraryDependencies += "com.lihaoyi" %% "scalatags" % "0.7.0"
libraryDependencies += "com.typesafe.slick" %% "slick" % "3.3.2"
libraryDependencies += "com.typesafe.slick" %% "slick-hikaricp" % "3.3.2"
libraryDependencies += "org.flywaydb" % "flyway-core" % "6.0.6"
libraryDependencies += "org.xerial" % "sqlite-jdbc" % "3.28.0"
libraryDependencies += "org.postgresql" % "postgresql" % "9.3-1104-jdbc4"
libraryDependencies += "org.mindrot" % "jbcrypt" % "0.3m"

19
libraries.sbt Normal file
View File

@ -0,0 +1,19 @@
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.2"
libraryDependencies += "com.typesafe.akka" %% "akka-http" % "10.1.10"
libraryDependencies += "com.typesafe.akka" %% "akka-http-spray-json" % "10.1.10"
libraryDependencies += "com.typesafe.akka" %% "akka-stream" % "2.5.23"
libraryDependencies += "com.github.swagger-akka-http" %% "swagger-akka-http" % "2.0.4"
libraryDependencies += "javax.ws.rs" % "javax.ws.rs-api" % "2.1.1"
libraryDependencies += "io.spray" %% "spray-json" % "1.3.5"
libraryDependencies += "com.lihaoyi" %% "scalatags" % "0.7.0"
libraryDependencies += "com.typesafe.slick" %% "slick" % "3.3.2"
libraryDependencies += "com.typesafe.slick" %% "slick-hikaricp" % "3.3.2"
libraryDependencies += "org.flywaydb" % "flyway-core" % "6.0.6"
libraryDependencies += "org.xerial" % "sqlite-jdbc" % "3.28.0"
libraryDependencies += "org.postgresql" % "postgresql" % "9.3-1104-jdbc4"
libraryDependencies += "org.mindrot" % "jbcrypt" % "0.3m"

View File

@ -0,0 +1,36 @@
create table players (
party_id text not null,
player_id bigserial,
created bigint not null,
nick text not null,
job text not null,
bis_link text,
priority integer not null default 1);
create unique index players_nick_job_idx on players(party_id, nick, job);
create table loot (
loot_id bigserial,
player_id bigint not null,
created bigint not null,
piece text not null,
is_tome integer not null,
job text not null,
foreign key (player_id) references players(player_id) on delete cascade);
create index loot_owner_idx on loot(player_id);
create table bis (
player_id bigint not null,
created bigint not null,
piece text not null,
is_tome integer not null,
job text not null,
foreign key (player_id) references players(player_id) on delete cascade);
create unique index bis_piece_player_id_idx on bis(player_id, piece);
create table users (
party_id text not null,
user_id bigserial,
username text not null,
password text not null,
permission text not null);
create unique index users_username_idx on users(party_id, username);

View File

@ -1,6 +1,6 @@
create table players (
party_id text not null,
player_id integer primary key,
player_id integer primary key autoincrement,
created integer not null,
nick text not null,
job text not null,
@ -9,7 +9,7 @@ create table players (
create unique index players_nick_job_idx on players(party_id, nick, job);
create table loot (
loot_id integer primary key,
loot_id integer primary key autoincrement,
player_id integer not null,
created integer not null,
piece text not null,
@ -29,7 +29,7 @@ create unique index bis_piece_player_id_idx on bis(player_id, piece);
create table users (
party_id text not null,
user_id integer primary key,
user_id integer primary key autoincrement,
username text not null,
password text not null,
permission text not null);

View File

@ -0,0 +1,5 @@
<configuration>
<logger name="slick.jdbc.JdbcBackend.statement" level="DEBUG" />
</configuration>

View File

@ -22,6 +22,16 @@ me.arcanis.ffxivbis {
}
numThreads = 10
}
postgresql {
profile = "slick.jdbc.PostgresProfile$"
db {
url = "jdbc:postgresql://localhost/ffxivbis"
user = "user"
password = "password"
}
numThreads = 10
}
}
settings {

View File

@ -20,5 +20,5 @@ class AriyalaHelper(ariyala: ActorRef) {
def downloadBiS(link: String, job: Job.Job)
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[BiS] =
(ariyala ? Ariyala.GetBiS(link, job)).mapTo[Seq[Piece]].map(BiS(_))
(ariyala ? Ariyala.GetBiS(link, job)).mapTo[BiS]
}

View File

@ -36,7 +36,7 @@ class PlayerHelper(storage: ActorRef, ariyala: ActorRef) extends AriyalaHelper(a
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Seq[Player]] =
maybePlayerId match {
case Some(playerId) =>
(storage ? DatabasePartyHandler.GetPlayer(playerId)).mapTo[Player].map(Seq(_))
(storage ? DatabasePartyHandler.GetPlayer(playerId)).mapTo[Option[Player]].map(_.toSeq)
case None =>
(storage ? DatabasePartyHandler.GetParty(partyId)).mapTo[Party].map(_.players.values.toSeq)
}

View File

@ -20,7 +20,7 @@ class UserHelper(storage: ActorRef) {
def addUser(user: User, isHashedPassword: Boolean)
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Int] =
(storage ? DatabaseUserHandler.InsertUser(user, isHashedPassword)).mapTo[Int]
(storage ? DatabaseUserHandler.AddUser(user, isHashedPassword)).mapTo[Int]
def user(partyId: String, username: String)
(implicit executionContext: ExecutionContext, timeout: Timeout): Future[Option[User]] =

View File

@ -22,4 +22,5 @@ case class User(partyId: String,
def hash: String = BCrypt.hashpw(password, BCrypt.gensalt)
def verify(plain: String): Boolean = BCrypt.checkpw(plain, password)
def verityScope(scope: Permission.Value): Boolean = permission >= scope
def withHashedPassword: User = copy(password = hash)
}

View File

@ -18,7 +18,7 @@ import akka.stream.ActorMaterializer
import akka.stream.scaladsl.{Keep, Sink}
import akka.util.ByteString
import com.typesafe.scalalogging.StrictLogging
import me.arcanis.ffxivbis.models.{Job, Piece}
import me.arcanis.ffxivbis.models.{BiS, Job, Piece}
import spray.json._
import scala.concurrent.{ExecutionContext, Future}
@ -39,7 +39,7 @@ class Ariyala extends Actor with StrictLogging {
override def receive: Receive = {
case GetBiS(link, job) =>
val client = sender()
get(link, job).pipeTo(client)
get(link, job).map(BiS(_)).pipeTo(client)
}
private def get(link: String, job: Job.Job): Future[Seq[Piece]] = {

View File

@ -19,6 +19,11 @@ trait Database extends Actor with StrictLogging {
implicit def executionContext: ExecutionContext
def profile: DatabaseProfile
override def postStop(): Unit = {
profile.db.close()
super.postStop()
}
def filterParty(party: Party, maybePlayerId: Option[PlayerId]): Seq[Player] =
(party, maybePlayerId) match {
case (_, Some(playerId)) => party.player(playerId).map(Seq(_)).getOrElse(Seq.empty)

View File

@ -12,6 +12,8 @@ import akka.pattern.pipe
import me.arcanis.ffxivbis.models.{BiS, Player, PlayerId}
import me.arcanis.ffxivbis.service.Database
import scala.concurrent.Future
trait DatabasePartyHandler { this: Database =>
import DatabasePartyHandler._
@ -26,11 +28,16 @@ trait DatabasePartyHandler { this: Database =>
case GetPlayer(playerId) =>
val client = sender()
val player = for {
bis <- profile.getPiecesBiS(playerId)
loot <- profile.getPieces(playerId)
} yield Player(playerId.partyId, playerId.job, playerId.nick,
BiS(bis.map(_.piece)), loot.map(_.piece))
val player = profile.getPlayerFull(playerId).flatMap { maybePlayerData =>
Future.traverse(maybePlayerData.toSeq) { playerData =>
for {
bis <- profile.getPiecesBiS(playerId)
loot <- profile.getPieces(playerId)
} yield Player(playerId.partyId, playerId.job, playerId.nick,
BiS(bis.map(_.piece)), loot.map(_.piece),
playerData.link, playerData.priority)
}
}.map(_.headOption)
player.pipeTo(client)
case RemovePlayer(playerId) =>

View File

@ -16,6 +16,11 @@ trait DatabaseUserHandler { this: Database =>
import DatabaseUserHandler._
def userHandler: Receive = {
case AddUser(user, isHashedPassword) =>
val client = sender()
val toInsert = if (isHashedPassword) user else user.withHashedPassword
profile.insertUser(toInsert).pipeTo(client)
case DeleteUser(partyId, username) =>
val client = sender()
profile.deleteUser(partyId, username).pipeTo(client)
@ -27,17 +32,12 @@ trait DatabaseUserHandler { this: Database =>
case GetUsers(partyId) =>
val client = sender()
profile.getUsers(partyId).pipeTo(client)
case InsertUser(user, isHashedPassword) =>
val client = sender()
val toInsert = if (isHashedPassword) user else user.copy(password = user.hash)
profile.insertUser(toInsert).pipeTo(client)
}
}
object DatabaseUserHandler {
case class AddUser(user: User, isHashedPassword: Boolean)
case class DeleteUser(partyId: String, username: String)
case class GetUser(partyId: String, username: String)
case class GetUsers(partyId: String)
case class InsertUser(user: User, isHashedPassword: Boolean)
}

View File

@ -44,7 +44,10 @@ trait LootProfile { this: DatabaseProfile =>
}
def deletePieceById(piece: Piece)(playerId: Long): Future[Int] =
db.run(pieceLoot(LootRep.fromPiece(playerId, piece)).take(1).delete)
db.run(pieceLoot(LootRep.fromPiece(playerId, piece)).map(_.lootId).max.result).flatMap {
case Some(id) => db.run(lootTable.filter(_.lootId === id).delete)
case _ => throw new IllegalArgumentException(s"Could not find piece $piece belong to $playerId")
}
def getPiecesById(playerId: Long): Future[Seq[Loot]] = getPiecesById(Seq(playerId))
def getPiecesById(playerIds: Seq[Long]): Future[Seq[Loot]] =
db.run(piecesLoot(playerIds).result).map(_.map(_.toLoot))

View File

@ -10,6 +10,7 @@ package me.arcanis.ffxivbis.storage
import com.typesafe.config.Config
import org.flywaydb.core.Flyway
import org.flywaydb.core.api.configuration.ClassicConfiguration
import scala.concurrent.Future
@ -21,7 +22,16 @@ class Migration(config: Config) {
val username = section.getString("db.user")
val password = section.getString("db.password")
val flyway = Flyway.configure().dataSource(url, username, password).load()
val provider = url match {
case s"jdbc:$p:$_" => p
case other => throw new NotImplementedError(s"unknown could not parse jdbc url from $other")
}
val flywayConfiguration = new ClassicConfiguration
flywayConfiguration.setLocationsAsStrings(s"db/migration/$provider")
flywayConfiguration.setDataSource(url, username, password)
val flyway = new Flyway(flywayConfiguration)
Future.successful(flyway.migrate())
}
}

View File

@ -49,6 +49,8 @@ trait PlayersProfile { this: DatabaseProfile =>
})
def getPlayer(playerId: PlayerId): Future[Option[Long]] =
db.run(player(playerId).map(_.playerId).result.headOption)
def getPlayerFull(playerId: PlayerId): Future[Option[Player]] =
db.run(player(playerId).result.headOption.map(_.map(_.toPlayer)))
def getPlayers(partyId: String): Future[Seq[Long]] =
db.run(players(partyId).map(_.playerId).result)
def insertPlayer(playerObj: Player): Future[Int] =

View File

@ -22,7 +22,7 @@ trait UsersProfile { this: DatabaseProfile =>
}
object UserRep {
def fromUser(user: User, id: Option[Long]): UserRep =
UserRep(user.partyId, None, user.username, user.password, user.permission.toString)
UserRep(user.partyId, id, user.username, user.password, user.permission.toString)
}
class Users(tag: Tag) extends Table[UserRep](tag, "users") {
@ -47,8 +47,8 @@ trait UsersProfile { this: DatabaseProfile =>
def getUsers(partyId: String): Future[Seq[User]] =
db.run(user(partyId, None).result).map(_.map(_.toUser))
def insertUser(userObj: User): Future[Int] =
db.run(user(userObj.partyId, Some(userObj.username)).result.headOption).map {
case Some(user) => db.run(usersTable.update(UserRep.fromUser(userObj, user.userId)))
db.run(user(userObj.partyId, Some(userObj.username)).map(_.userId).result.headOption).map {
case Some(id) => db.run(usersTable.insertOrUpdate(UserRep.fromUser(userObj, Some(id))))
case _ => db.run(usersTable.insertOrUpdate(UserRep.fromUser(userObj, None)))
}.flatten

View File

@ -0,0 +1,43 @@
package me.arcanis.ffxivbis.models
import me.arcanis.ffxivbis.service.Party
object Fixtures {
lazy val bis: BiS = BiS(
Seq(
Weapon(isTome = false ,Job.DNC),
Head(isTome = false, Job.DNC),
Body(isTome = false, Job.DNC),
Hands(isTome = true, Job.DNC),
Waist(isTome = true, Job.DNC),
Legs(isTome = true, Job.DNC),
Feet(isTome = false, Job.DNC),
Ears(isTome = false, Job.DNC),
Neck(isTome = true, Job.DNC),
Wrist(isTome = false, Job.DNC),
Ring(isTome = true, Job.DNC, "leftRing"),
Ring(isTome = true, Job.DNC, "rightRing")
)
)
lazy val link: String = "https://ffxiv.ariyala.com/19V5R"
lazy val lootBody: Piece = Body(isTome = false, Job.DNC)
lazy val lootHands: Piece = Hands(isTome = true, Job.DNC)
lazy val lootLegs: Piece = Legs(isTome = false, Job.DNC)
lazy val lootUpgrade: Piece = BodyUpgrade
lazy val loot: Seq[Piece] = Seq(lootBody, lootHands, lootLegs, lootUpgrade)
lazy val partyId: String = Party.randomPartyId
lazy val partyId2: String = Party.randomPartyId
lazy val playerEmpty: Player =
Player(partyId, Job.DNC, "Siuan Sanche", BiS(), Seq.empty, Some(link))
lazy val playerWithBiS: Player = playerEmpty.copy(bis = bis)
lazy val userPassword: String = "password"
lazy val userPassword2: String = "pa55w0rd"
lazy val userAdmin: User = User(partyId, "admin", userPassword, Permission.admin).withHashedPassword
lazy val userGet: User = User(partyId, "get", userPassword, Permission.get).withHashedPassword
lazy val users: Seq[User] = Seq(userAdmin, userGet)
}

View File

@ -0,0 +1,29 @@
package me.arcanis.ffxivbis.models
import java.io.File
import com.typesafe.config.{Config, ConfigFactory, ConfigValueFactory}
object Settings {
def config(values: Map[String, AnyRef]): Config = {
@scala.annotation.tailrec
def replace(acc: Config, iter: List[(String, AnyRef)]): Config = iter match {
case Nil => acc
case (key -> value) :: tail => replace(acc.withValue(key, ConfigValueFactory.fromAnyRef(value)), tail)
}
val default = ConfigFactory.load()
replace(default, values.toList)
}
def clearDatabase(config: Config): Unit = {
val databasePath =
config.getString("me.arcanis.ffxivbis.database.sqlite.db.url").split(":").last
val databaseFile = new File(databasePath)
if (databaseFile.exists)
databaseFile.delete()
}
def randomDatabasePath: String = File.createTempFile("ffxivdb-",".db").toPath.toString
def withRandomDatabase: Config =
config(Map("me.arcanis.ffxivbis.database.sqlite.db.url" -> s"jdbc:sqlite:$randomDatabasePath"))
}

View File

@ -0,0 +1,27 @@
package me.arcanis.ffxivbis.service
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import me.arcanis.ffxivbis.models.{Fixtures, Job}
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import scala.concurrent.duration._
import scala.language.postfixOps
class AriyalaTest extends TestKit(ActorSystem("ariyala"))
with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
private val timeout: FiniteDuration = 60 seconds
override def afterAll: Unit = TestKit.shutdownActorSystem(system)
"ariyala actor" must {
"get best in slot set" in {
val ariyala = system.actorOf(Ariyala.props)
ariyala ! Ariyala.GetBiS(Fixtures.link, Job.DNC)
expectMsg(timeout, Fixtures.bis)
}
}
}

View File

@ -0,0 +1,82 @@
package me.arcanis.ffxivbis.service
import akka.actor.ActorSystem
import akka.pattern.ask
import akka.testkit.{ImplicitSender, TestKit}
import me.arcanis.ffxivbis.models.{Fixtures, Hands, Job, Piece, Player, Settings}
import me.arcanis.ffxivbis.storage.Migration
import me.arcanis.ffxivbis.utils.Compare
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.language.postfixOps
class DatabaseBiSHandlerTest
extends TestKit(ActorSystem("database-bis-handler", Settings.withRandomDatabase))
with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
private val database = system.actorOf(impl.DatabaseImpl.props)
private val timeout: FiniteDuration = 60 seconds
override def beforeAll: Unit = {
Await.result(Migration(system.settings.config), timeout)
Await.result((database ? impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty))(timeout).mapTo[Int], timeout)
}
override def afterAll: Unit = {
TestKit.shutdownActorSystem(system)
Settings.clearDatabase(system.settings.config)
}
"database bis handler" must {
"add pieces to bis" in {
database ! impl.DatabaseBiSHandler.AddPieceToBis(Fixtures.playerEmpty.playerId, Fixtures.lootBody)
expectMsg(timeout, 1)
database ! impl.DatabaseBiSHandler.AddPieceToBis(Fixtures.playerEmpty.playerId, Fixtures.lootHands)
expectMsg(timeout, 1)
}
"get party bis set" in {
database ! impl.DatabaseBiSHandler.GetBiS(Fixtures.playerEmpty.partyId, None)
expectMsgPF(timeout) {
case party: Seq[_] if partyBiSCompare(party, Seq(Fixtures.lootBody, Fixtures.lootHands)) => ()
}
}
"get bis set" in {
database ! impl.DatabaseBiSHandler.GetBiS(Fixtures.playerEmpty.partyId, Some(Fixtures.playerEmpty.playerId))
expectMsgPF(timeout) {
case party: Seq[_] if partyBiSCompare(party, Seq(Fixtures.lootBody, Fixtures.lootHands)) => ()
}
}
"remove piece from bis set" in {
database ! impl.DatabaseBiSHandler.RemovePieceFromBiS(Fixtures.playerEmpty.playerId, Fixtures.lootBody)
expectMsg(timeout, 1)
database ! impl.DatabaseBiSHandler.GetBiS(Fixtures.playerEmpty.partyId, None)
expectMsgPF(timeout) {
case party: Seq[_] if partyBiSCompare(party, Seq(Fixtures.lootHands)) => ()
}
}
"update piece in bis set" in {
val newPiece = Hands(isTome = false, Job.DNC)
database ! impl.DatabaseBiSHandler.AddPieceToBis(Fixtures.playerEmpty.playerId, newPiece)
expectMsg(timeout, 1)
database ! impl.DatabaseBiSHandler.GetBiS(Fixtures.playerEmpty.partyId, None)
expectMsgPF(timeout) {
case party: Seq[_] if partyBiSCompare(party, Seq(newPiece)) => ()
}
}
}
private def partyBiSCompare[T](party: Seq[T], bis: Seq[Piece]): Boolean =
Compare.seqEquals(party.foldLeft(Seq.empty[Piece]){ case (acc, player) => acc ++ player.asInstanceOf[Player].bis.pieces }, bis)
}

View File

@ -0,0 +1,86 @@
package me.arcanis.ffxivbis.service
import akka.actor.ActorSystem
import akka.pattern.ask
import akka.testkit.{ImplicitSender, TestKit}
import me.arcanis.ffxivbis.models.{Fixtures, Piece, Player, Settings}
import me.arcanis.ffxivbis.storage.Migration
import me.arcanis.ffxivbis.utils.Compare
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.language.postfixOps
class DatabaseLootHandlerTest
extends TestKit(ActorSystem("database-loot-handler", Settings.withRandomDatabase))
with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
private val database = system.actorOf(impl.DatabaseImpl.props)
private val timeout: FiniteDuration = 60 seconds
override def beforeAll: Unit = {
Await.result(Migration(system.settings.config), timeout)
Await.result((database ? impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty))(timeout).mapTo[Int], timeout)
}
override def afterAll: Unit = {
TestKit.shutdownActorSystem(system)
Settings.clearDatabase(system.settings.config)
}
"database loot handler actor" must {
"add loot" in {
Fixtures.loot.foreach { piece =>
database ! impl.DatabaseLootHandler.AddPieceTo(Fixtures.playerEmpty.playerId, piece)
expectMsg(timeout, 1)
}
}
"get party loot" in {
database ! impl.DatabaseLootHandler.GetLoot(Fixtures.playerEmpty.partyId, None)
expectMsgPF(timeout) {
case party: Seq[_] if partyLootCompare(party, Fixtures.loot) => ()
}
}
"get loot" in {
database ! impl.DatabaseLootHandler.GetLoot(Fixtures.playerEmpty.partyId, Some(Fixtures.playerEmpty.playerId))
expectMsgPF(timeout) {
case party: Seq[_] if partyLootCompare(party, Fixtures.loot) => ()
}
}
"remove loot" in {
database ! impl.DatabaseLootHandler.RemovePieceFrom(Fixtures.playerEmpty.playerId, Fixtures.lootBody)
expectMsg(timeout, 1)
val newLoot = Fixtures.loot.filterNot(_ == Fixtures.lootBody)
database ! impl.DatabaseLootHandler.GetLoot(Fixtures.playerEmpty.partyId, None)
expectMsgPF(timeout) {
case party: Seq[_] if partyLootCompare(party, newLoot) => ()
}
}
"add same loot" in {
database ! impl.DatabaseLootHandler.AddPieceTo(Fixtures.playerEmpty.playerId, Fixtures.lootBody)
expectMsg(timeout, 1)
Fixtures.loot.foreach { piece =>
database ! impl.DatabaseLootHandler.AddPieceTo(Fixtures.playerEmpty.playerId, piece)
expectMsg(timeout, 1)
}
database ! impl.DatabaseLootHandler.GetLoot(Fixtures.playerEmpty.partyId, None)
expectMsgPF(timeout) {
case party: Seq[_] if partyLootCompare(party, Fixtures.loot ++ Fixtures.loot) => ()
}
}
}
private def partyLootCompare[T](party: Seq[T], loot: Seq[Piece]): Boolean =
Compare.seqEquals(party.foldLeft(Seq.empty[Piece]){ case (acc, player) => acc ++ player.asInstanceOf[Player].loot }, loot)
}

View File

@ -0,0 +1,73 @@
package me.arcanis.ffxivbis.service
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import me.arcanis.ffxivbis.models.{Fixtures, Settings}
import me.arcanis.ffxivbis.storage.Migration
import me.arcanis.ffxivbis.utils.Compare
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.language.postfixOps
class DatabasePartyHandlerTest
extends TestKit(ActorSystem("database-party-handler", Settings.withRandomDatabase))
with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
private val database = system.actorOf(impl.DatabaseImpl.props)
private val timeout: FiniteDuration = 60 seconds
override def beforeAll: Unit = {
Await.result(Migration(system.settings.config), timeout)
}
override def afterAll: Unit = {
TestKit.shutdownActorSystem(system)
Settings.clearDatabase(system.settings.config)
}
"database party handler actor" must {
"add player" in {
database ! impl.DatabasePartyHandler.AddPlayer(Fixtures.playerEmpty)
expectMsg(timeout, 1)
}
"get party" in {
database ! impl.DatabasePartyHandler.GetParty(Fixtures.partyId)
expectMsgPF(timeout) {
case p: Party if Compare.seqEquals(p.getPlayers, Seq(Fixtures.playerEmpty)) => ()
}
}
"get player" in {
database ! impl.DatabasePartyHandler.GetPlayer(Fixtures.playerEmpty.playerId)
expectMsg(timeout, Some(Fixtures.playerEmpty))
}
"update player" in {
val newPlayer = Fixtures.playerEmpty.copy(priority = 2)
database ! impl.DatabasePartyHandler.AddPlayer(newPlayer)
expectMsg(timeout, 1)
database ! impl.DatabasePartyHandler.GetPlayer(newPlayer.playerId)
expectMsg(timeout, Some(newPlayer))
database ! impl.DatabasePartyHandler.GetParty(Fixtures.partyId)
expectMsgPF(timeout) {
case p: Party if Compare.seqEquals(p.getPlayers, Seq(newPlayer)) => ()
}
}
"remove player" in {
database ! impl.DatabasePartyHandler.RemovePlayer(Fixtures.playerEmpty.playerId)
expectMsg(timeout, 1)
database ! impl.DatabasePartyHandler.GetPlayer(Fixtures.playerEmpty.playerId)
expectMsg(timeout, None)
}
}
}

View File

@ -0,0 +1,76 @@
package me.arcanis.ffxivbis.service
import akka.actor.ActorSystem
import akka.testkit.{ImplicitSender, TestKit}
import me.arcanis.ffxivbis.models.{Fixtures, Settings}
import me.arcanis.ffxivbis.storage.Migration
import me.arcanis.ffxivbis.utils.Compare
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.language.postfixOps
class DatabaseUserHandlerTest
extends TestKit(ActorSystem("database-user-handler", Settings.withRandomDatabase))
with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
private val database = system.actorOf(impl.DatabaseImpl.props)
private val timeout: FiniteDuration = 60 seconds
override def beforeAll: Unit = {
Await.result(Migration(system.settings.config), timeout)
}
override def afterAll: Unit = {
TestKit.shutdownActorSystem(system)
Settings.clearDatabase(system.settings.config)
}
"database user handler actor" must {
"add user" in {
database ! impl.DatabaseUserHandler.AddUser(Fixtures.userAdmin, isHashedPassword = true)
expectMsg(timeout, 1)
}
"get user" in {
database ! impl.DatabaseUserHandler.GetUser(Fixtures.partyId, Fixtures.userAdmin.username)
expectMsg(timeout, Some(Fixtures.userAdmin))
}
"get users" in {
database ! impl.DatabaseUserHandler.AddUser(Fixtures.userGet, isHashedPassword = true)
expectMsg(timeout, 1)
database ! impl.DatabaseUserHandler.GetUsers(Fixtures.partyId)
expectMsgPF(timeout) {
case u: Seq[_] if Compare.seqEquals(u, Fixtures.users) => ()
}
}
"update user" in {
val newUser= Fixtures.userGet.copy(password = Fixtures.userPassword2).withHashedPassword
val newUserSet = Seq(newUser, Fixtures.userAdmin)
database ! impl.DatabaseUserHandler.AddUser(newUser, isHashedPassword = true)
expectMsg(timeout, 1)
database ! impl.DatabaseUserHandler.GetUser(Fixtures.partyId, newUser.username)
expectMsg(timeout, Some(newUser))
database ! impl.DatabaseUserHandler.GetUsers(Fixtures.partyId)
expectMsgPF(timeout) {
case u: Seq[_] if Compare.seqEquals(u, newUserSet) => ()
}
}
"remove user" in {
database ! impl.DatabaseUserHandler.DeleteUser(Fixtures.partyId, Fixtures.userGet.username)
expectMsg(timeout, 1)
database ! impl.DatabaseUserHandler.GetUser(Fixtures.partyId, Fixtures.userGet.username)
expectMsg(timeout, None)
}
}
}

View File

@ -0,0 +1,8 @@
package me.arcanis.ffxivbis.utils
object Compare {
def seqEquals[T](left: Seq[T], right: Seq[T]): Boolean =
left.groupBy(identity).view.mapValues(_.size).forall {
case (key, count) => right.count(_ == key) == count
}
}

4
test.sbt Normal file
View File

@ -0,0 +1,4 @@
libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.8"
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.8" % "test"
libraryDependencies += "com.typesafe.akka" %% "akka-testkit" % "2.5.26" % "test"

1
version.sbt Normal file
View File

@ -0,0 +1 @@
version := "0.9.0"