PSF-LoginServer/common/src/test/scala/objects/RepairableTest.scala
Jakob Gillich e0defe8240
Persistence #1 featuring quill (#508)
* Add .scalafmt.conf

* Adopt quill for database access

* Removed postgresql-async
* Refactored all instances of database access
* Creating duplicate characters of the same account is no longer possible
* Rewrote large parts of LoginSessionActor

* Implement migrations

* Move overrides into subdirectory

* Make usernames case insensitive

* Use LOWER(?) comparison instead of storing lowercased username

* import scala.util.{Success, Failure}

* Add config and joda-time dependencies

* Add sbt-scalafmt

* Use defaultWithAlign scalafmt preset

* Format all

* Add scalafix

* Remove unused imports

* Don't lowercase username when inserting

* Update readme

* Listen on worldserver.Hostname address

* Remove database test on startup

It could fail when the global thread pool is busy loading zone
maps. Migrations run on the main thread and also serve the
purpose of verifying the database configuration so it's fine to
remove the test altogether.

* Refactor chat message handlers, zones

What started as a small change to how zones are stored turned
into a pretty big effort of refactoring the chat message handler.
The !hack command was removed, the /capturebase commandwas added.

* Expose db ports in docker-compose.yml

* Silence property override log

* Rework configuration

* Unify configuration using the typesafe.config library
* Add configuration option for public address
* Configuration is now loaded from application.conf rather than worldserver.ini
* Refactor PsLogin and remove unnecessary logging
* Move pslogin into net.psforever.pslogin namespace

* Fix coverage
2020-07-13 23:54:05 -04:00

440 lines
16 KiB
Scala

