mirror of
https://github.com/arcan1s/ffxivbis.git
synced 2025-04-25 01:37:17 +00:00
add more tests
* also make auth provider more powerful
This commit is contained in:
parent
0ab9162cb5
commit
ccbf581332
@ -37,23 +37,16 @@ trait Authorization {
|
|||||||
def authAdmin(partyId: String)(username: String, password: String)(implicit
|
def authAdmin(partyId: String)(username: String, password: String)(implicit
|
||||||
executionContext: ExecutionContext
|
executionContext: ExecutionContext
|
||||||
): Future[Option[User]] =
|
): Future[Option[User]] =
|
||||||
authenticator(Permission.admin, partyId)(username, password)
|
auth.authenticator(Permission.admin, partyId)(username, password)
|
||||||
|
|
||||||
def authGet(partyId: String)(username: String, password: String)(implicit
|
def authGet(partyId: String)(username: String, password: String)(implicit
|
||||||
executionContext: ExecutionContext
|
executionContext: ExecutionContext
|
||||||
): Future[Option[User]] =
|
): Future[Option[User]] =
|
||||||
authenticator(Permission.get, partyId)(username, password)
|
auth.authenticator(Permission.get, partyId)(username, password)
|
||||||
|
|
||||||
def authPost(partyId: String)(username: String, password: String)(implicit
|
def authPost(partyId: String)(username: String, password: String)(implicit
|
||||||
executionContext: ExecutionContext
|
executionContext: ExecutionContext
|
||||||
): Future[Option[User]] =
|
): Future[Option[User]] =
|
||||||
authenticator(Permission.post, partyId)(username, password)
|
auth.authenticator(Permission.post, partyId)(username, password)
|
||||||
|
|
||||||
private def authenticator(scope: Permission.Value, partyId: String)(username: String, password: String)(implicit
|
|
||||||
executionContext: ExecutionContext
|
|
||||||
): Future[Option[User]] =
|
|
||||||
auth.get(partyId, username).map {
|
|
||||||
case Some(user) if user.verify(password) && user.verityScope(scope) => Some(user)
|
|
||||||
case _ => None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -14,19 +14,31 @@ import akka.util.Timeout
|
|||||||
import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
|
import com.google.common.cache.{CacheBuilder, CacheLoader, LoadingCache}
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
import me.arcanis.ffxivbis.messages.{GetUser, Message}
|
import me.arcanis.ffxivbis.messages.{GetUser, Message}
|
||||||
import me.arcanis.ffxivbis.models.User
|
import me.arcanis.ffxivbis.models.{Permission, User}
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
trait AuthorizationProvider {
|
trait AuthorizationProvider {
|
||||||
|
|
||||||
def get(partyId: String, username: String): Future[Option[User]]
|
def get(partyId: String, username: String): Future[Option[User]]
|
||||||
|
|
||||||
|
def authenticator[T](scope: Permission.Value, partyId: String)(username: String, password: String)(implicit
|
||||||
|
executionContext: ExecutionContext,
|
||||||
|
extractor: User => T
|
||||||
|
): Future[Option[T]] =
|
||||||
|
get(partyId, username).map {
|
||||||
|
case Some(user) if user.verify(password) && user.verityScope(scope) => Some(extractor(user))
|
||||||
|
case _ => None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object AuthorizationProvider {
|
object AuthorizationProvider {
|
||||||
|
|
||||||
def apply(config: Config, storage: ActorRef[Message], timeout: Timeout, scheduler: Scheduler): AuthorizationProvider =
|
def apply(config: Config, storage: ActorRef[Message])(implicit
|
||||||
|
timeout: Timeout,
|
||||||
|
scheduler: Scheduler
|
||||||
|
): AuthorizationProvider =
|
||||||
new AuthorizationProvider {
|
new AuthorizationProvider {
|
||||||
private val cacheSize = config.getInt("me.arcanis.ffxivbis.web.authorization-cache.cache-size")
|
private val cacheSize = config.getInt("me.arcanis.ffxivbis.web.authorization-cache.cache-size")
|
||||||
private val cacheTimeout =
|
private val cacheTimeout =
|
||||||
|
@ -28,7 +28,7 @@ class RootEndpoint(system: ActorSystem[Nothing], storage: ActorRef[Message], pro
|
|||||||
implicit val scheduler: Scheduler = system.scheduler
|
implicit val scheduler: Scheduler = system.scheduler
|
||||||
implicit val timeout: Timeout = config.getTimeout("me.arcanis.ffxivbis.settings.request-timeout")
|
implicit val timeout: Timeout = config.getTimeout("me.arcanis.ffxivbis.settings.request-timeout")
|
||||||
|
|
||||||
private val auth = AuthorizationProvider(config, storage, timeout, scheduler)
|
private val auth = AuthorizationProvider(config, storage)
|
||||||
|
|
||||||
private val rootApiV1Endpoint = new RootApiV1Endpoint(storage, auth, provider, config)
|
private val rootApiV1Endpoint = new RootApiV1Endpoint(storage, auth, provider, config)
|
||||||
private val rootView = new RootView(auth)
|
private val rootView = new RootView(auth)
|
||||||
|
@ -8,36 +8,38 @@
|
|||||||
*/
|
*/
|
||||||
package me.arcanis.ffxivbis.messages
|
package me.arcanis.ffxivbis.messages
|
||||||
|
|
||||||
import akka.actor.typed.{ActorRef, Behavior}
|
import akka.actor.typed.ActorRef
|
||||||
import me.arcanis.ffxivbis.models._
|
import me.arcanis.ffxivbis.models._
|
||||||
import me.arcanis.ffxivbis.service.LootSelector
|
import me.arcanis.ffxivbis.service.LootSelector
|
||||||
|
|
||||||
sealed trait DatabaseMessage extends Message {
|
sealed trait DatabaseMessage extends Message {
|
||||||
|
|
||||||
def partyId: String
|
def partyId: String
|
||||||
}
|
|
||||||
|
|
||||||
object DatabaseMessage {
|
def isReadOnly: Boolean
|
||||||
|
|
||||||
type Handler = PartialFunction[DatabaseMessage, Behavior[DatabaseMessage]]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bis handler
|
// bis handler
|
||||||
trait BisDatabaseMessage extends DatabaseMessage
|
trait BisDatabaseMessage extends DatabaseMessage
|
||||||
|
|
||||||
case class AddPieceToBis(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
case class AddPieceToBis(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class GetBiS(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
case class GetBiS(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||||
extends BisDatabaseMessage
|
extends BisDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
case class RemovePieceFromBiS(playerId: PlayerId, piece: Piece, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class RemovePiecesFromBiS(playerId: PlayerId, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
case class RemovePiecesFromBiS(playerId: PlayerId, replyTo: ActorRef[Unit]) extends BisDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// loot handler
|
// loot handler
|
||||||
@ -45,54 +47,77 @@ trait LootDatabaseMessage extends DatabaseMessage
|
|||||||
|
|
||||||
case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
case class AddPieceTo(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||||
extends LootDatabaseMessage {
|
extends LootDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class GetLoot(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
case class GetLoot(partyId: String, playerId: Option[PlayerId], replyTo: ActorRef[Seq[Player]])
|
||||||
extends LootDatabaseMessage
|
extends LootDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class RemovePieceFrom(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
case class RemovePieceFrom(playerId: PlayerId, piece: Piece, isFreeLoot: Boolean, replyTo: ActorRef[Unit])
|
||||||
extends LootDatabaseMessage {
|
extends LootDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult])
|
case class SuggestLoot(partyId: String, piece: Piece, replyTo: ActorRef[LootSelector.LootSelectorResult])
|
||||||
extends LootDatabaseMessage
|
extends LootDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
// party handler
|
// party handler
|
||||||
trait PartyDatabaseMessage extends DatabaseMessage
|
trait PartyDatabaseMessage extends DatabaseMessage
|
||||||
|
|
||||||
case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
case class AddPlayer(player: Player, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||||
override def partyId: String = player.partyId
|
override val partyId: String = player.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class GetParty(partyId: String, replyTo: ActorRef[Party]) extends PartyDatabaseMessage
|
case class GetParty(partyId: String, replyTo: ActorRef[Party]) extends PartyDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class GetPartyDescription(partyId: String, replyTo: ActorRef[PartyDescription]) extends PartyDatabaseMessage
|
case class GetPartyDescription(partyId: String, replyTo: ActorRef[PartyDescription]) extends PartyDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class GetPlayer(playerId: PlayerId, replyTo: ActorRef[Option[Player]]) extends PartyDatabaseMessage {
|
case class GetPlayer(playerId: PlayerId, replyTo: ActorRef[Option[Player]]) extends PartyDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
}
|
}
|
||||||
|
|
||||||
case class RemovePlayer(playerId: PlayerId, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
case class RemovePlayer(playerId: PlayerId, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||||
override def partyId: String = playerId.partyId
|
override val partyId: String = playerId.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
case class UpdateParty(partyDescription: PartyDescription, replyTo: ActorRef[Unit]) extends PartyDatabaseMessage {
|
||||||
override def partyId: String = partyDescription.partyId
|
override val partyId: String = partyDescription.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// user handler
|
// user handler
|
||||||
trait UserDatabaseMessage extends DatabaseMessage
|
trait UserDatabaseMessage extends DatabaseMessage
|
||||||
|
|
||||||
case class AddUser(user: User, isHashedPassword: Boolean, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
case class AddUser(user: User, isHashedPassword: Boolean, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||||
override def partyId: String = user.partyId
|
override val partyId: String = user.partyId
|
||||||
|
override val isReadOnly: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
case class DeleteUser(partyId: String, username: String, replyTo: ActorRef[Unit]) extends UserDatabaseMessage
|
case class DeleteUser(partyId: String, username: String, replyTo: ActorRef[Unit]) extends UserDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends UserDatabaseMessage
|
case class Exists(partyId: String, replyTo: ActorRef[Boolean]) extends UserDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends UserDatabaseMessage
|
case class GetUser(partyId: String, username: String, replyTo: ActorRef[Option[User]]) extends UserDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
|
||||||
case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends UserDatabaseMessage
|
case class GetUsers(partyId: String, replyTo: ActorRef[Seq[User]]) extends UserDatabaseMessage {
|
||||||
|
override val isReadOnly: Boolean = true
|
||||||
|
}
|
||||||
|
@ -16,7 +16,6 @@ import com.typesafe.scalalogging.StrictLogging
|
|||||||
import me.arcanis.ffxivbis.messages._
|
import me.arcanis.ffxivbis.messages._
|
||||||
import me.arcanis.ffxivbis.models.Party
|
import me.arcanis.ffxivbis.models.Party
|
||||||
|
|
||||||
import scala.concurrent.duration.FiniteDuration
|
|
||||||
import scala.concurrent.{ExecutionContext, Future}
|
import scala.concurrent.{ExecutionContext, Future}
|
||||||
|
|
||||||
class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMessage])
|
class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMessage])
|
||||||
@ -62,7 +61,8 @@ class PartyService(context: ActorContext[Message], storage: ActorRef[DatabaseMes
|
|||||||
|
|
||||||
case req: DatabaseMessage =>
|
case req: DatabaseMessage =>
|
||||||
storage ! req
|
storage ! req
|
||||||
Behaviors.receiveMessage(handle(cache - req.partyId))
|
val result = if (req.isReadOnly) cache else cache - req.partyId
|
||||||
|
Behaviors.receiveMessage(handle(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def getPartyId: Future[String] = {
|
private def getPartyId: Future[String] = {
|
||||||
|
@ -84,4 +84,5 @@ object Fixtures {
|
|||||||
lazy val users: Seq[User] = Seq(userAdmin, userGet)
|
lazy val users: Seq[User] = Seq(userAdmin, userGet)
|
||||||
|
|
||||||
lazy val authProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(Some(userAdmin))
|
lazy val authProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(Some(userAdmin))
|
||||||
|
lazy val rejectingProvider: AuthorizationProvider = (_: String, _: String) => Future.successful(None)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package me.arcanis.ffxivbis.http
|
||||||
|
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
|
||||||
|
import akka.http.scaladsl.server.Route
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import me.arcanis.ffxivbis.Fixtures
|
||||||
|
import me.arcanis.ffxivbis.http.view.RootView
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class AuthorizationTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val auth =
|
||||||
|
Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword))
|
||||||
|
|
||||||
|
"authorization directive" must {
|
||||||
|
|
||||||
|
"accept credentials" in {
|
||||||
|
val route = new RootView(Fixtures.authProvider).routes
|
||||||
|
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"reject credentials" in {
|
||||||
|
val route = new RootView(Fixtures.rejectingProvider).routes
|
||||||
|
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}")).withHeaders(auth) ~> Route.seal(route) ~> check {
|
||||||
|
status shouldEqual StatusCodes.Unauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"reject with empty credentials" in {
|
||||||
|
val route = new RootView(Fixtures.authProvider).routes
|
||||||
|
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}")) ~> Route.seal(route) ~> check {
|
||||||
|
status shouldEqual StatusCodes.Unauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
22
src/test/scala/me/arcanis/ffxivbis/http/HttpLogTest.scala
Normal file
22
src/test/scala/me/arcanis/ffxivbis/http/HttpLogTest.scala
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package me.arcanis.ffxivbis.http
|
||||||
|
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.server.Directives.complete
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class HttpLogTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val log = new HttpLog {}
|
||||||
|
|
||||||
|
"log directive" must {
|
||||||
|
|
||||||
|
"work with empty request" in {
|
||||||
|
Get(Uri("/")) ~> log.withHttpLog(complete(StatusCodes.OK)) ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package me.arcanis.ffxivbis.http
|
||||||
|
|
||||||
|
import akka.actor.testkit.typed.scaladsl.ActorTestKit
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import com.typesafe.config.Config
|
||||||
|
import me.arcanis.ffxivbis.Settings
|
||||||
|
import me.arcanis.ffxivbis.service.PartyService
|
||||||
|
import me.arcanis.ffxivbis.service.bis.BisProvider
|
||||||
|
import me.arcanis.ffxivbis.service.database.Database
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class RootEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val testKit = ActorTestKit(Settings.withRandomDatabase)
|
||||||
|
override val testConfig: Config = testKit.system.settings.config
|
||||||
|
|
||||||
|
private val storage = testKit.spawn(Database())
|
||||||
|
private val provider = testKit.spawn(BisProvider())
|
||||||
|
private val party = testKit.spawn(PartyService(storage))
|
||||||
|
private val route = new RootEndpoint(testKit.system, party, provider).routes
|
||||||
|
|
||||||
|
"root route" must {
|
||||||
|
|
||||||
|
"return swagger ui" in {
|
||||||
|
Get(Uri("/api-docs")) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return static routes" in {
|
||||||
|
Get(Uri("/static/favicon.ico")) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
28
src/test/scala/me/arcanis/ffxivbis/http/SwaggerTest.scala
Normal file
28
src/test/scala/me/arcanis/ffxivbis/http/SwaggerTest.scala
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package me.arcanis.ffxivbis.http
|
||||||
|
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import me.arcanis.ffxivbis.Settings
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class SwaggerTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val swagger = new Swagger(Settings.withRandomDatabase)
|
||||||
|
|
||||||
|
"swagger guard" must {
|
||||||
|
|
||||||
|
"generate json" in {
|
||||||
|
Get(Uri("/api-docs/swagger.json")) ~> swagger.routes ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"generate yml" in {
|
||||||
|
Get(Uri("/api-docs/swagger.yaml")) ~> swagger.routes ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -95,5 +95,15 @@ class LootEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRoute
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"suggest loot" in {
|
||||||
|
val entity = PieceModel.fromPiece(Fixtures.lootBody)
|
||||||
|
val response = Seq(Fixtures.playerEmpty.withCounters(Some(Fixtures.lootBody))).map(PlayerIdWithCountersModel.fromPlayerId)
|
||||||
|
|
||||||
|
Put(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[Seq[PlayerIdWithCountersModel]] shouldEqual response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import akka.http.scaladsl.model.{StatusCodes, Uri}
|
|||||||
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
import akka.http.scaladsl.testkit.{RouteTestTimeout, ScalatestRouteTest}
|
||||||
import akka.testkit.TestKit
|
import akka.testkit.TestKit
|
||||||
import com.typesafe.config.Config
|
import com.typesafe.config.Config
|
||||||
|
import me.arcanis.ffxivbis
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser}
|
import me.arcanis.ffxivbis.messages.{AddPlayer, AddUser}
|
||||||
import me.arcanis.ffxivbis.service.PartyService
|
import me.arcanis.ffxivbis.service.PartyService
|
||||||
@ -52,7 +53,7 @@ class PlayerEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRou
|
|||||||
|
|
||||||
"api v1 player endpoint" must {
|
"api v1 player endpoint" must {
|
||||||
|
|
||||||
"get users" in {
|
"get users belonging to the party" in {
|
||||||
val response = Seq(PlayerModel.fromPlayer(Fixtures.playerEmpty))
|
val response = Seq(PlayerModel.fromPlayer(Fixtures.playerEmpty))
|
||||||
|
|
||||||
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
@ -61,5 +62,42 @@ class PlayerEndpointTest extends AnyWordSpecLike with Matchers with ScalatestRou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"get party stats" in {
|
||||||
|
val response = Seq(PlayerIdWithCountersModel.fromPlayerId(Fixtures.playerEmpty.withCounters(None)))
|
||||||
|
|
||||||
|
Get(endpoint.withPath(endpoint.path / "stats")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[Seq[PlayerIdWithCountersModel]] shouldEqual response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"add new player to the party" in {
|
||||||
|
val entity = PlayerActionModel(ApiAction.add, PlayerModel.fromPlayer(Fixtures.playerWithBiS))
|
||||||
|
|
||||||
|
Post(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.Accepted
|
||||||
|
responseAs[String] shouldEqual ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[Seq[PlayerModel]].map(_.toPlayer.playerId) should contain(Fixtures.playerWithBiS.playerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"remove player from the party" in {
|
||||||
|
val entity = PlayerActionModel(ApiAction.remove, PlayerModel.fromPlayer(Fixtures.playerEmpty))
|
||||||
|
|
||||||
|
Post(endpoint, entity).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.Accepted
|
||||||
|
responseAs[String] shouldEqual ""
|
||||||
|
}
|
||||||
|
|
||||||
|
Get(endpoint).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[Seq[PlayerModel]].map(_.toPlayer.playerId) should not contain(Fixtures.playerEmpty.playerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package me.arcanis.ffxivbis.http.api.v1
|
|||||||
|
|
||||||
import akka.http.scaladsl.model.StatusCodes
|
import akka.http.scaladsl.model.StatusCodes
|
||||||
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
import com.typesafe.config.Config
|
|
||||||
import me.arcanis.ffxivbis.Settings
|
|
||||||
import me.arcanis.ffxivbis.http.api.v1.json._
|
import me.arcanis.ffxivbis.http.api.v1.json._
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
import org.scalatest.wordspec.AnyWordSpecLike
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
@ -13,8 +11,6 @@ import scala.language.postfixOps
|
|||||||
class StatusEndpointTest extends AnyWordSpecLike
|
class StatusEndpointTest extends AnyWordSpecLike
|
||||||
with Matchers with ScalatestRouteTest with JsonSupport {
|
with Matchers with ScalatestRouteTest with JsonSupport {
|
||||||
|
|
||||||
override val testConfig: Config = Settings.withRandomDatabase
|
|
||||||
|
|
||||||
private val route = new StatusEndpoint().routes
|
private val route = new StatusEndpoint().routes
|
||||||
|
|
||||||
"api v1 status endpoint" must {
|
"api v1 status endpoint" must {
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
package me.arcanis.ffxivbis.http.view
|
||||||
|
|
||||||
|
import akka.http.scaladsl.model.{StatusCodes, Uri}
|
||||||
|
import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials}
|
||||||
|
import akka.http.scaladsl.testkit.ScalatestRouteTest
|
||||||
|
import me.arcanis.ffxivbis.Fixtures
|
||||||
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
|
class RootViewTest extends AnyWordSpecLike with Matchers with ScalatestRouteTest {
|
||||||
|
|
||||||
|
private val auth =
|
||||||
|
Authorization(BasicHttpCredentials(Fixtures.userAdmin.username, Fixtures.userPassword))
|
||||||
|
|
||||||
|
private val route = new RootView(Fixtures.authProvider).routes
|
||||||
|
|
||||||
|
"html view endpoint" must {
|
||||||
|
|
||||||
|
"return root view" in {
|
||||||
|
Get(Uri("/")) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return root party view" in {
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return bis view" in {
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}/bis")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return loot view" in {
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}/loot")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"return users view" in {
|
||||||
|
Get(Uri(s"/party/${Fixtures.partyId}/users")).withHeaders(auth) ~> route ~> check {
|
||||||
|
status shouldEqual StatusCodes.OK
|
||||||
|
responseAs[String] should not be empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -6,5 +6,7 @@ import java.time.Instant
|
|||||||
import scala.language.implicitConversions
|
import scala.language.implicitConversions
|
||||||
|
|
||||||
object Converters {
|
object Converters {
|
||||||
|
|
||||||
implicit def pieceToLoot(piece: Piece): Loot = Loot(-1, piece, Instant.ofEpochMilli(0), isFreeLoot = false)
|
implicit def pieceToLoot(piece: Piece): Loot = Loot(-1, piece, Instant.ofEpochMilli(0), isFreeLoot = false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user