mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-24 17:27:17 +00:00
even more tests
This commit is contained in:
parent
4cdcd80d51
commit
2a1eb9430e
9
.travis.yml
Normal file
9
.travis.yml
Normal file
@ -0,0 +1,9 @@
|
||||
sudo: required
|
||||
language: generic
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
script:
|
||||
- docker run -it --rm mozilla/sbt sbt compile
|
||||
- docker run -it --rm mozilla/sbt sbt test
|
@ -1,16 +1,16 @@
|
||||
me.arcanis.ffxivbis {
|
||||
ariyala {
|
||||
// ariyala base url, string, required
|
||||
# ariyala base url, string, required
|
||||
ariyala-url = "https://ffxiv.ariyala.com"
|
||||
// xivapi base url, string, required
|
||||
# xivapi base url, string, required
|
||||
xivapi-url = "https://xivapi.com"
|
||||
// xivapi developer key, string, optional
|
||||
# xivapi-key = "abc-def"
|
||||
# xivapi developer key, string, optional
|
||||
# xivapi-key = "abcdef"
|
||||
}
|
||||
|
||||
database {
|
||||
// database section. Section must be declared inside
|
||||
// for more detailed section descriptions refer to slick documentation
|
||||
# database section. Section must be declared inside
|
||||
# for more detailed section descriptions refer to slick documentation
|
||||
mode = "sqlite"
|
||||
|
||||
sqlite {
|
||||
@ -35,19 +35,19 @@ me.arcanis.ffxivbis {
|
||||
}
|
||||
|
||||
settings {
|
||||
// counters of Player class which will be called to sort players for loot priority
|
||||
// list of strings, required
|
||||
# counters of Player class which will be called to sort players for loot priority
|
||||
# list of strings, required
|
||||
priority = [
|
||||
"isRequired", "lootCountBiS", "priority", "lootCount", "lootCountTotal"
|
||||
]
|
||||
// general request timeout, duratin, required
|
||||
# general request timeout, duratin, required
|
||||
request-timeout = 10s
|
||||
}
|
||||
|
||||
web {
|
||||
// address to bind, string, required
|
||||
# address to bind, string, required
|
||||
host = "0.0.0.0"
|
||||
// port to bind, int, required
|
||||
# port to bind, int, required
|
||||
port = 8000
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ object Party {
|
||||
case (acc, (playerId, player)) =>
|
||||
acc + (player.playerId -> player
|
||||
.withBiS(bisByPlayer.get(playerId))
|
||||
.withLoot(lootByPlayer.get(playerId)))
|
||||
.withLoot(lootByPlayer.getOrElse(playerId, Seq.empty)))
|
||||
}
|
||||
Party(partyId, getRules(config), playersWithItems)
|
||||
}
|
||||
|
@ -27,9 +27,10 @@ case class Player(partyId: String,
|
||||
partyId, job, nick, isRequired(piece), priority,
|
||||
bisCountTotal(piece), lootCount(piece),
|
||||
lootCountBiS(piece), lootCountTotal(piece))
|
||||
def withLoot(list: Option[Seq[Piece]]): Player = list match {
|
||||
case Some(value) => copy(loot = value)
|
||||
case None => this
|
||||
def withLoot(piece: Piece): Player = withLoot(Seq(piece))
|
||||
def withLoot(list: Seq[Piece]): Player = list match {
|
||||
case Nil => this
|
||||
case _ => copy(loot = list)
|
||||
}
|
||||
|
||||
def isRequired(piece: Option[Piece]): Boolean = {
|
||||
|
@ -26,12 +26,12 @@ case class PlayerIdWithCounters(partyId: String,
|
||||
def playerId: PlayerId = PlayerId(partyId, job, nick)
|
||||
|
||||
private val counters: Map[String, Int] = Map(
|
||||
"isRequired" -> (if (isRequired) 1 else 0),
|
||||
"priority" -> priority,
|
||||
"bisCountTotal" -> bisCountTotal,
|
||||
"lootCount" -> lootCount,
|
||||
"lootCountBiS" -> lootCountBiS,
|
||||
"lootCountTotal" -> lootCountTotal) withDefaultValue 0
|
||||
"isRequired" -> (if (isRequired) 1 else 0), // true has more priority
|
||||
"priority" -> -priority, // the less value the more priority
|
||||
"bisCountTotal" -> bisCountTotal, // the more pieces in bis the more priority
|
||||
"lootCount" -> -lootCount, // the less loot got the more priority
|
||||
"lootCountBiS" -> -lootCountBiS, // the less bis pieces looted the more priority
|
||||
"lootCountTotal" -> -lootCountTotal) withDefaultValue 0 // the less pieces looted the more priority
|
||||
|
||||
private def withCounters(orderBy: Seq[String]): PlayerCountersComparator =
|
||||
PlayerCountersComparator(orderBy.map(counters): _*)
|
||||
|
@ -22,7 +22,7 @@ import me.arcanis.ffxivbis.models.{BiS, Job, Piece}
|
||||
import spray.json._
|
||||
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
import scala.util.Try
|
||||
import scala.util.{Failure, Success, Try}
|
||||
|
||||
class Ariyala extends Actor with StrictLogging {
|
||||
import Ariyala._
|
||||
@ -51,12 +51,16 @@ class Ariyala extends Actor with StrictLogging {
|
||||
sendRequest(uri, Ariyala.parseAriyalaJsonToPieces(job, getIsTome))
|
||||
}
|
||||
|
||||
private def getIsTome(itemId: Long): Future[Boolean] = {
|
||||
val uri = Try(Uri(xivapiUrl)
|
||||
.withPath(Uri.Path / "item" / itemId.toString)
|
||||
.withQuery(Uri.Query(Map("columns" -> "IsEquippable", "private_key" -> xivapiKey.getOrElse("")))))
|
||||
private def getIsTome(itemIds: Seq[Long]): Future[Map[Long, Boolean]] = {
|
||||
val uri = Uri(xivapiUrl)
|
||||
.withPath(Uri.Path / "item")
|
||||
.withQuery(Uri.Query(Map(
|
||||
"columns" -> Seq("ID", "IsEquippable").mkString(","),
|
||||
"ids" -> itemIds.mkString(","),
|
||||
"private_key" -> xivapiKey.getOrElse("")
|
||||
)))
|
||||
|
||||
sendRequest(uri.toOption.get, Ariyala.parseXivapiJson)
|
||||
sendRequest(uri, Ariyala.parseXivapiJson)
|
||||
}
|
||||
|
||||
private def sendRequest[T](uri: Uri, parser: JsObject => Future[T]): Future[T] =
|
||||
@ -68,7 +72,7 @@ class Ariyala extends Actor with StrictLogging {
|
||||
.map(result => parser(result.parseJson.asJsObject))
|
||||
.toMat(Sink.head)(Keep.right)
|
||||
.run().flatten
|
||||
case _ => Future.failed(deserializationError("Invalid response from server"))
|
||||
case other => Future.failed(new Error(s"Invalid response from server $other"))
|
||||
}.flatten
|
||||
}
|
||||
|
||||
@ -77,13 +81,14 @@ object Ariyala {
|
||||
|
||||
case class GetBiS(link: String, job: Job.Job)
|
||||
|
||||
private def parseAriyalaJson(job: Job.Job)(js: JsObject): Future[Map[String, Long]] = {
|
||||
try {
|
||||
private def parseAriyalaJson(job: Job.Job)(js: JsObject)
|
||||
(implicit executionContext: ExecutionContext): Future[Map[String, Long]] =
|
||||
Future {
|
||||
val apiJob = js.fields.get("content") match {
|
||||
case Some(JsString(value)) => value
|
||||
case other => throw deserializationError(s"Invalid job name $other")
|
||||
}
|
||||
Future.successful(js.fields.get("datasets") match {
|
||||
js.fields.get("datasets") match {
|
||||
case Some(datasets: JsObject) =>
|
||||
val fields = datasets.fields
|
||||
fields.getOrElse(apiJob, fields(job.toString)).asJsObject
|
||||
@ -94,24 +99,30 @@ object Ariyala {
|
||||
case (acc, _) => acc
|
||||
}
|
||||
case other => throw deserializationError(s"Invalid json $other")
|
||||
})
|
||||
} catch {
|
||||
case e: Exception => Future.failed(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def parseAriyalaJsonToPieces(job: Job.Job, isTome: Long => Future[Boolean])(js: JsObject)
|
||||
private def parseAriyalaJsonToPieces(job: Job.Job, isTome: Seq[Long] => Future[Map[Long, Boolean]])(js: JsObject)
|
||||
(implicit executionContext: ExecutionContext): Future[Seq[Piece]] =
|
||||
parseAriyalaJson(job)(js).map { pieces =>
|
||||
Future.sequence(pieces.toSeq.map {
|
||||
case (itemName, itemId) => isTome(itemId).map(Piece(itemName, _, job))
|
||||
})
|
||||
}.flatten
|
||||
parseAriyalaJson(job)(js).flatMap { pieces =>
|
||||
isTome(pieces.values.toSeq).map { tomePieces =>
|
||||
pieces.view.mapValues(tomePieces).map {
|
||||
case (piece, isTomePiece) => Piece(piece, isTomePiece, job)
|
||||
}.toSeq
|
||||
}
|
||||
}
|
||||
|
||||
private def parseXivapiJson(js: JsObject): Future[Boolean] =
|
||||
js.fields.get("IsEquippable") match {
|
||||
case Some(JsNumber(value)) => Future.successful(value == 0) // don't ask
|
||||
case other => Future.failed(deserializationError(s"Could not parse $other"))
|
||||
private def parseXivapiJson(js: JsObject)
|
||||
(implicit executionContext: ExecutionContext): Future[Map[Long, Boolean]] =
|
||||
Future {
|
||||
js.fields("Results") match {
|
||||
case array: JsArray =>
|
||||
array.elements.map(_.asJsObject.getFields("ID", "IsEquippable") match {
|
||||
case Seq(JsNumber(id), JsNumber(isTome)) => id.toLong -> (isTome == 0)
|
||||
case other => throw deserializationError(s"Could not parse $other")
|
||||
}).toMap
|
||||
case other => throw deserializationError(s"Could not parse $other")
|
||||
}
|
||||
}
|
||||
|
||||
private def remapKey(key: String): Option[String] = key match {
|
||||
|
@ -1,4 +1,6 @@
|
||||
package me.arcanis.ffxivbis.models
|
||||
package me.arcanis.ffxivbis
|
||||
|
||||
import me.arcanis.ffxivbis.models._
|
||||
|
||||
object Fixtures {
|
||||
lazy val bis: BiS = BiS(
|
||||
@ -19,6 +21,7 @@ object Fixtures {
|
||||
)
|
||||
|
||||
lazy val link: String = "https://ffxiv.ariyala.com/19V5R"
|
||||
lazy val link2: String = "https://ffxiv.ariyala.com/1A0WM"
|
||||
|
||||
lazy val lootWeapon: Piece = Weapon(isTome = true, Job.AnyJob)
|
||||
lazy val lootBody: Piece = Body(isTome = false, Job.AnyJob)
|
@ -1,4 +1,4 @@
|
||||
package me.arcanis.ffxivbis.models
|
||||
package me.arcanis.ffxivbis
|
||||
|
||||
import java.io.File
|
||||
|
@ -1,5 +1,6 @@
|
||||
package me.arcanis.ffxivbis.models
|
||||
|
||||
import me.arcanis.ffxivbis.Fixtures
|
||||
import me.arcanis.ffxivbis.utils.Compare
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package me.arcanis.ffxivbis.models
|
||||
|
||||
import me.arcanis.ffxivbis.Fixtures
|
||||
import me.arcanis.ffxivbis.utils.Compare
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package me.arcanis.ffxivbis.models
|
||||
|
||||
import me.arcanis.ffxivbis.Fixtures
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
||||
class PieceTest extends WordSpecLike with Matchers with BeforeAndAfterAll {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package me.arcanis.ffxivbis.models
|
||||
|
||||
import me.arcanis.ffxivbis.Fixtures
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
||||
class PlayerIdTest extends WordSpecLike with Matchers with BeforeAndAfterAll {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package me.arcanis.ffxivbis.models
|
||||
|
||||
import me.arcanis.ffxivbis.Fixtures
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
||||
class PlayerTest extends WordSpecLike with Matchers with BeforeAndAfterAll {
|
||||
@ -11,7 +12,7 @@ class PlayerTest extends WordSpecLike with Matchers with BeforeAndAfterAll {
|
||||
}
|
||||
|
||||
"add loot" in {
|
||||
Fixtures.playerEmpty.withLoot(Some(Fixtures.loot)).loot shouldEqual Fixtures.loot
|
||||
Fixtures.playerEmpty.withLoot(Fixtures.loot).loot shouldEqual Fixtures.loot
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package me.arcanis.ffxivbis.models
|
||||
|
||||
import me.arcanis.ffxivbis.Fixtures
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
||||
class UserTest extends WordSpecLike with Matchers with BeforeAndAfterAll {
|
||||
|
@ -2,7 +2,8 @@ package me.arcanis.ffxivbis.service
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit.{ImplicitSender, TestKit}
|
||||
import me.arcanis.ffxivbis.models.{Fixtures, Job}
|
||||
import me.arcanis.ffxivbis.Fixtures
|
||||
import me.arcanis.ffxivbis.models._
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
@ -3,7 +3,8 @@ 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.{Fixtures, Settings}
|
||||
import me.arcanis.ffxivbis.models._
|
||||
import me.arcanis.ffxivbis.storage.Migration
|
||||
import me.arcanis.ffxivbis.utils.Compare
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
@ -3,7 +3,8 @@ 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.{Fixtures, Settings}
|
||||
import me.arcanis.ffxivbis.models._
|
||||
import me.arcanis.ffxivbis.storage.Migration
|
||||
import me.arcanis.ffxivbis.utils.Compare
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
@ -2,7 +2,8 @@ package me.arcanis.ffxivbis.service
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.testkit.{ImplicitSender, TestKit}
|
||||
import me.arcanis.ffxivbis.models.{Fixtures, Party, Settings}
|
||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||
import me.arcanis.ffxivbis.models._
|
||||
import me.arcanis.ffxivbis.storage.Migration
|
||||
import me.arcanis.ffxivbis.utils.Compare
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
@ -2,7 +2,7 @@ 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.{Fixtures, Settings}
|
||||
import me.arcanis.ffxivbis.storage.Migration
|
||||
import me.arcanis.ffxivbis.utils.Compare
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
@ -0,0 +1,89 @@
|
||||
package me.arcanis.ffxivbis.service
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.pattern.ask
|
||||
import akka.testkit.{ImplicitSender, TestKit}
|
||||
import me.arcanis.ffxivbis.{Fixtures, Settings}
|
||||
import me.arcanis.ffxivbis.models._
|
||||
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}
|
||||
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
import scala.language.postfixOps
|
||||
|
||||
class LootSelectorTest extends TestKit(ActorSystem("lootselector"))
|
||||
with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
|
||||
|
||||
private var default: Party = Party(Some(Fixtures.partyId), Settings.config(Map.empty))
|
||||
private var dnc: Player = Player(Fixtures.partyId, Job.DNC, "a nick", BiS(), Seq.empty, Some(Fixtures.link))
|
||||
private var drg: Player = Player(Fixtures.partyId, Job.DRG, "another nick", BiS(), Seq.empty, Some(Fixtures.link2))
|
||||
private val timeout: FiniteDuration = 60 seconds
|
||||
|
||||
override def beforeAll(): Unit = {
|
||||
val ariyala = system.actorOf(Ariyala.props)
|
||||
|
||||
val dncSet = Await.result((ariyala ? Ariyala.GetBiS(Fixtures.link, Job.DNC) )(timeout).mapTo[BiS], timeout)
|
||||
dnc = dnc.withBiS(Some(dncSet))
|
||||
|
||||
val drgSet = Await.result((ariyala ? Ariyala.GetBiS(Fixtures.link2, Job.DRG) )(timeout).mapTo[BiS], timeout)
|
||||
drg = drg.withBiS(Some(drgSet))
|
||||
|
||||
default = default.withPlayer(dnc).withPlayer(drg)
|
||||
system.stop(ariyala)
|
||||
}
|
||||
|
||||
"loot selector" must {
|
||||
|
||||
"suggest loot by isRequired" in {
|
||||
toPlayerId(default.suggestLoot(Head(isTome = false, Job.AnyJob))) shouldEqual Seq(dnc.playerId, drg.playerId)
|
||||
}
|
||||
|
||||
"suggest loot if a player already have it" in {
|
||||
val piece = Body(isTome = false, Job.AnyJob)
|
||||
val party = default.withPlayer(dnc.withLoot(piece))
|
||||
|
||||
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||
}
|
||||
|
||||
"suggest upgrade" in {
|
||||
val party = default.withPlayer(
|
||||
dnc.withBiS(
|
||||
Some(dnc.bis.copy(weapon = Some(Weapon(isTome = true, Job.DNC))))
|
||||
)
|
||||
)
|
||||
|
||||
toPlayerId(party.suggestLoot(WeaponUpgrade)) shouldEqual Seq(dnc.playerId, drg.playerId)
|
||||
}
|
||||
|
||||
"suggest loot by priority" in {
|
||||
val party = default.withPlayer(dnc.copy(priority = 2))
|
||||
|
||||
toPlayerId(party.suggestLoot(Body(isTome = false, Job.AnyJob))) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||
}
|
||||
|
||||
"suggest loot by bis pieces got" in {
|
||||
val party = default.withPlayer(dnc.withLoot(Head(isTome = false, Job.AnyJob)))
|
||||
|
||||
toPlayerId(party.suggestLoot(Body(isTome = false, Job.AnyJob))) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||
}
|
||||
|
||||
"suggest loot by this piece got" in {
|
||||
val piece = Body(isTome = true, Job.AnyJob)
|
||||
val party = default.withPlayer(dnc.withLoot(piece))
|
||||
|
||||
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||
}
|
||||
|
||||
"suggest loot by total piece got" in {
|
||||
val piece = Body(isTome = true, Job.AnyJob)
|
||||
val party = default
|
||||
.withPlayer(dnc.withLoot(Seq(piece, piece)))
|
||||
.withPlayer(drg.withLoot(piece))
|
||||
|
||||
toPlayerId(party.suggestLoot(piece)) shouldEqual Seq(drg.playerId, dnc.playerId)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def toPlayerId(result: LootSelector.LootSelectorResult): Seq[PlayerId] = result.result.map(_.playerId)
|
||||
}
|
Loading…
Reference in New Issue
Block a user