// Copyright (c) 2020 PSForever
package objects
import akka.actor.Props
import akka.testkit.TestProbe
import base.ActorTest
import net.psforever.objects._
import net.psforever.objects.guid.NumberPoolHub
import net.psforever.objects.guid.source.LimitedNumberSource
import net.psforever.objects.serverobject.CommonMessages
import net.psforever.objects.serverobject.generator.{Generator, GeneratorControl}
import net.psforever.objects.serverobject.structures.{Building, StructureType}
import net.psforever.objects.serverobject.terminals.{Terminal, TerminalControl}
import net.psforever.objects.serverobject.turret.{FacilityTurret, FacilityTurretControl}
import net.psforever.objects.vehicles.VehicleControl
import net.psforever.objects.zones.{Zone, ZoneMap}
import net.psforever.packet.game.{InventoryStateMessage, RepairMessage}
import net.psforever.types._
import services.avatar.{AvatarAction, AvatarServiceMessage}
import services.vehicle.{VehicleAction, VehicleServiceMessage}
import scala.concurrent.duration._
/*
the generator is used to test basic entity repair
essentially, treat it more as a generic entity whose object type is repairable
see GeneratorTest in relation to what the generator does above and beyond that during repair
*/
class RepairableEntityRepairTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val gen = Generator(GlobalDefinitions.generator) //guid=2
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"RepairableEntity" should {
"handle repairs" in {
assert(gen.Health == gen.Definition.DefaultHealth) //ideal
val originalHealth = gen.Health -= 50
assert(gen.Health < gen.Definition.DefaultHealth) //damage
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair
val msg123 = avatarProbe.receiveN(3, 500 milliseconds)
assert(
msg123.head match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction
.SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(5), _, PlanetSideGUID(4), _))
) =>
true
case _ => false
}
)
assert(
msg123(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg123(2) match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _))
) =>
true
case _ => false
}
)
assert(originalHealth < gen.Health) //generator repaired a bit
}
}
}
class RepairableEntityNotRepairTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val gen = Generator(GlobalDefinitions.generator) //guid=2
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
guid.register(building, 1)
guid.register(gen, 2)
guid.register(player1, 3)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = gen
gen.Position = Vector3(1, 0, 0)
gen.Actor = system.actorOf(Props(classOf[GeneratorControl], gen), "generator-control")
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"RepairableEntity" should {
"not repair if health is already full" in {
assert(gen.Health == gen.Definition.DefaultHealth) //ideal
gen.Actor ! CommonMessages.Use(player1, Some(tool)) //repair?
avatarProbe.expectNoMessage(1000 milliseconds) //no messages
}
}
}
class RepairableAmenityTest extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val term = Terminal(GlobalDefinitions.order_terminal) //guid=2
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
guid.register(building, 1)
guid.register(term, 2)
guid.register(player1, 3)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = term
term.Position = Vector3(1, 0, 0)
term.Actor = system.actorOf(Props(classOf[TerminalControl], term), "terminal-control")
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
building.Actor = buildingProbe.ref
val tool = Tool(GlobalDefinitions.nano_dispenser) //4 & 5
guid.register(tool, 4)
guid.register(tool.AmmoSlot.Box, 5)
expectNoMessage(200 milliseconds)
//we're not testing that the math is correct
"RepairableAmenity" should {
"send initialization messages upon restoration" in {
//the decimator does enough damage to one-shot this terminal from any initial health
val originalHealth = term.Health = term.Definition.RepairRestoresAt - 1 //initial state manip
term.Destroyed = true
assert(originalHealth < term.Definition.RepairRestoresAt)
assert(term.Destroyed)
term.Actor ! CommonMessages.Use(player1, Some(tool))
val msg12345 = avatarProbe.receiveN(5, 500 milliseconds)
assert(
msg12345.head match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction
.SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(5), _, PlanetSideGUID(4), _))
) =>
true
case _ => false
}
)
assert(
msg12345(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
assert(
msg12345(2) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 50, 0)) => true
case _ => false
}
)
assert(
msg12345(3) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 51, 0)) => true
case _ => false
}
)
assert(
msg12345(4) match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _))
) =>
true
case _ => false
}
)
assert(term.Health > term.Definition.RepairRestoresAt)
assert(!term.Destroyed)
}
}
}
class RepairableTurretWeapon extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val building = Building("test-building", 1, 1, zone, StructureType.Facility) //guid=1
val activityProbe = TestProbe()
val avatarProbe = TestProbe()
val vehicleProbe = TestProbe()
val buildingProbe = TestProbe()
zone.Activity = activityProbe.ref
zone.AvatarEvents = avatarProbe.ref
zone.VehicleEvents = vehicleProbe.ref
building.Actor = buildingProbe.ref
val turret = new FacilityTurret(GlobalDefinitions.manned_turret) //2, 5, 6
turret.Actor = system.actorOf(Props(classOf[FacilityTurretControl], turret), "turret-control")
turret.Zone = zone
turret.Position = Vector3(1, 0, 0)
val turretWeapon = turret.Weapons.values.head.Equipment.get.asInstanceOf[Tool]
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=3
player1.Spawn
player1.Position = Vector3(2, 2, 2)
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
guid.register(building, 1)
guid.register(turret, 2)
guid.register(player1, 3)
guid.register(turretWeapon, 5)
guid.register(turretWeapon.AmmoSlot.Box, 6)
building.Position = Vector3(1, 0, 0)
building.Zone = zone
building.Amenities = turret
val tool = Tool(GlobalDefinitions.nano_dispenser) //7 & 8
guid.register(tool, 7)
guid.register(tool.AmmoSlot.Box, 8)
"RepairableTurretWeapon" should {
"handle repairs and restoration" in {
turret.Health = turret.Definition.RepairRestoresAt - 1 //initial state manip
turret.Destroyed = true //initial state manip
assert(turret.Health < turret.Definition.RepairRestoresAt)
assert(turret.Destroyed)
turret.Actor ! CommonMessages.Use(player1, Some(tool))
val msg12345 = avatarProbe.receiveN(5, 500 milliseconds)
val msg4 = vehicleProbe.receiveOne(500 milliseconds)
assert(
msg12345.head match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction
.SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(8), _, PlanetSideGUID(7), _))
) =>
true
case _ => false
}
)
assert(
msg12345(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(2), 0, _)) => true
case _ => false
}
)
//msg12345(2) and msg12345(3) are related to RepairableAmenity
assert(
msg12345(4) match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(2), _))
) =>
true
case _ => false
}
)
assert(
msg4 match {
case VehicleServiceMessage("test", VehicleAction.EquipmentInSlot(_, PlanetSideGUID(2), 1, t))
if t eq turretWeapon =>
true
case _ => false
}
)
assert(turret.Health > turret.Definition.RepairRestoresAt)
assert(!turret.Destroyed)
}
}
}
class RepairableVehicleRepair extends ActorTest {
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1, 2, 3
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "vehicle-control")
atv.Position = Vector3(1, 0, 0)
val atvWeapon = atv.Weapons(1).Equipment.get.asInstanceOf[Tool]
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=4
player1.Spawn
player1.Position = Vector3(2, 2, 2)
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
guid.register(atv, 1)
guid.register(atvWeapon, 2)
guid.register(atvWeapon.AmmoSlot.Box, 3)
guid.register(player1, 4)
atv.Zone = zone
val tool = Tool(GlobalDefinitions.nano_dispenser) //5 & 6
guid.register(tool, 5)
guid.register(tool.AmmoSlot.Box, 6)
"RepairableVehicle" should {
"handle repairs" in {
val originalHealth = atv.Health = atv.Definition.DamageDestroysAt + 1 //initial state manip
assert(atv.Health == originalHealth)
atv.Actor ! CommonMessages.Use(player1, Some(tool))
val msg123 = avatarProbe.receiveN(3, 500 milliseconds)
assert(
msg123.head match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction
.SendResponse(PlanetSideGUID(0), InventoryStateMessage(PlanetSideGUID(6), _, PlanetSideGUID(5), _))
) =>
true
case _ => false
}
)
assert(
msg123(1) match {
case AvatarServiceMessage("test", AvatarAction.PlanetsideAttributeToAll(PlanetSideGUID(1), 0, _)) => true
case _ => false
}
)
assert(
msg123(2) match {
case AvatarServiceMessage(
"TestCharacter1",
AvatarAction.SendResponse(PlanetSideGUID(0), RepairMessage(PlanetSideGUID(1), _))
) =>
true
case _ => false
}
)
assert(atv.Health > originalHealth)
}
}
}
class RepairableVehicleRestoration extends ActorTest {
/*
no messages are dispatched, in this case, because most vehicles are flagged to not be repairable if destroyed
*/
val guid = new NumberPoolHub(new LimitedNumberSource(10))
val zone = new Zone("test", new ZoneMap("test"), 0) {
override def SetupNumberPools() = {}
GUID(guid)
}
val avatarProbe = TestProbe()
zone.AvatarEvents = avatarProbe.ref
val atv = Vehicle(GlobalDefinitions.quadassault) //guid=1, 2, 3
atv.Actor = system.actorOf(Props(classOf[VehicleControl], atv), "vehicle-control")
atv.Position = Vector3(1, 0, 0)
val atvWeapon = atv.Weapons(1).Equipment.get.asInstanceOf[Tool]
val player1 =
Player(Avatar("TestCharacter1", PlanetSideEmpire.TR, CharacterGender.Male, 0, CharacterVoice.Mute)) //guid=4
player1.Spawn
player1.Position = Vector3(2, 2, 2)
val player1Probe = TestProbe()
player1.Actor = player1Probe.ref
guid.register(atv, 1)
guid.register(atvWeapon, 2)
guid.register(atvWeapon.AmmoSlot.Box, 3)
guid.register(player1, 4)
atv.Zone = zone
val tool = Tool(GlobalDefinitions.nano_dispenser) //5 & 6
guid.register(tool, 5)
guid.register(tool.AmmoSlot.Box, 6)
"RepairableVehicle" should {
"will not restore a destroyed vehicle to working order" in {
atv.Health = atv.Definition.DamageDestroysAt - 1 //initial state manip
atv.Destroyed = true //initial state manip
assert(atv.Health <= atv.Definition.DamageDestroysAt)
assert(atv.Destroyed)
atv.Actor ! CommonMessages.Use(player1, Some(tool))
avatarProbe.expectNoMessage(500 milliseconds)
assert(atv.Health == 0) //set to zero explicitly
assert(atv.Destroyed)
}
}
}
object RepairableTest {}