17 Commits

Author SHA1 Message Date
Vassyli 1e900d8ec7 Actions have now an additional variable 'action' available, which can access the action's parameters. Render accepts additional template values. 2021-04-08 17:50:33 +02:00
Vassyli 30b9d981fa Added comment and a test for viewpoint data fields. 2021-04-08 17:50:33 +02:00
Vassyli 9019caaafc Viewpoint propagates itself to Actions and ActionGroups. 2021-04-08 17:50:33 +02:00
Vassyli 31eca85df4 Fix: Adjusted tests. 2021-03-01 20:54:30 +01:00
Vassyli 8d43b3d2ce Fix: renamed two events from character-config to scene-config. 2021-03-01 20:54:30 +01:00
Vassyli a9e5647fe0 Adds the missing initialisation of the Scene.properties collection with corresponding tests. 2021-02-26 14:32:59 +01:00
Vassyli 25ccbd8974 Adds default value to type Scene.properties. 2021-02-25 16:46:42 +01:00
Vassyli 2e9f0dc564 Sets cli column width to 120 to standardise testing environment 2021-02-16 22:13:52 +01:00
Vassyli 03fde83fc1 Fixes the actual command to import the correct commands. 2021-02-16 22:13:52 +01:00
Vassyli 669085e65d Added the possibilities to set character and scene settings over cli, with module hooks. 2021-02-16 22:13:52 +01:00
Vassyli f72adb1a38 Adds tests for scene commands. 2021-02-16 22:13:52 +01:00
Vassyli 0292a66252 Adds new commands to manage modules and corresponding tests. 2021-02-16 22:13:52 +01:00
Vassyli eea72f848b Adds tests for all character commands. 2021-02-16 22:13:52 +01:00
Vassyli c2a139d192 Adds a new CharacterEditCommand including a test. 2021-02-16 22:13:52 +01:00
Vassyli d83e437b4a Removed the same wrong assumption from LotGDTestCase, too. 2021-02-04 16:37:07 +01:00
Vassyli 99d554e5d7 Fixes a bug in HasAction->searchAction that caused actions not to be found if no groupTitle was given. 2021-02-04 16:37:07 +01:00
Vassyli 75dd7494c3 Fixes a bug in Action->setTitle() where a return type was expected instead of void. 2021-02-04 12:03:43 +01:00
79 changed files with 4738 additions and 399 deletions
+1
View File
@@ -18,6 +18,7 @@
"gedmo/doctrine-extensions": "^2.3|^3.0",
"doctrine/orm": "^2.8",
"doctrine/common": "^3.0",
"doctrine/dbal": "^2.12 <2.13",
"monolog/monolog": "^2.0",
"symfony/console": "^5.0",
"symfony/yaml": "^5.0",
Generated
+194 -200
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -1,6 +1,7 @@
<phpunit bootstrap="bootstrap/bootstrap.php">
<php>
<env name="LOTGD_TESTS_CONFIG_PATH" value="./config/lotgd.yml"/>
<env name="COLUMNS" value="120" />
</php>
<testsuites>
<testsuite name="All">
+59 -3
View File
@@ -3,13 +3,16 @@ declare(strict_types=1);
namespace LotGD\Core;
use LotGD\Core\Models\Viewpoint;
/**
* A representation of an action the user can take to affect the game
* state. An encapsulation of a navigation menu option.
*/
class Action
class Action implements \Serializable
{
protected string $id;
private ?Viewpoint $viewpoint = null;
/**
* Construct a new action with the specified Scene as its destination.
@@ -25,6 +28,41 @@ class Action
$this->id = \bin2hex(\random_bytes(8));
}
public function serialize()
{
return serialize([
"id" => $this->id,
"destinationSceneId" => $this->destinationSceneId,
"title" => $this->title,
"parameters" => $this->parameters,
]);
}
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->id = $data["id"];
$this->destinationSceneId = $data["destinationSceneId"];
$this->title = $data["title"];
$this->parameters = $data["parameters"];
}
/**
* @param Viewpoint|null $viewpoint
*/
public function setViewpoint(?Viewpoint $viewpoint)
{
$this->viewpoint = $viewpoint;
}
/**
* @return Viewpoint|null
*/
public function getViewpoint(): ?Viewpoint
{
return $this->viewpoint;
}
/**
* Returns the unique, automatically generated identifier for this action.
* Use this ID to refer to this action when calling Game::takeAction().
@@ -54,10 +92,28 @@ class Action
}
/**
* @param string|null $title
* Returns the rendered action title.
* @return string|null
* @throws Exceptions\InsecureTwigTemplateError
*/
public function setTitle(?string $title): ?string
public function getRenderedTitle(): ?string
{
$title = $this->getTitle();
$sceneRenderer = $this->getViewpoint()?->getTwigSceneRenderer();
if (!$title) {
return null;
} elseif ($sceneRenderer) {
return $sceneRenderer->render($title, $this->viewpoint, ignoreErrors: true, templateValues: ["Action" => $this]);
} else {
return $title;
}
}
/**
* @param string|null $title
*/
public function setTitle(?string $title): void
{
$this->title = $title;
}
+69 -2
View File
@@ -3,15 +3,21 @@ declare(strict_types=1);
namespace LotGD\Core;
use LotGD\Core\Models\Viewpoint;
/**
* A grouping of navigation actions, like a submenu.
*/
class ActionGroup implements \Countable
class ActionGroup implements \Countable, \Serializable
{
const DefaultGroup = 'lotgd/core/default';
const HiddenGroup = 'lotgd/core/hidden';
private $actions;
/**
* @var Action[]
*/
private array $actions;
private ?Viewpoint $viewpoint = null;
/**
* Create a new ActionGroup, starting with an empty set of actions.
@@ -27,6 +33,45 @@ class ActionGroup implements \Countable
$this->actions = [];
}
public function serialize()
{
return serialize([
"id" => $this->id,
"title" => $this->title,
"actions" => $this->actions,
"sortKey" => $this->sortKey,
]);
}
public function unserialize($serialized)
{
$data = unserialize($serialized);
$this->id = $data["id"];
$this->title = $data["title"];
$this->actions = $data["actions"];
$this->sortKey = $data["sortKey"];
}
/**
* @param Viewpoint|null $viewpoint
*/
public function setViewpoint(?Viewpoint $viewpoint)
{
$this->viewpoint = $viewpoint;
foreach ($this->actions as $action) {
$action->setViewpoint($viewpoint);
}
}
/**
* @return Viewpoint|null
*/
public function getViewpoint(): ?Viewpoint
{
return $this->viewpoint;
}
/**
* Returns the number of registered Actions for this group.
* @return int
@@ -54,6 +99,23 @@ class ActionGroup implements \Countable
return $this->title;
}
/**
* Returns the rendered title for this group.
* @return string
* @throws Exceptions\InsecureTwigTemplateError
*/
public function getRenderedTitle(): string
{
$title = $this->getTitle();
$sceneRenderer = $this->viewpoint?->getTwigSceneRenderer();
if ($sceneRenderer) {
return $sceneRenderer->render($title, $this->viewpoint, ignoreErrors: true);
} else {
return $title;
}
}
/**
* Returns the sort key for this group. The ordering of the groups in the
* final menu displayed to users will be sorted by this key. The default
@@ -80,6 +142,10 @@ class ActionGroup implements \Countable
*/
public function setActions(array $actions)
{
foreach ($actions as $action) {
$action->setViewpoint($this->viewpoint);
}
$this->actions = $actions;
}
@@ -89,6 +155,7 @@ class ActionGroup implements \Countable
*/
public function addAction(Action $action)
{
$action->setViewpoint($this->viewpoint);
$this->actions[] = $action;
}
}
+21 -2
View File
@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command;
use LotGD\Core\Game;
use Monolog\Logger;
use Symfony\Component\Console\Command\Command;
/**
@@ -12,7 +12,8 @@ use Symfony\Component\Console\Command\Command;
*/
abstract class BaseCommand extends Command
{
protected $game;
protected Game $game;
protected ?string $namespace = null;
/**
* Construct the command, using the provided Game.
@@ -23,4 +24,22 @@ abstract class BaseCommand extends Command
parent::__construct();
$this->game = $game;
}
/**
* Returns a cloned logger with a different context name.
* @return Logger
*/
public function getCliLogger(): Logger
{
return $this->game->getLogger()->withName("daenerys-cli");
}
protected function namespaced(string $command): string
{
if ($this->namespace) {
return "{$this->namespace}:{$command}";
} else {
return $command;
}
}
}
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Character;
use Exception;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Character;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@@ -17,15 +16,15 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class CharacterAddCommand extends BaseCommand
class CharacterAddCommand extends CharacterBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('character:add')
->setDescription('Add a character.')
$this->setName($this->namespaced("add"))
->setDescription("Add a character.")
->setDefinition(
new InputDefinition([
new InputArgument(
@@ -56,8 +55,7 @@ class CharacterAddCommand extends BaseCommand
protected function execute(InputInterface $input, OutputInterface $output): int
{
$em = $this->game->getEntityManager();
$logger = $this->game->getLogger();
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
$name = $input->getArgument("name");
@@ -76,6 +74,13 @@ class CharacterAddCommand extends BaseCommand
$maxHealth = intval($maxHealth);
}
if ($maxHealth < 0) {
$io->error("Maximum health must be at least 0.");
return Command::FAILURE;
} elseif ($maxHealth === 0) {
$io->warning("The character will have 0 max health and will be permanently dead.");
}
$character = Character::createAtFullHealth([
"name" => $name,
"level" => $level,
@@ -93,7 +98,7 @@ class CharacterAddCommand extends BaseCommand
}
$io->success("{$character} was successfully created.");
$logger->info("{$character} was created.");
$logger->info("Character was created.", ["character" => $character]);
return Command::SUCCESS;
}
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Character;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Character;
use Symfony\Component\Console\Input\InputArgument;
class CharacterBaseCommand extends BaseCommand
{
protected ?string $namespace = "character";
/**
* @return InputArgument
*/
protected function getCharacterIdArgumentDefinition(): InputArgument
{
return new InputArgument(
name: "id",
mode: InputArgument::REQUIRED,
description: "Character ID",
);
}
/**
* @param string $id
* @return Character|null
*/
protected function getCharacter(string $id): ?Character
{
/** @var Character|null $character */
$character = $this->game->getEntityManager()->getRepository(Character::class)->find($id);
return $character;
}
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Character;
use LotGD\Core\Events\EventContextData;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class CharacterConfigListCommand extends CharacterBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("config:list"))
->setDescription('List available settings for a character')
->setDefinition([
$this->getCharacterIdArgumentDefinition(),
])
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$character = $this->getCharacter($input->getArgument("id"));
if (!$character) {
$io->error("Character was not found.");
return Command::FAILURE;
}
// Create hook
$context = EventContextData::create([
"character" => $character,
"io" => $io,
"settings" => [],
]);
$newContext = $this->game->getEventManager()->publish(
event: "h/lotgd/core/cli/character-config-list",
contextData: $context
);
$settings = $newContext->get("settings");
$io->title("Character ".$character->getDisplayName());
if (count($settings) === 0) {
$io->note("There are no character settings available.");
} else {
$io->table(["setting", "value", "description"], $settings);
}
return Command::SUCCESS;
}
}
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Character;
use LotGD\Core\Events\EventContextData;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class CharacterConfigResetCommand extends CharacterBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("config:reset"))
->setDescription('Reset a character setting')
->setDefinition([
$this->getCharacterIdArgumentDefinition(),
new InputArgument(
"setting",
mode: InputArgument::REQUIRED,
description: "Name of setting, see {$this->namespaced('config:list')}.",
),
])
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
$character = $this->getCharacter($input->getArgument("id"));
if (!$character) {
$io->error("Module was not found.");
return Command::FAILURE;
}
$io->title("Character {$character->getDisplayName()}");
// Create hook
$context = EventContextData::create([
"character" => $character,
"io" => $io,
"setting" => $input->getArgument("setting"),
"return" => Command::FAILURE,
"reason" => "Setting does not exist.",
]);
$newContext = $this->game->getEventManager()->publish(
event: "h/lotgd/core/cli/character-config-reset",
contextData: $context
);
if ($newContext->get("return") != Command::SUCCESS) {
$io->error($newContext->get("reason"));
return Command::FAILURE;
}
$this->game->getEntityManager()->flush();
return Command::SUCCESS;
}
}
@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Character;
use LotGD\Core\Events\EventContextData;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class CharacterConfigSetCommand extends CharacterBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("config:set"))
->setDescription('Change a character setting')
->setDefinition([
$this->getCharacterIdArgumentDefinition(),
new InputArgument(
"setting",
mode: InputArgument::REQUIRED,
description: "Name of setting, see {$this->namespaced('config:list')}.",
),
new InputArgument(
"value",
InputArgument::REQUIRED,
description: "New value for the given setting.",
),
])
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
$character = $this->getCharacter($input->getArgument("id"));
if (!$character) {
$io->error("Module was not found.");
return Command::FAILURE;
}
$io->title("Character {$character->getDisplayName()}");
// Create hook
$context = EventContextData::create([
"character" => $character,
"io" => $io,
"setting" => $input->getArgument("setting"),
"value" => $input->getArgument("value"),
"return" => Command::FAILURE,
"reason" => "Setting does not exist.",
]);
$newContext = $this->game->getEventManager()->publish(
event: "h/lotgd/core/cli/character-config-set",
contextData: $context
);
if ($newContext->get("return") != Command::SUCCESS) {
$io->error($newContext->get("reason"));
return Command::FAILURE;
}
$this->game->getEntityManager()->flush();
return Command::SUCCESS;
}
}
@@ -0,0 +1,227 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Character;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class CharacterEditCommand extends CharacterBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("edit"))
->setDescription("Edit a given character.")
->setDefinition(
new InputDefinition([
$this->getCharacterIdArgumentDefinition(),
new InputOption(
"level",
mode: InputOption::VALUE_REQUIRED,
description: "Changes the level"
),
new InputOption(
"maxHealth",
mode: InputOption::VALUE_REQUIRED,
description: "Change maximum amount of health points. Health will be adjusted accordingly."
),
new InputOption(
"heal",
mode: InputOption::VALUE_NONE,
description: "Restores full health"
),
new InputOption(
"revive",
mode: InputOption::VALUE_OPTIONAL,
description: "Revives at full health. Give a number between 0 and 1 to revive fractionally.",
default: false,
),
new InputOption(
"kill",
mode: InputOption::VALUE_NONE,
description: "Kills"
),
])
)
;
}
/**
* @inheritDoc
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$em = $this->game->getEntityManager();
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
$id = $input->getArgument("id");
$level = $input->getOption("level");
$heal = $input->getOption("heal");
$revive = $input->getOption("revive");
$kill = $input->getOption("kill");
$maxHealth = $input->getOption("maxHealth");
$changed = false;
// Find character
$character = $this->getCharacter($id);
if (!$character) {
$io->error("The character with the id {$id} was not found.");
return Command::FAILURE;
}
// Change level
if ($level !== null) {
$level = intval($level);
if ($level <= 0) {
$io->error("Cannot set the level below 1.");
return Command::FAILURE;
}
// Only change level if necessary
if ($character->getLevel() !== $level) {
// Log
$logger->info("Character level changed", [
"for" => $character,
"from" => $character->getLevel(),
"to" => $level
]);
// Change
$character->setLevel($level);
$changed = true;
}
}
// Heal
if ($heal) {
if ($character->getHealth() >= $character->getMaxHealth()) {
$io->note("Character is already at full health.");
} elseif ($character->isAlive()) {
$oldHealth = $character->getHealth();
// Log
$logger->info("Character health changed", [
"for" => $character,
"from" => $oldHealth,
"to" => $character->getMaxHealth()
]);
// Change
$character->setHealth($character->getMaxHealth());
$io->success("Character was restored to full health ({$oldHealth} to {$character->getMaxHealth()}).");
$changed = true;
} else {
$io->error("Cannot heal a dead character. Use --revive instead.");
return Command::FAILURE;
}
}
// Revive the character
if ($revive !== false) {
// Make sure we revive between 0 and 1
if ($revive === null) {
$revive = 1;
} elseif (str_ends_with($revive, "%")) {
$revive = min(floatval($revive)/100, 1);
} else {
$revive = min(floatval($revive), 1);
}
if ($character->isAlive()) {
$io->error("Character is already alive. Use --heal instead.");
return Command::FAILURE;
} else {
// Make sure we heal at least by 1.
$reviveAmount = (int)round(max($revive * $character->getMaxHealth(), 1), 0);
// Log
$logger->info("Character was revived", [
"for" => $character,
"to" => $reviveAmount
]);
// Change
$character->setHealth($reviveAmount);
$io->success("Character was revived with {$reviveAmount} of health points "
."(max: {$character->getMaxHealth()}).");
$changed = true;
}
}
if ($kill) {
if (!$character->isAlive()) {
$io->error("What is dead may never die.");
return Command::FAILURE;
} else {
// Log
$logger->info("Character was killed", ["for" => $character]);
// Change
$character->setHealth(0);
$io->success("Character was killed.");
$changed = true;
}
}
if ($maxHealth) {
$maxHealth = intval($maxHealth);
if ($maxHealth < 0) {
$io->error("Cannot set maximum health below 0.");
return Command::FAILURE;
}
if ($character->getMaxHealth() === 0) {
$healthProportion = 0;
} else {
$healthProportion = $character->getHealth() / $character->getMaxHealth();
}
// Log
$logger->info("Character maxHealth changed", [
"for" => $character,
"from" => $character->getMaxHealth(),
"to" => $maxHealth
]);
// Change
$character->setMaxHealth($maxHealth);
$character->setHealth((int)round($healthProportion*$maxHealth, 0));
$io->success("Character has new maximum health of {$maxHealth} (current health is {$character->getHealth()}).");
$changed = true;
}
// Save changes
if ($changed) {
try {
$em->flush();
$io->success("The character was successfully changed.");
// Log
$logger->info("Changed committed.");
} catch (Exception $e) {
$io->error("Character could not be saved. Reason: {$e}");
$logger->error("Changes rolled back.", ["exception" => $e]);
return Command::FAILURE;
}
} else {
$io->note("Nothing was changed.");
}
return Command::SUCCESS;
}
}
@@ -3,25 +3,41 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Character;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Character;
use LotGD\Core\Models\Repositories\CharacterRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Command to list all characters.
*/
class CharacterListCommand extends BaseCommand
class CharacterListCommand extends CharacterBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('character:list')
$this->setName($this->namespaced("list"))
->setDescription('Lists all characters')
->setDefinition(
new InputDefinition([
new InputOption(
"includeSoftDeleted",
mode: InputOption::VALUE_NONE,
description: "Includes soft-deleted characters",
),
new InputOption(
"onlySoftDeleted",
mode: InputOption::VALUE_NONE,
description: "Displays only soft-deleted characters",
),
])
)
;
}
@@ -33,13 +49,27 @@ class CharacterListCommand extends BaseCommand
$em = $this->game->getEntityManager();
$io = new SymfonyStyle($input, $output);
$characters = $em->getRepository(Character::class)->findAll();
/** @var CharacterRepository $repository */
$repository = $em->getRepository(Character::class);
$marker = "";
if ($input->getOption("includeSoftDeleted")) {
$io->writeln("* marks soft-deleted characters");
$marker = "*";
$characters = $repository->findAll(CharacterRepository::INCLUDE_SOFTDELETED);
} elseif ($input->getOption("onlySoftDeleted")) {
$io->writeln("Only soft-deleted characters are shown");
$characters = $repository->findAll(CharacterRepository::ONLY_SOFTDELETED);
} else {
$characters = $repository->findAll();
}
$table = [["id", "name", "level"], []];
foreach ($characters as $character) {
$table[1][] = [
$character->getId(),
$character->getDisplayName(),
$marker.$character->getName(),
$character->getLevel(),
];
}
@@ -4,33 +4,35 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Character;
use Exception;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Character;
use LotGD\Core\Models\Repositories\CharacterRepository;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class CharacterRemoveCommand extends BaseCommand
class CharacterRemoveCommand extends CharacterBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('character:remove')
->setDescription('Definitely removes a character (no soft delete).')
$this->setName($this->namespaced("remove"))
->setDescription("Definitely removes a character (no soft delete).")
->setDefinition(
new InputDefinition([
new InputArgument(
"id",
mode: InputArgument::REQUIRED,
description: "Character ID",
$this->getCharacterIdArgumentDefinition(),
new InputOption(
name: "soft",
mode: InputOption::VALUE_NONE,
description: "Only removes the character softly (soft delete)."
),
])
)
@@ -43,7 +45,9 @@ class CharacterRemoveCommand extends BaseCommand
protected function execute(InputInterface $input, OutputInterface $output): int
{
$em = $this->game->getEntityManager();
$logger = $this->game->getLogger();
/** @var CharacterRepository $characterRepository */
$characterRepository = $em->getRepository(Character::class);
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
@@ -51,25 +55,40 @@ class CharacterRemoveCommand extends BaseCommand
// Find character
/** @var Character $character */
$character = $em->getRepository(Character::class)->find($id);
$character = $characterRepository->findWithSoftDeleted($id);
if (!$character) {
$io->error("The character with the id {$id} was not found.");
return Command::FAILURE;
}
try {
$em->remove($character);
// Commit changes
$em->flush();
} catch (Exception $e) {
$io->error("Removing {$character} was not possible. Reason: {$e->getMessage()}.");
return Command::FAILURE;
if ($character->isDeleted()) {
$io->info("Character was marked as soft-deleted.");
}
$io->success("{$character} was successfully removed.");
$logger->info("{$character} was removed.");
try {
if ($input->getOption("soft")) {
// Only soft-delete if requested
$character->delete($em);
// Commit changes
$em->flush();
$io->success("{$character} was successfully soft-deleted.");
$logger->info("Character was soft-deleted.", ["character" => $character]);
} else {
$em->remove($character);
// Commit changes
$em->flush();
$io->success("{$character} was successfully removed.");
$logger->info("Character was removed.", ["character" => $character]);
}
} catch (Exception $e) {
$io->error("Removing {$character} was not possible. Reason: {$e}.");
return Command::FAILURE;
}
return Command::SUCCESS;
}
@@ -3,32 +3,31 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Character;
use LotGD\Core\Console\Command\BaseCommand;
use Exception;
use LotGD\Core\Models\Character;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class CharacterResetViewpointCommand extends BaseCommand
class CharacterResetViewpointCommand extends CharacterBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('character:resetViewpoint')
->setDescription('Resets the viewpoint of a given character.')
$this->setName($this->namespaced("resetViewpoint"))
->setDescription("Resets the viewpoint of a given character.")
->setDefinition(
new InputDefinition([
new InputArgument("id", InputArgument::REQUIRED, "ID of the character"),
])
$this->getCharacterIdArgumentDefinition(),
]),
)
;
}
@@ -39,6 +38,7 @@ class CharacterResetViewpointCommand extends BaseCommand
protected function execute(InputInterface $input, OutputInterface $output): int
{
$em = $this->game->getEntityManager();
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
$id = $input->getArgument("id");
@@ -51,11 +51,22 @@ class CharacterResetViewpointCommand extends BaseCommand
return Command::FAILURE;
}
$em->remove($character->getViewpoint());
$character->setViewpoint(null);
if ($character->getViewpoint() === null) {
$io->info("Character does not have a viewpoint yet.");
} else {
try {
$em->remove($character->getViewpoint());
$character->setViewpoint(null);
# Save
$em->flush();
$io->success("Viewpoint was successfully reset.");
# Save
$em->flush();
} catch (Exception $e) {
$io->error("Resetting the viewpoint was not possible. Reason: {$e}");
return Command::FAILURE;
}
}
return Command::SUCCESS;
}
@@ -18,26 +18,22 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class CharacterShowCommand extends BaseCommand
class CharacterShowCommand extends CharacterBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('character:show')
->setDescription('Shows details about character.')
$this->setName($this->namespaced("show"))
->setDescription("Shows details about character.")
->setDefinition(
new InputDefinition([
new InputArgument(
"id",
mode: InputArgument::REQUIRED,
description: "Character ID",
),
$this->getCharacterIdArgumentDefinition(),
new InputOption(
"onlyViewpoint",
mode: InputOption::VALUE_NONE,
description: "Set to true to only display viewpoint",
description: "Set to only display viewpoint",
)
])
)
@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Module;
use Doctrine\Persistence\ObjectRepository;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Module;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
class ModuleBaseCommand extends BaseCommand
{
protected ?string $namespace = "module";
/**
* @return InputArgument
*/
protected function getModuleNameArgumentDefinition(): InputArgument
{
return new InputArgument(
name: "moduleName",
mode: InputArgument::REQUIRED,
description: "Name of the module, in vendor/package format",
);
}
protected function getModuleRepository(): ObjectRepository
{
return $this->game->getEntityManager()->getRepository(Module::class);
}
protected function getModuleModel(InputInterface $input): ?Module
{
return $this->game->getModuleManager()->getModule($input->getArgument("moduleName"));
}
}
@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Module;
use LotGD\Core\Events\EventContextData;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class ModuleConfigListCommand extends ModuleBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("config:list"))
->setDescription('List available configuration option for a module')
->setDefinition([
$this->getModuleNameArgumentDefinition(),
])
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$module = $this->getModuleModel($input);
if (!$module) {
$io->error("Module was not found.");
return Command::FAILURE;
}
// Create hook
$context = EventContextData::create([
"module" => $module,
"io" => $io,
"settings" => [],
]);
$newContext = $this->game->getEventManager()->publish(
event: "h/lotgd/core/cli/module-config-list/".$module->getLibrary(),
contextData: $context
);
$settings = $newContext->get("settings");
$io->title("Module ".$module->getLibrary());
if (count($settings) === 0) {
$io->note("This module does not provide any settings.");
} else {
$io->table(["setting", "value", "description"], $settings);
}
return Command::SUCCESS;
}
}
@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Module;
use LotGD\Core\Events\EventContextData;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class ModuleConfigResetCommand extends ModuleBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("config:reset"))
->setDescription('Reset a module setting')
->setDefinition([
$this->getModuleNameArgumentDefinition(),
new InputArgument(
"setting",
mode: InputArgument::REQUIRED,
description: "Name of setting, see {$this->namespaced('config:list')}.",
),
])
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
$module = $this->getModuleModel($input);
if (!$module) {
$io->error("Module was not found.");
return Command::FAILURE;
}
$io->title("Module {$module->getLibrary()}");
// Create hook
$context = EventContextData::create([
"module" => $module,
"io" => $io,
"setting" => $input->getArgument("setting"),
"return" => Command::FAILURE,
"reason" => "Setting does not exist.",
]);
$newContext = $this->game->getEventManager()->publish(
event: "h/lotgd/core/cli/module-config-reset/{$module->getLibrary()}",
contextData: $context
);
if ($newContext->get("return") != Command::SUCCESS) {
$io->error($newContext->get("reason"));
return Command::FAILURE;
}
$this->game->getEntityManager()->flush();
return Command::SUCCESS;
}
}
@@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Module;
use LotGD\Core\Events\EventContextData;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class ModuleConfigSetCommand extends ModuleBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("config:set"))
->setDescription('Change a module setting')
->setDefinition([
$this->getModuleNameArgumentDefinition(),
new InputArgument(
"setting",
mode: InputArgument::REQUIRED,
description: "Name of setting, see {$this->namespaced('config:list')}.",
),
new InputArgument(
"value",
InputArgument::REQUIRED,
description: "New value for the given setting.",
),
])
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
$module = $this->getModuleModel($input);
if (!$module) {
$io->error("Module was not found.");
return Command::FAILURE;
}
$io->title("Module {$module->getLibrary()}");
// Create hook
$context = EventContextData::create([
"module" => $module,
"io" => $io,
"setting" => $input->getArgument("setting"),
"value" => $input->getArgument("value"),
"return" => Command::FAILURE,
"reason" => "Setting does not exist.",
]);
$newContext = $this->game->getEventManager()->publish(
event: "h/lotgd/core/cli/module-config-set/{$module->getLibrary()}",
contextData: $context
);
if ($newContext->get("return") != Command::SUCCESS) {
$io->error($newContext->get("reason"));
return Command::FAILURE;
}
$this->game->getEntityManager()->flush();
return Command::SUCCESS;
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Module;
use LotGD\Core\Models\Module;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class ModuleListCommand extends ModuleBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("list"))
->setDescription('List all installed modules.')
;
}
/**
* @inheritDoc
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
/** @var Module[] $modules */
$modules = $this->game->getModuleManager()->getModules();
$io->title("Installed modules");
if (count($modules) > 0) {
$listing = [];
foreach ($modules as $module) {
$listing[] = [$module->getLibrary() => $module->getCreatedAt()->format("d M Y, H:i")];
}
$io->definitionList(...$listing);
} else {
$io->note("No modules installed.");
}
return Command::SUCCESS;
}
}
@@ -18,14 +18,14 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Danerys command to register and initiate any newly installed modules.
*/
class ModuleRegisterCommand extends BaseCommand
class ModuleRegisterCommand extends ModuleBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('module:register')
$this->setName($this->namespaced("register"))
->setDescription('Register and initialize any newly installed modules')
;
}
@@ -11,14 +11,14 @@ use Symfony\Component\Console\Output\OutputInterface;
/**
* Danerys command to validate installed modules.
*/
class ModuleValidateCommand extends BaseCommand
class ModuleValidateCommand extends ModuleBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('module:validate')
$this->setName($this->namespaced("validate"))
->setDescription('Validate installed modules')
;
}
+9 -10
View File
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use Exception;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Scene;
use LotGD\Core\Models\SceneTemplate;
use Symfony\Component\Console\Command\Command;
@@ -18,15 +17,15 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class SceneAddCommand extends BaseCommand
class SceneAddCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('scene:add')
->setDescription('Add a scene.')
$this->setName($this->namespaced("add"))
->setDescription("Add a scene.")
->setDefinition(
new InputDefinition([
new InputArgument(
@@ -57,7 +56,7 @@ class SceneAddCommand extends BaseCommand
protected function execute(InputInterface $input, OutputInterface $output): int
{
$em = $this->game->getEntityManager();
$logger = $this->game->getLogger();
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
@@ -76,11 +75,11 @@ class SceneAddCommand extends BaseCommand
$template = $templateClass;
}
$scene = Scene::create([
"title" => $title,
"description" => $description,
"template" => $template,
]);
$scene = new Scene(
title: $title,
description: $description,
template: $template
);
try {
$em->persist($scene);
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use Exception;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Exceptions\ArgumentException;
use LotGD\Core\Models\Scene;
use LotGD\Core\Models\SceneConnectionGroup;
@@ -18,18 +17,18 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class SceneAddConnectionGroupCommand extends BaseCommand
class SceneAddConnectionGroupCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('scene:addConnectionGroup')
->setDescription('Add a connection group to an existing scene.')
$this->setName($this->namespaced('addConnectionGroup'))
->setDescription("Add a connection group to an existing scene.")
->setDefinition(
new InputDefinition([
new InputArgument("id", InputArgument::REQUIRED, "ID of the scene"),
$this->getSceneIdArgumentDefinition(),
new InputArgument("groupName", InputArgument::REQUIRED, "Internal id of the group."),
new InputArgument("groupTitle", InputArgument::REQUIRED, "Title of the group (what the character can see"),
]),
@@ -43,7 +42,7 @@ class SceneAddConnectionGroupCommand extends BaseCommand
protected function execute(InputInterface $input, OutputInterface $output): int
{
$em = $this->game->getEntityManager();
$logger = $this->game->getLogger();
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
@@ -56,7 +55,7 @@ class SceneAddConnectionGroupCommand extends BaseCommand
$scene = $em->getRepository(Scene::class)->find($sceneId);
if (!$scene) {
$io->error("The requested scene with the ID {$sceneId} was not found");
$io->error("The requested scene with the ID {$sceneId} was not found.");
return Command::FAILURE;
}
@@ -70,10 +69,12 @@ class SceneAddConnectionGroupCommand extends BaseCommand
// Commit changes
$em->flush();
} catch(ArgumentException $e) {
// Catches the error if a group already exists.
$io->error($e->getMessage());
return Command::FAILURE;
} catch (Exception $e) {
$io->error("An unknown error occured: {$e->getMessage()}");
$io->error("An unknown error occurred: {$e->getMessage()}");
return Command::FAILURE;
}
$io->success("{$connectionGroup} successfully added.");
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Scene;
use LotGD\Core\SceneTemplates\SceneTemplateInterface;
use Symfony\Component\Console\Input\InputArgument;
class SceneBaseCommand extends BaseCommand
{
protected ?string $namespace = "scene";
/**
* @return InputArgument
*/
protected function getSceneIdArgumentDefinition(): InputArgument
{
return new InputArgument(
name: "id",
mode: InputArgument::REQUIRED,
description: "Scene ID",
);
}
/**
* @param string $id
* @return Scene|null
*/
protected function getScene(string $id): ?Scene
{
/** @var Scene|null $scene */
$scene = $this->game->getEntityManager()->getRepository(Scene::class)->find($id);
return $scene;
}
/**
* @param Scene $scene
* @return string
*/
protected function getSceneTemplatePath(Scene $scene)
{
$sceneTemplate = "no-template";
if ($scene->getTemplate()) {
/** @var SceneTemplateInterface $templateClass */
$templateClass = $scene->getTemplate()->getClass();
$sceneTemplate = $templateClass::getNavigationEvent();
}
return $sceneTemplate;
}
}
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\SceneTemplates\SceneTemplateInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class SceneConfigListCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("config:list"))
->setDescription('List available settings for a scene')
->setDefinition([
$this->getSceneIdArgumentDefinition(),
])
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $output);
$scene = $this->getScene($input->getArgument("id"));
if (!$scene) {
$io->error("Scene was not found.");
return Command::FAILURE;
}
$sceneTemplate = $this->getSceneTemplatePath($scene);
// Create hook
$context = EventContextData::create([
"scene" => $scene,
"io" => $io,
"settings" => [],
]);
$newContext = $this->game->getEventManager()->publish(
event: "h/lotgd/core/cli/scene-config-list/$sceneTemplate",
contextData: $context
);
$settings = $newContext->get("settings");
$io->title("Scene ".$scene->getTitle());
if (count($settings) === 0) {
$io->note("There are no scene settings available.");
} else {
$io->table(["setting", "value", "description"], $settings);
}
return Command::SUCCESS;
}
}
@@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\SceneTemplates\SceneTemplateInterface;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class SceneConfigResetCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("config:reset"))
->setDescription('Reset a scene setting')
->setDefinition([
$this->getSceneIdArgumentDefinition(),
new InputArgument(
"setting",
mode: InputArgument::REQUIRED,
description: "Name of setting, see {$this->namespaced('config:list')}.",
),
])
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
$scene = $this->getScene($input->getArgument("id"));
if (!$scene) {
$io->error("Scene was not found.");
return Command::FAILURE;
}
$sceneTemplate = $this->getSceneTemplatePath($scene);
$io->title("Scene {$scene->getTitle()}");
// Create hook
$context = EventContextData::create([
"scene" => $scene,
"io" => $io,
"setting" => $input->getArgument("setting"),
"return" => Command::FAILURE,
"reason" => "Setting does not exist.",
]);
$newContext = $this->game->getEventManager()->publish(
event: "h/lotgd/core/cli/scene-config-reset/{$sceneTemplate}",
contextData: $context
);
if ($newContext->get("return") != Command::SUCCESS) {
$io->error($newContext->get("reason"));
return Command::FAILURE;
}
$this->game->getEntityManager()->flush();
return Command::SUCCESS;
}
}
@@ -0,0 +1,75 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use LotGD\Core\Events\EventContextData;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class SceneConfigSetCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName($this->namespaced("config:set"))
->setDescription('Change a scene setting')
->setDefinition([
$this->getSceneIdArgumentDefinition(),
new InputArgument(
"setting",
mode: InputArgument::REQUIRED,
description: "Name of setting, see {$this->namespaced('config:list')}.",
),
new InputArgument(
"value",
InputArgument::REQUIRED,
description: "New value for the given setting.",
),
])
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
$scene = $this->getScene($input->getArgument("id"));
if (!$scene) {
$io->error("Scene was not found.");
return Command::FAILURE;
}
$sceneTemplate = $this->getSceneTemplatePath($scene);
$io->title("Scene {$scene->getTitle()}");
// Create hook
$context = EventContextData::create([
"scene" => $scene,
"io" => $io,
"setting" => $input->getArgument("setting"),
"value" => $input->getArgument("value"),
"return" => Command::FAILURE,
"reason" => "Setting does not exist.",
]);
$newContext = $this->game->getEventManager()->publish(
event: "h/lotgd/core/cli/scene-config-set/{$sceneTemplate}",
contextData: $context
);
if ($newContext->get("return") != Command::SUCCESS) {
$io->error($newContext->get("reason"));
return Command::FAILURE;
}
$this->game->getEntityManager()->flush();
return Command::SUCCESS;
}
}
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use Exception;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Exceptions\ArgumentException;
use LotGD\Core\Models\Scene;
use LotGD\Core\Models\SceneConnectable;
@@ -19,15 +18,15 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class SceneConnectCommand extends BaseCommand
class SceneConnectCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('scene:connect')
->setDescription('Connects two scenes.')
$this->setName($this->namespaced("connect"))
->setDescription("Connects two scenes.")
->setDefinition(
new InputDefinition([
new InputArgument(
@@ -72,7 +71,7 @@ class SceneConnectCommand extends BaseCommand
protected function execute(InputInterface $input, OutputInterface $output): int
{
$em = $this->game->getEntityManager();
$logger = $this->game->getLogger();
$logger = $this->getCliLogger();
$sceneRepository = $em->getRepository(Scene::class);
$io = new SymfonyStyle($input, $output);
@@ -141,10 +140,10 @@ class SceneConnectCommand extends BaseCommand
// Commit changes
$em->flush();
} catch (ArgumentException $e) {
$io->error("Scenes were not connected. Reason: {$e}.");
$io->error("Scenes were not connected. Reason: {$e->getMessage()}.");
return Command::FAILURE;
} catch (Exception $e) {
$io->error("An unknown error occured: {$e->getMessage()}");
$io->error("An unknown error occurred: {$e}");
return Command::FAILURE;
}
@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use Exception;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Scene;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
@@ -16,26 +15,26 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class SceneDisconnectCommand extends BaseCommand
class SceneDisconnectCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('scene:disconnect')
->setDescription('Disconnects two scenes.')
$this->setName($this->namespaced("disconnect"))
->setDescription("Disconnects two scenes.")
->setDefinition(
new InputDefinition([
new InputArgument(
"scene1",
mode: InputArgument::REQUIRED,
description: "Outgoing scene ID",
description: "One scene ID",
),
new InputArgument(
"scene2",
mode: InputArgument::REQUIRED,
description: "Incoming scene ID",
description: "The other scene ID",
),
])
)
@@ -48,7 +47,7 @@ class SceneDisconnectCommand extends BaseCommand
protected function execute(InputInterface $input, OutputInterface $output): int
{
$em = $this->game->getEntityManager();
$logger = $this->game->getLogger();
$logger = $this->getCliLogger();
$sceneRepository = $em->getRepository(Scene::class);
$io = new SymfonyStyle($input, $output);
@@ -71,7 +70,7 @@ class SceneDisconnectCommand extends BaseCommand
$connection = $scene1->getConnectionTo($scene2);
if (!$connection) {
$io->error("The to given scenes do not share a connection.");
$io->error("The given scenes do not share a connection.");
return Command::FAILURE;
}
@@ -80,7 +79,7 @@ class SceneDisconnectCommand extends BaseCommand
$em->remove($connection);
$em->flush();
} catch (Exception $e) {
$io->error("An unknown error occured: {$e->getMessage()}");
$io->error("An unknown error occurred: {$e->getMessage()}");
return Command::FAILURE;
}
@@ -10,15 +10,15 @@ use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
class SceneListCommand extends BaseCommand
class SceneListCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('scene:list')
->setDescription('Lists all scenes')
$this->setName($this->namespaced("list"))
->setDescription("Lists all scenes")
;
}
@@ -33,12 +33,13 @@ class SceneListCommand extends BaseCommand
/** @var Scene[] $scenes */
$scenes = $em->getRepository(Scene::class)->findAll();
$table = [["id", "title", "connections"], []];
$table = [["id", "title", "connections", "template"], []];
foreach ($scenes as $scene) {
$table[1][] = [
$scene->getId(),
$scene->getTitle(),
count($scene->getConnectedScenes()),
$scene->getTemplate()?->getClass(),
];
}
@@ -3,10 +3,8 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Scene;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -15,22 +13,18 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class SceneRemoveCommand extends BaseCommand
class SceneRemoveCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('scene:remove')
->setDescription('Removes a scene.')
$this->setName($this->namespaced("remove"))
->setDescription("Removes a scene.")
->setDefinition(
new InputDefinition([
new InputArgument(
"id",
mode: InputArgument::REQUIRED,
description: "Scene ID",
),
$this->getSceneIdArgumentDefinition(),
])
)
;
@@ -57,7 +51,7 @@ class SceneRemoveCommand extends BaseCommand
}
if (!$scene->isRemovable()) {
$io->error("The scene with the ID {$sceneId} was marked as not removable. Please remove the responsible");
$io->error("The scene with the ID {$sceneId} was marked as not removable. Please remove the responsible module instead.");
return Command::FAILURE;
}
@@ -3,7 +3,6 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Scene;
use LotGD\Core\Models\SceneConnection;
use Symfony\Component\Console\Command\Command;
@@ -16,18 +15,18 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class SceneRemoveConnectionGroupCommand extends BaseCommand
class SceneRemoveConnectionGroupCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('scene:removeConnectionGroup')
->setDescription('Removes a connection group from an existing scene.')
$this->setName($this->namespaced("removeConnectionGroup"))
->setDescription("Removes a connection group from an existing scene.")
->setDefinition(
new InputDefinition([
new InputArgument("id", InputArgument::REQUIRED, "ID of the scene"),
$this->getSceneIdArgumentDefinition(),
new InputArgument("groupName", InputArgument::REQUIRED, "Internal id of the group."),
]),
)
@@ -40,7 +39,7 @@ class SceneRemoveConnectionGroupCommand extends BaseCommand
protected function execute(InputInterface $input, OutputInterface $output): int
{
$em = $this->game->getEntityManager();
$logger = $this->game->getLogger();
$logger = $this->getCliLogger();
$io = new SymfonyStyle($input, $output);
@@ -52,12 +51,12 @@ class SceneRemoveConnectionGroupCommand extends BaseCommand
$scene = $em->getRepository(Scene::class)->find($sceneId);
if (!$scene) {
$io->error("The requested scene with the ID {$sceneId} was not found");
$io->error("The scene with the ID {$sceneId} was not found.");
return Command::FAILURE;
}
if (!$scene->hasConnectionGroup($groupName)) {
$io->error("The scene {$sceneId} oes not have a connection group with the name {$groupName}");
$io->error("The scene {$sceneId} does not have a connection group with the name {$groupName}");
return Command::FAILURE;
}
@@ -84,7 +83,7 @@ class SceneRemoveConnectionGroupCommand extends BaseCommand
try {
$em->flush();
} catch (\Exception $e) {
$io->error("An unknown error occured: {$e->getMessage()}");
$io->error("An unknown error occurred: {$e->getMessage()}");
return Command::FAILURE;
}
+8 -10
View File
@@ -3,13 +3,11 @@ declare(strict_types=1);
namespace LotGD\Core\Console\Command\Scene;
use LotGD\Core\Console\Command\BaseCommand;
use LotGD\Core\Models\Scene;
use LotGD\Core\Models\SceneConnectable;
use LotGD\Core\Models\SceneConnection;
use LotGD\Core\Models\SceneConnectionGroup;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
@@ -18,18 +16,18 @@ use Symfony\Component\Console\Style\SymfonyStyle;
/**
* Resets the viewpoint of a given character.
*/
class SceneShowCommand extends BaseCommand
class SceneShowCommand extends SceneBaseCommand
{
/**
* @inheritDoc
*/
protected function configure()
{
$this->setName('scene:show')
->setDescription('Show details about a specific scene.')
$this->setName($this->namespaced("show"))
->setDescription("Show details about a specific scene.")
->setDefinition(
new InputDefinition([
new InputArgument("id", InputArgument::REQUIRED, "ID of the scene"),
$this->getSceneIdArgumentDefinition(),
])
)
;
@@ -57,7 +55,7 @@ class SceneShowCommand extends BaseCommand
$io->listing([
"ID: {$scene->getId()}",
"Title: {$scene->getTitle()}",
"Template: {$scene->getTemplate()->getClass()}",
"Template: {$scene->getTemplate()?->getClass()}",
]);
$io->text($scene->getDescription());
@@ -81,7 +79,7 @@ class SceneShowCommand extends BaseCommand
# Get formatting for outgoing scene connection group name
$outgoingSceneConnectionGroup = $connection->getOutgoingConnectionGroupName();
if ($outgoingSceneConnectionGroup) {
$outgoingSceneConnectionGroup = "(on $outgoingSceneConnectionGroup)";
$outgoingSceneConnectionGroup = " (on $outgoingSceneConnectionGroup)";
} else {
$outgoingSceneConnectionGroup = "";
}
@@ -100,9 +98,9 @@ class SceneShowCommand extends BaseCommand
# Check if the connection is bidirectional (only out (this)->in)
if ($connection->isDirectionality(SceneConnectable::Bidirectional)) {
$list[] = "this$outgoingSceneConnectionGroup <=> {$other->getTitle()}$incomingSceneConnectionGroup (id={$other->getId()})";
$list[] = "this$outgoingSceneConnectionGroup <=> {$other->getTitle()}$incomingSceneConnectionGroup(id={$other->getId()})";
} else {
$list[] = "this$outgoingSceneConnectionGroup => {$other->getTitle()}$incomingSceneConnectionGroup (id={$other->getId()})";
$list[] = "this$outgoingSceneConnectionGroup => {$other->getTitle()}$incomingSceneConnectionGroup(id={$other->getId()})";
}
} else {
$other = $connection->getOutgoingScene();
+33 -8
View File
@@ -6,6 +6,10 @@ namespace LotGD\Core\Console;
use LotGD\Core\Bootstrap;
use LotGD\Core\Console\Command\Character\CharacterAddCommand;
use LotGD\Core\Console\Command\Character\CharacterConfigListCommand;
use LotGD\Core\Console\Command\Character\CharacterConfigResetCommand;
use LotGD\Core\Console\Command\Character\CharacterConfigSetCommand;
use LotGD\Core\Console\Command\Character\CharacterEditCommand;
use LotGD\Core\Console\Command\Character\CharacterListCommand;
use LotGD\Core\Console\Command\Character\CharacterRemoveCommand;
use LotGD\Core\Console\Command\Character\CharacterResetViewpointCommand;
@@ -13,17 +17,24 @@ use LotGD\Core\Console\Command\Character\CharacterShowCommand;
use LotGD\Core\Console\Command\ConsoleCommand;
use LotGD\Core\Console\Command\Database\DatabaseInitCommand;
use LotGD\Core\Console\Command\Database\DatabaseSchemaUpdateCommand;
use LotGD\Core\Console\Command\Module\ModuleConfigListCommand;
use LotGD\Core\Console\Command\Module\ModuleConfigResetCommand;
use LotGD\Core\Console\Command\Module\ModuleConfigSetCommand;
use LotGD\Core\Console\Command\Module\ModuleListCommand;
use LotGD\Core\Console\Command\Module\ModuleRegisterCommand;
use LotGD\Core\Console\Command\Module\ModuleValidateCommand;
use LotGD\Core\Console\Command\Scene\SceneConfigListCommand;
use LotGD\Core\Console\Command\Scene\SceneConfigResetCommand;
use LotGD\Core\Console\Command\Scene\SceneConfigSetCommand;
use LotGD\Core\Console\Command\SceneTemplates\SceneTemplateListCommand;
use LotGD\Core\Console\Command\Scene\SceneAddCommand;
use LotGD\Core\Console\Command\Scene\SceneRemoveCommand;
use LotGD\Core\Console\Command\Scene\SceneAddConnectionGroupCommand;
use LotGD\Core\Console\Command\Scene\SceneConnectCommand;
use LotGD\Core\Console\Command\Scene\SceneListCommand;
use LotGD\Core\Console\Command\Scene\SceneDisconnectCommand;
use LotGD\Core\Console\Command\Scene\SceneListCommand;
use LotGD\Core\Console\Command\Scene\SceneRemoveCommand;
use LotGD\Core\Console\Command\Scene\SceneRemoveConnectionGroupCommand;
use LotGD\Core\Console\Command\Scene\SceneShowCommand;
use LotGD\Core\Console\Command\SceneTemplates\SceneTemplateListCommand;
use LotGD\Core\Game;
use Symfony\Component\Console\Application;
@@ -44,7 +55,7 @@ class Main
$this->application = new Application();
$this->application->setName("daenerys 🐲 ");
$this->application->setVersion("0.0.1 (lotgd/core version " . \LotGD\Core\Game::getVersion() . ")");
$this->application->setVersion("lotgd/core version " . Game::getVersion() . "");
}
/**
@@ -52,22 +63,36 @@ class Main
*/
protected function addCommands()
{
$this->application->add(new ModuleValidateCommand($this->game));
$this->application->add(new ModuleRegisterCommand($this->game));
$this->application->add(new DatabaseInitCommand($this->game));
$this->application->add(new DatabaseSchemaUpdateCommand($this->game));
$this->application->add(new ConsoleCommand($this->game));
// Module commands
$this->application->add(new ModuleConfigListCommand($this->game));
$this->application->add(new ModuleConfigResetCommand($this->game));
$this->application->add(new ModuleConfigSetCommand($this->game));
$this->application->add(new ModuleListCommand($this->game));
$this->application->add(new ModuleRegisterCommand($this->game));
$this->application->add(new ModuleValidateCommand($this->game));
// Character commands
$this->application->add(new CharacterAddCommand($this->game));
$this->application->add(new CharacterConfigListCommand($this->game));
$this->application->add(new CharacterConfigResetCommand($this->game));
$this->application->add(new CharacterConfigSetCommand($this->game));
$this->application->add(new CharacterEditCommand($this->game));
$this->application->add(new CharacterListCommand($this->game));
$this->application->add(new CharacterRemoveCommand($this->game));
$this->application->add(new CharacterResetViewpointCommand($this->game));
$this->application->add(new CharacterShowCommand($this->game));
// Scene commands
$this->application->add(new SceneListCommand($this->game));
$this->application->add(new SceneAddCommand($this->game));
$this->application->add(new SceneConfigListCommand($this->game));
$this->application->add(new SceneConfigResetCommand($this->game));
$this->application->add(new SceneConfigSetCommand($this->game));
$this->application->add(new SceneListCommand($this->game));
$this->application->add(new SceneRemoveCommand($this->game));
$this->application->add(new SceneShowCommand($this->game));
@@ -87,7 +112,7 @@ class Main
}
/**
* Run the danerys tool.
* Run the Daenerys tool.
*/
public function run()
{
+3 -1
View File
@@ -288,6 +288,8 @@ class Game
private function navigateToScene(Scene $scene, array $parameters)
{
$viewpoint = $this->getCharacter()->getViewpoint();
$viewpoint->setTwigSceneRenderer($this->getSceneRenderer());
do {
$referrer = $viewpoint->getScene();
@@ -296,7 +298,7 @@ class Game
$this->getLogger()->debug("Navigating to sceneId={$id} from referrer sceneId={$referrerId}");
// Copy over the basic structure from the scene database.
$viewpoint->changeFromScene($scene, $this->getSceneRenderer());
$viewpoint->changeFromScene($scene);
// Generate the default set of actions: the default group with
// all children.
+1 -1
View File
@@ -17,7 +17,6 @@ use LotGD\Core\Exceptions\ArgumentException;
use LotGD\Core\Exceptions\AttributeMissingException;
use LotGD\Core\Exceptions\UnexpectedArrayKeyException;
use LotGD\Core\Exceptions\WrongTypeException;
use LotGD\Core\Tools\Model\Creator;
use LotGD\Core\Tools\Model\Deletor;
use LotGD\Core\Tools\Model\PropertyManager;
use LotGD\Core\Tools\Model\Saveable;
@@ -142,6 +141,7 @@ class Scene implements CreateableInterface, SceneConnectable
$this->outgoingConnections = new ArrayCollection();
$this->incomingConnections = new ArrayCollection();
$this->attachments = new ArrayCollection();
$this->properties = new ArrayCollection();
}
public function __toString(): string
+72 -12
View File
@@ -41,10 +41,16 @@ class Viewpoint implements CreateableInterface
*/
private Character $owner;
/** @Column(type="array") */
/**
* @var ActionGroup[]
* @Column(type="array")
*/
private array $actionGroups = [];
/** @Column(type="array") */
/**
* @var Attachment[]
* @Column(type="array")
*/
private array $attachments = [];
/** @Column(type="array") */
@@ -57,6 +63,7 @@ class Viewpoint implements CreateableInterface
private ?Scene $scene = null;
private ?SceneDescription $_description = null;
private ?TwigSceneRenderer $twigSceneRenderer = null;
private static array $fillable = [
"owner",
@@ -80,6 +87,22 @@ class Viewpoint implements CreateableInterface
$this->owner = $owner;
}
/**
* @param TwigSceneRenderer $twigSceneRenderer
*/
public function setTwigSceneRenderer(TwigSceneRenderer $twigSceneRenderer)
{
$this->twigSceneRenderer = $twigSceneRenderer;
}
/**
* @return TwigSceneRenderer|null
*/
public function getTwigSceneRenderer(): ?TwigSceneRenderer
{
return $this->twigSceneRenderer;
}
/**
* Sets the description of this viewpoint.
* @param string $description
@@ -125,15 +148,11 @@ class Viewpoint implements CreateableInterface
/**
* Copies the static data from a scene to this Viewpoint entity.
* @param Scene $scene
* @param TwigSceneRenderer $renderer Required to parse title and description.
*/
public function changeFromScene(Scene $scene, TwigSceneRenderer $renderer)
public function changeFromScene(Scene $scene)
{
$title = $renderer->render($scene->getTitle(), $scene, ignoreErrors: true);
$description = $renderer->render($scene->getDescription(), $scene, ignoreErrors: true);
$this->setTitle($title);
$this->setDescription($description);
$this->setTitle($scene->getTitle());
$this->setDescription($scene->getDescription());
$this->setTemplate($scene->getTemplate());
$this->setScene($scene);
@@ -178,6 +197,10 @@ class Viewpoint implements CreateableInterface
$this->setActionGroups($snapshot->getActionGroups());
$this->setAttachments($snapshot->getAttachments());
$this->setData($snapshot->getData());
foreach ($this->actionGroups as $actionGroup) {
$actionGroup->setViewpoint($this);
}
}
/**
@@ -213,6 +236,11 @@ class Viewpoint implements CreateableInterface
*/
public function setActionGroups(array $actionGroups)
{
foreach ($actionGroups as $actionGroup) {
if ($actionGroup instanceof ActionGroup) {
$actionGroup->setViewpoint($this);
}
}
$this->actionGroups = $actionGroups;
}
@@ -229,6 +257,10 @@ class Viewpoint implements CreateableInterface
throw new ArgumentException("Group {$group} is already contained in this viewpoint.");
}
foreach ($group->getActions() as $action) {
$action->setViewpoint($this);
}
if ($after === null) {
$this->actionGroups[] = $group;
} else {
@@ -271,9 +303,7 @@ class Viewpoint implements CreateableInterface
$actionGroups = $this->getActionGroups();
foreach ($actionGroups as $group) {
if ($group->getId() == $actionGroupId) {
$actions = $group->getActions();
$actions[] = $action;
$group->setActions($actions);
$group->addAction($action);
break;
}
}
@@ -381,4 +411,34 @@ class Viewpoint implements CreateableInterface
$group->setActions($actions);
}
}
/**
* Returns the rendered version of the title
* @return string
*/
public function getRenderedTitle(): string
{
$title = $this->getTitle();
if ($this->twigSceneRenderer) {
return $this->twigSceneRenderer->render($title, $this, ignoreErrors: true);
} else {
return $title;
}
}
/**
* Returns the rendered version of the description
* @return string
*/
public function getRenderedDescription(): string
{
$description = $this->getDescription();
if ($this->twigSceneRenderer) {
return $this->twigSceneRenderer->render($description, $this, ignoreErrors: true);
} else {
return $description;
}
}
}
+3
View File
@@ -25,6 +25,9 @@ class ViewpointSnapshot
private array $attachments,
private array $data,
) {
foreach ($this->actionGroups as $actionGroup) {
$actionGroup->setViewpoint(null);
}
}
/**
+1 -2
View File
@@ -78,8 +78,7 @@ class HasAction extends Constraint
$checkedOnce = True;
}
# Using KNF, !A or B is only false if A is true and B is not.
if ($action->$methodToCheck() == $valueToHave and (!is_null($groupTitle) or $group->getTitle() === $groupTitle)) {
if ($action->$methodToCheck() == $valueToHave and (is_null($groupTitle) or $group->getTitle() === $groupTitle)) {
$found = $action;
}
}
+1 -1
View File
@@ -156,7 +156,7 @@ class LotGDTestCase extends TestCase
}
# Using KNF, !A or B is only false if A is true and B is not.
if ($action->$methodToCheck() == $valueToHave and (!is_null($groupTitle) or $group->getTitle() === $groupTitle)) {
if ($action->$methodToCheck() == $valueToHave and (is_null($groupTitle) or $group->getTitle() === $groupTitle)) {
$found = $action;
}
}
+45 -12
View File
@@ -5,12 +5,17 @@ declare(strict_types=1);
namespace LotGD\Core\Services;
use LotGD\Core\Action;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Exceptions\CharacterNotFoundException;
use LotGD\Core\Exceptions\InsecureTwigTemplateError;
use LotGD\Core\Game;
use LotGD\Core\Models\Character;
use LotGD\Core\Models\Scene;
use LotGD\Core\Models\Viewpoint;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\SyntaxError;
use Twig\Extension\SandboxExtension;
use Twig\Sandbox\SecurityError;
use Twig\Sandbox\SecurityPolicy;
@@ -18,19 +23,40 @@ use Twig\Sandbox\SecurityPolicy;
class TwigSceneRenderer
{
private Environment $twig;
private array $templateValues;
public function __construct(
private Game $game
) {
$this->twig = new Environment(new TwigNullLoader());
$securityPolicy = $this->getSecurityPolicy();
// Add a hook for additional fields
// This is for global changes only. Viewpoint-dependent changes should try to store the important values within
// the viewpoint itself.
$eventManager = $this->game->getEventManager();
$contextData = EventContextData::create(["templateValues" => []]);
$newContextData = $eventManager->publish("h/lotgd/core/scene-renderer/templateValues", $contextData);
$this->templateValues = $newContextData->get("templateValues") ?? [];
# Add Sandbox extension
// Add Sandbox extension
$securityPolicy = $this->getSecurityPolicy();
$this->twig->addExtension(new SandboxExtension($securityPolicy, sandboxed: true));
}
public function render(string $string, Scene $scene, bool $ignoreErrors = false): string
/**
* Renders a given string in the context if a given viewpoint.
*
* @param string $string
* @param Viewpoint $viewpoint
* @param bool $ignoreErrors If set to true, errors are ignored and the unparsed string will be returned instead.
* @param array $templateValues
* @return string
* @throws InsecureTwigTemplateError
* @throws CharacterNotFoundException
* @throws LoaderError
* @throws SyntaxError
*/
public function render(string $string, Viewpoint $viewpoint, bool $ignoreErrors = false, array $templateValues = []): string
{
// We catch here "Tag" errors. If error, we'll exit either by returning the input ($ignoreError === true) or
// throwing an exception.
@@ -44,16 +70,14 @@ class TwigSceneRenderer
}
}
$templateValues = [
$defaultTemplateValues = [
"Character" => $this->game->getCharacter(),
"Scene" => $scene,
"Scene" => $viewpoint->getScene(),
"Viewpoint" => $viewpoint,
];
// Publish event to change $templateValues
$eventManager = $this->game->getEventManager();
$contextData = EventContextData::create(["templateValues" => $templateValues]);
$newContextData = $eventManager->publish("h/lotgd/core/scene-renderer/templateValues", $contextData);
$templateValues = $newContextData->get("templateValues");
// Merges additional template values with important ones.
$templateValues = array_merge($this->templateValues, $defaultTemplateValues, $templateValues);
// Try to render the template
try {
@@ -70,17 +94,26 @@ class TwigSceneRenderer
return $result;
}
/**
* Returns the current security policy.
* This method provides a hook.
* @return SecurityPolicy
*/
public function getSecurityPolicy(): SecurityPolicy
{
$tags = ["if"];
$filters = ["lower", "upper", "escape"];
$filters = ["lower", "upper", "escape", "round"];
$functions = ["range"];
$methods = [
Character::class => ["getDisplayName", "getLevel", "isAlive", "getHealth", "getMaxHealth", "getProperty"],
Scene::class => ["getProperty"],
Viewpoint::class => ["getData"],
Action::class => ["getParameters"],
];
$properties = [
"Character" => ["displayName", "level", "health", "maxHealth"],
"Character" => ["displayName", "level", "alive", "health", "maxHealth", "property"],
"Viewpoint" => ["data"],
"Action" => ["parameters"],
];
// Publish event to change $templateValues
@@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterAddCommand;
use LotGD\Core\Models\Character;
use LotGD\Core\Models\Repositories\CharacterRepository;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
class CharacterAddCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "character";
protected function getCommand(): CommandTester
{
return new CommandTester(new CharacterAddCommand($this->g));
}
public function testIfCommandFailsIfNoNameWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([]);
}
public function testIfCharacterGetsCreatedWithOnlyName()
{
/** @var CharacterRepository $repository */
$repository = $this->g->getEntityManager()->getRepository(Character::class);
$command = $this->getCommand();
$command->execute([
"name" => "Gandalf the First",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("was successfully created.", $output);
// Check the database, too.
$this->g->getEntityManager()->clear();
$character = $repository->findOneBy(["name" => "Gandalf the First"]);
$this->assertNotNull($character);
$this->assertSame("Gandalf the First", $character->getName());
$this->assertSame(1, $character->getLevel());
$this->assertSame(10, $character->getMaxHealth());
$this->assertSame(10, $character->getHealth());
}
public function testIfCommandFailsIfLevelIsBelow1()
{
$command = $this->getCommand();
$levels = [-10, -5, 0];
foreach ($levels as $level) {
$command->execute([
"name" => "Gandalf the Failed",
"--level" => $level,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Level must at least be 1.", $output);
}
}
public function testIfCommandFailsWhenMaxHealthIsBelow0()
{
$command = $this->getCommand();
$levels = [-100, -50, -1];
foreach ($levels as $level) {
$command->execute([
"name" => "Gandalf the Unhealthy",
"--maxHealth" => $level,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Maximum health must be at least 0.", $output);
}
}
public function testIfCommandWarnsWhenMaxHealthIs0()
{
/** @var CharacterRepository $repository */
$repository = $this->g->getEntityManager()->getRepository(Character::class);
$command = $this->getCommand();
$command->execute([
"name" => "Gandalf the Unhealthy",
"--maxHealth" => 0,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[WARNING]", $output);
$this->assertStringContainsString("The character will have 0 max health and will be permanently dead.", $output);
// Check the database, too.
$this->g->getEntityManager()->clear();
$character = $repository->findOneBy(["name" => "Gandalf the Unhealthy"]);
$this->assertNotNull($character);
$this->assertSame("Gandalf the Unhealthy", $character->getName());
$this->assertSame(1, $character->getLevel());
$this->assertSame(0, $character->getMaxHealth());
$this->assertSame(0, $character->getHealth());
}
}
@@ -0,0 +1,124 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterConfigListCommand;
use LotGD\Core\EventManager;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Game;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class CharacterConfigListCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "character";
protected function getCommand(): CommandTester
{
return new CommandTester(new CharacterConfigListCommand($this->g));
}
public function testIfCommandRunsWithoutRegisteredEvents()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("There are no character settings available.", $output);
}
public function testIfCommandEmitsEvent()
{
/** @var Game $game */
$game = $this->g;
$characters = [
["10000000-0000-0000-0000-000000000001", "Testcharacter 1"],
["10000000-0000-0000-0000-000000000002", "Testcharacter 2"],
];
foreach ($characters as [$character, $displayName]) {
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->with(
$this->equalTo("h/lotgd/core/cli/character-config-list"),
$this->callback(function (EventContextData $eventContextData) use ($character, $displayName) {
$pass = 1;
$pass &= $eventContextData->has("character") === true;
$pass &= $eventContextData->get("character")->getDisplayName() === $displayName;
$pass &= $eventContextData->has("io") === true;
$pass &= $eventContextData->get("io") instanceof SymfonyStyle;
$pass &= $eventContextData->has("settings") === true;
$pass &= $eventContextData->get("settings") === [];
return $pass == true;
}),
)->will($this->returnArgument(1));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => $character,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Character {$displayName}", $output);
$this->assertStringContainsString("There are no character settings available.", $output);
}
}
public function testIfCommandDisplaysSettingsWhenGivenByEvent()
{
/** @var Game $game */
$game = $this->g;
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->will($this->returnCallback(function (string $a, EventContextData $b) {
$newSettings = [
["setting1", "0.00000000000000000000000001", "float 0..1, chance to succeed"],
["setting2", "DragonMillions", "string, name of the lottery"],
];
$settings = $b->get("settings");
$settings = [...$settings, ...$newSettings];
return $b->set("settings", $settings);
}));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("setting1", $output);
$this->assertStringContainsString("0.00000000000000000000000001", $output);
$this->assertStringContainsString("float 0..1, chance to succeed", $output);
$this->assertStringContainsString("setting2", $output);
$this->assertStringContainsString("DragonMillions", $output);
$this->assertStringContainsString("string, name of the lottery", $output);
$this->assertStringNotContainsString("There are no character settings available.", $output);
}
}
@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterConfigResetCommand;
use LotGD\Core\EventManager;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Game;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class CharacterConfigResetCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "character";
protected function getCommand(): CommandTester
{
return new CommandTester(new CharacterConfigResetCommand($this->g));
}
public function testIfCommandEmitsEvent()
{
/** @var Game $game */
$game = $this->g;
$modules = [
["10000000-0000-0000-0000-000000000001", "Testcharacter 1", "test"],
["10000000-0000-0000-0000-000000000002", "Testcharacter 2", "test-other"],
];
foreach ($modules as [$module, $displayName, $setting]) {
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->with(
$this->equalTo("h/lotgd/core/cli/character-config-reset"),
$this->callback(function (EventContextData $eventContextData) use ($module, $displayName, $setting) {
$pass = 1;
$pass &= $eventContextData->has("character") === true;
$pass &= $eventContextData->get("character")->getDisplayName() === $displayName;
$pass &= $eventContextData->has("io") === true;
$pass &= $eventContextData->get("io") instanceof SymfonyStyle;
$pass &= $eventContextData->has("setting") === true;
$pass &= $eventContextData->get("setting") === $setting;
$pass &= $eventContextData->has("return") === true;
$pass &= $eventContextData->get("return") === 1;
$pass &= $eventContextData->has("reason") === true;
$pass &= $eventContextData->get("reason") === "Setting does not exist.";
return $pass == true;
}),
)->will($this->returnArgument(1));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => $module,
"setting" => $setting,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("Character {$displayName}", $output);
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("[OK]", $output);
$this->assertStringContainsString("Setting does not exist.", $output);
}
}
public function testIfCommandSucceedsWhenReturnedCallbackIsSetToSuccess()
{
/** @var Game $game */
$game = $this->g;
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->will($this->returnCallback(function (string $a, EventContextData $b) {
return $b->set("return", Command::SUCCESS);
}));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
"setting" => "Setting",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Character Testcharacter 1", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("Setting does not exist.", $output);
}
}
@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterConfigSetCommand;
use LotGD\Core\EventManager;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Game;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class CharacterConfigSetCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "character";
protected function getCommand(): CommandTester
{
return new CommandTester(new CharacterConfigSetCommand($this->g));
}
public function testIfCommandEmitsEvent()
{
/** @var Game $game */
$game = $this->g;
$characters = [
["10000000-0000-0000-0000-000000000001", "Testcharacter 1", "test", "0.126"],
["10000000-0000-0000-0000-000000000002", "Testcharacter 2", "test-other", "hi"],
];
foreach ($characters as [$character, $displayName, $setting, $value]) {
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->with(
$this->equalTo("h/lotgd/core/cli/character-config-set"),
$this->callback(function (EventContextData $eventContextData) use ($character, $displayName, $setting, $value) {
$pass = 1;
$pass &= $eventContextData->has("character") === true;
$pass &= $eventContextData->get("character")->getDisplayName() === $displayName;
$pass &= $eventContextData->has("io") === true;
$pass &= $eventContextData->get("io") instanceof SymfonyStyle;
$pass &= $eventContextData->has("setting") === true;
$pass &= $eventContextData->get("setting") === $setting;
$pass &= $eventContextData->has("value") === true;
$pass &= $eventContextData->get("value") === $value;
$pass &= $eventContextData->has("return") === true;
$pass &= $eventContextData->get("return") === 1;
$pass &= $eventContextData->has("reason") === true;
$pass &= $eventContextData->get("reason") === "Setting does not exist.";
return $pass == true;
}),
)->will($this->returnArgument(1));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => $character,
"setting" => $setting,
"value" => $value,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("Character {$displayName}", $output);
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Setting does not exist.", $output);
}
}
public function testIfCommandSucceedsWhenReturnedCallbackIsSetToSuccess()
{
/** @var Game $game */
$game = $this->g;
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->will($this->returnCallback(function (string $a, EventContextData $b) {
return $b->set("return", Command::SUCCESS);
}));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
"setting" => "Setting",
"value" => 13,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Character Testcharacter 1", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("Setting does not exist.", $output);
}
}
@@ -0,0 +1,331 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterEditCommand;
use LotGD\Core\Models\Character;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
class CharacterEditCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "character";
protected function getCommand(): CommandTester
{
return new CommandTester(new CharacterEditCommand($this->g));
}
public function testIfWrongCharacterIdReturnsInFailure()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000000",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("not found", $output);
}
public function testIfExistingCharacterIsFound()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[NOTE]", $output);
$this->assertStringContainsString("Nothing was changed", $output);
}
public function testIfChangingLevelToValuesBelowOneResultsInAnError()
{
$command = $this->getCommand();
$levels_to_test = [-5, 0];
foreach ($levels_to_test as $level) {
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
"--level" => $level,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("below 1", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
$character = $this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000001");
$this->assertSame(1, $character->getLevel());
}
}
public function testIfChangingLevelToValuesBetween1And15Succeeds()
{
$command = $this->getCommand();
$levels_to_test = range(1, 15);
foreach ($levels_to_test as $level) {
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
"--level" => $level,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
if ($level === 1) {
$this->assertStringContainsString("[NOTE]", $output);
$this->assertStringContainsString("Nothing was changed", $output);
} else {
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("The character was successfully changed", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
}
// Test if change was reflected in the model, too.
$this->g->getEntityManager()->clear(); // important to refetch the model. Makes sure the command flushes.
$character = $this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000001");
$this->assertSame($level, $character->getLevel());
}
}
public function testIfCharacterAtFullHealthDoesNotChangeAnything()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000002",
"--heal" => null,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[NOTE]", $output);
$this->assertStringContainsString("Character is already at full health.", $output);
$this->assertStringContainsString("Nothing was changed", $output);
}
public function testIfDeadCharacterCannotGetHealed()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
"--heal" => null,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Cannot heal a dead character. Use --revive instead.", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
}
public function testIfCharacterBelowFullHealthGetsHealed()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000004",
"--heal" => null,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("Character was restored to full health (80 to 90).", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
// Test if change was reflected in the model, too.
$this->g->getEntityManager()->clear(); // important to refetch the model. Makes sure the command flushes.
$character = $this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000004");
$this->assertSame($character->getMaxHealth(), $character->getHealth());
}
public function testIfAliveCharacterCannotGetRevived()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000002",
"--revive" => "",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Character is already alive. Use --heal instead.", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
}
public function testIfDeadCharacterGetsCompletelyRevivedWhenReviveValueIsNotGiven()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
"--revive" => null,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("Character was revived with 100 of health points (max: 100).", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
$this->g->getEntityManager()->clear(); // important to refetch the model. Makes sure the command flushes.
$character = $this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000001");
$this->assertSame($character->getMaxHealth(), $character->getHealth());
}
public function testIfDeadCharacterGetsHealedAccordingToTheDesiredAmount()
{
$amounts = [
[0, 1],
[0.1, 10],
[0.5, 50],
[0.99, 99],
["10%", 10],
["50 %", 50],
["50.5%", 51],
];
$command = $this->getCommand();
foreach ($amounts as [$amount, $newHealth]) {
// Kill character first.
$this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000001")->setHealth(0);
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
"--revive" => strval($amount),
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("Character was revived with {$newHealth} of health points (max: 100).", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
$this->g->getEntityManager()->clear(); // important to refetch the model. Makes sure the command flushes.
$character = $this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000001");
$this->assertSame($newHealth, $character->getHealth());
}
}
public function testIfADeadCharacterCannotGetKilled()
{
// Kill character first.
$this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000001")->setHealth(0);
$this->g->getEntityManager()->flush();
$this->g->getEntityManager()->clear();
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
"--kill" => true,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("What is dead may never die.", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
$this->g->getEntityManager()->clear();
$character = $this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000001");
$this->assertFalse($character->isAlive());
$this->assertSame(0, $character->getHealth());
}
public function testIfALivingCharacterCanGetKilled()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000002",
"--kill" => true,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("Character was killed.", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
$character = $this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000002");
$this->assertFalse($character->isAlive());
$this->assertSame(0, $character->getHealth());
}
public function testThatMaxHealthCannotBeSetSmallerThanZero()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000002",
"--maxHealth" => -1,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Cannot set maximum health below 0.", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
}
public function testThatMaxHealthCanBeChangedAndThatCharacterHealthChangesProportionally()
{
$tests = [
[200, 50, 100],
[200, 100, 200],
[200, 0, 0],
];
$command = $this->getCommand();
foreach ($tests as [$newMaxHealth, $healthBefore, $healthAfter]) {
// Get a character and set the health accordingly
$character = $this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000001");
$character->setHealth($healthBefore);
$character->setMaxHealth(100);
$this->g->getEntityManager()->flush();
$this->g->getEntityManager()->clear();
// Run the command
$command->execute([
"id" => "10000000-0000-0000-0000-000000000001",
"--maxHealth" => $newMaxHealth,
]);
$output = $command->getDisplay();
// Assert the output
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("Character has new maximum health of {$newMaxHealth} (current health is {$healthAfter}).", $output);
$this->assertStringNotContainsString("Nothing was changed", $output);
// Assert that the change is reflected in character model
$this->g->getEntityManager()->clear();
$character = $this->g->getEntityManager()->getRepository(Character::class)
->find("10000000-0000-0000-0000-000000000001");
$this->assertSame($newMaxHealth, $character->getMaxHealth());
$this->assertSame($healthAfter, $character->getHealth());
}
}
}
@@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterListCommand;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
class CharacterListCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "character";
protected function getCommand(): CommandTester
{
return new CommandTester(new CharacterListCommand($this->g));
}
public function testIfCommandRunsWithoutArguments()
{
$command = $this->getCommand();
$command->execute([]);
$output = $command->getDisplay();
// Assertions
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Testcharacter 1", $output);
$this->assertStringContainsString("Testcharacter 2", $output);
$this->assertStringNotContainsString("Testcharacter 3", $output); // 3 is soft-deleted
$this->assertStringContainsString("Testcharacter 4", $output);
}
public function testIfIncludeSoftDeletedOptionsAlsoShowsSoftDeletedCharacters()
{
$command = $this->getCommand();
$command->execute([
"--includeSoftDeleted" => true
]);
$output = $command->getDisplay();
// Assertions
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Testcharacter 1", $output);
$this->assertStringContainsString("Testcharacter 2", $output);
$this->assertStringContainsString("*Testcharacter 3", $output);
$this->assertStringContainsString("Testcharacter 4", $output);
}
public function testIfOnlySoftDeletedOptionOptionOnlyShowsSoftDeletedEntries()
{
$command = $this->getCommand();
$command->execute([
"--onlySoftDeleted" => true
]);
$output = $command->getDisplay();
// Assertions
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringNotContainsString("Testcharacter 1", $output);
$this->assertStringNotContainsString("Testcharacter 2", $output);
$this->assertStringContainsString("Testcharacter 3", $output);
$this->assertStringNotContainsString("*Testcharacter 3", $output);
$this->assertStringNotContainsString("Testcharacter 4", $output);
}
}
@@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterRemoveCommand;
use LotGD\Core\Models\Character;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
class CharacterRemoveCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "character";
protected function getCommand(): CommandTester
{
return new CommandTester(new CharacterRemoveCommand($this->g));
}
public function testIfCommandFailesIfCharacterIdHasNotBeenFound()
{
$command = $this->getCommand();
$command->execute(["id" => "10000000-0000-0000-0000-000000000000"]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
}
public function testIfNormalCharacterGetsDeleted()
{
$command = $this->getCommand();
$command->execute(["id" => "10000000-0000-0000-0000-000000000001"]);
$output = $command->getDisplay();
// Assertions
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString(" was successfully removed.", $output);
$this->assertStringContainsString("Testcharacter 1", $output);
// Check database, too
$em = $this->g->getEntityManager();
$em->clear();
$character = $em->getRepository(Character::class)->findWithSoftDeleted("10000000-0000-0000-0000-000000000001");
$this->assertNull($character);
}
public function testIfSoftDeletedCharacterGetsDeleted()
{
$command = $this->getCommand();
$command->execute(["id" => "10000000-0000-0000-0000-000000000003"]);
$output = $command->getDisplay();
// Assertions
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("Character was marked as soft-deleted.", $output);
$this->assertStringContainsString(" was successfully removed.", $output);
$this->assertStringContainsString("Testcharacter 3", $output);
// Check database, too
$em = $this->g->getEntityManager();
$em->clear();
$character = $em->getRepository(Character::class)->findWithSoftDeleted("10000000-0000-0000-0000-000000000003");
$this->assertNull($character);
}
public function testIfSoftDeletionFlagOnlySoftDeletesACharacter()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000002",
"--soft" => true,
]);
$output = $command->getDisplay();
// Assertions
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("was successfully soft-deleted.", $output);
$this->assertStringContainsString("Testcharacter 2", $output);
// Check database, too
$em = $this->g->getEntityManager();
$em->clear();
$character = $em->getRepository(Character::class)->find("10000000-0000-0000-0000-000000000002");
$this->assertNull($character);
$character = $em->getRepository(Character::class)->findWithSoftDeleted("10000000-0000-0000-0000-000000000002");
$this->assertNotNull($character);
}
}
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterResetViewpointCommand;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
class CharacterResetViewpointCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "viewpoints";
protected function getCommand(): CommandTester
{
return new CommandTester(new CharacterResetViewpointCommand($this->g));
}
public function testIfCommandFailesIfCharacterIdHasNotBeenFound()
{
$command = $this->getCommand();
$command->execute(["id" => "10000000-0000-0000-0000-000000000000"]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
}
public function testIfCommandReportsIfCharacterDoesNotHaveAViewpoint()
{
$command = $this->getCommand();
$command->execute(["id" => "10000000-0000-0000-0000-000000000001"]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringNotContainsString("[OK]", $output);
$this->assertStringContainsString("[INFO]", $output);
$this->assertStringContainsString("Character does not have a viewpoint yet.", $output);
}
public function testIfCommandSucceedsOfCharacterHasAViewpoint()
{
$command = $this->getCommand();
$command->execute(["id" => "10000000-0000-0000-0000-000000000002"]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[INFO]", $output);
$this->assertStringContainsString("Viewpoint was successfully reset.", $output);
}
}
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterShowCommand;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
class CharacterShowCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "viewpoints";
protected function getCommand(): CommandTester
{
return new CommandTester(new CharacterShowCommand($this->g));
}
public function testIfCommandFailesIfCharacterIdHasNotBeenFound()
{
$command = $this->getCommand();
$command->execute(["id" => "10000000-0000-0000-0000-000000000000"]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
}
public function testIfCharacterShowCommandShowsCharacterInformationIfCharacterExists()
{
$command = $this->getCommand();
$command->execute(["id" => "10000000-0000-0000-0000-000000000001"]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("About Character Char without a Scene", $output);
$this->assertStringContainsString("No viewpoint yet", $output);
}
public function testIfCharacterWithViewpointGetsViewpointDisplayed()
{
$command = $this->getCommand();
$command->execute(["id" => "10000000-0000-0000-0000-000000000002"]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("About Character Char with a Scene", $output);
$this->assertStringNotContainsString("No viewpoint yet", $output);
$this->assertStringContainsString("The Village", $output);
$this->assertStringContainsString("This is the village.", $output);
}
public function testIfCharacterWithViewpointGetsOnlyViewpointDisplayedIfOptionIsGiven()
{
$command = $this->getCommand();
$command->execute([
"id" => "10000000-0000-0000-0000-000000000002",
"--onlyViewpoint" => true,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringNotContainsString("About Character Char with a Scene", $output);
$this->assertStringNotContainsString("No viewpoint yet", $output);
$this->assertStringContainsString("The Village", $output);
$this->assertStringContainsString("This is the village.", $output);
}
}
@@ -0,0 +1,124 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\ModuleCommands;
use LotGD\Core\Console\Command\Module\ModuleConfigListCommand;
use LotGD\Core\EventManager;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Game;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class ModuleConfigListCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "module-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new ModuleConfigListCommand($this->g));
}
public function testIfCommandRunsWithoutRegisteredEvents()
{
$command = $this->getCommand();
$command->execute([
"moduleName" => "lotgd/tests"
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("This module does not provide any settings.", $output);
}
public function testIfCommandEmitsEvent()
{
/** @var Game $game */
$game = $this->g;
$modules = [
"lotgd/tests",
"lotgd/tests-other"
];
foreach ($modules as $module) {
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->with(
$this->equalTo("h/lotgd/core/cli/module-config-list/{$module}"),
$this->callback(function (EventContextData $eventContextData) use ($module) {
$pass = 1;
$pass &= $eventContextData->has("module") === true;
$pass &= $eventContextData->get("module")->getLibrary() === $module;
$pass &= $eventContextData->has("io") === true;
$pass &= $eventContextData->get("io") instanceof SymfonyStyle;
$pass &= $eventContextData->has("settings") === true;
$pass &= $eventContextData->get("settings") === [];
return $pass == true;
}),
)->will($this->returnArgument(1));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"moduleName" => $module,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Module {$module}", $output);
$this->assertStringContainsString("This module does not provide any settings.", $output);
}
}
public function testIfCommandDisplaysSettingsWhenGivenByEvent()
{
/** @var Game $game */
$game = $this->g;
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->will($this->returnCallback(function (string $a, EventContextData $b) {
$newSettings = [
["setting1", "0.00000000000000000000000001", "float 0..1, chance to succeed"],
["setting2", "DragonMillions", "string, name of the lottery"],
];
$settings = $b->get("settings");
$settings = [...$settings, ...$newSettings];
return $b->set("settings", $settings);
}));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"moduleName" => "lotgd/tests"
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("setting1", $output);
$this->assertStringContainsString("0.00000000000000000000000001", $output);
$this->assertStringContainsString("float 0..1, chance to succeed", $output);
$this->assertStringContainsString("setting2", $output);
$this->assertStringContainsString("DragonMillions", $output);
$this->assertStringContainsString("string, name of the lottery", $output);
$this->assertStringNotContainsString("This module does not provide any settings.", $output);
}
}
@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\ModuleCommands;
use LotGD\Core\Console\Command\Module\ModuleConfigResetCommand;
use LotGD\Core\EventManager;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Game;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class ModuleConfigResetCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "module-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new ModuleConfigResetCommand($this->g));
}
public function testIfCommandEmitsEvent()
{
/** @var Game $game */
$game = $this->g;
$modules = [
["lotgd/tests", "test"],
["lotgd/tests-other", "test-other"],
];
foreach ($modules as [$module, $setting]) {
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->with(
$this->equalTo("h/lotgd/core/cli/module-config-reset/{$module}"),
$this->callback(function (EventContextData $eventContextData) use ($module, $setting) {
$pass = 1;
$pass &= $eventContextData->has("module") === true;
$pass &= $eventContextData->get("module")->getLibrary() === $module;
$pass &= $eventContextData->has("io") === true;
$pass &= $eventContextData->get("io") instanceof SymfonyStyle;
$pass &= $eventContextData->has("setting") === true;
$pass &= $eventContextData->get("setting") === $setting;
$pass &= $eventContextData->has("return") === true;
$pass &= $eventContextData->get("return") === 1;
$pass &= $eventContextData->has("reason") === true;
$pass &= $eventContextData->get("reason") === "Setting does not exist.";
return $pass == true;
}),
)->will($this->returnArgument(1));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"moduleName" => $module,
"setting" => $setting,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("Module lotgd/tests", $output);
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("[OK]", $output);
$this->assertStringContainsString("Setting does not exist.", $output);
}
}
public function testIfCommandSucceedsWhenReturnedCallbackIsSetToSuccess()
{
/** @var Game $game */
$game = $this->g;
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->will($this->returnCallback(function (string $a, EventContextData $b) {
return $b->set("return", Command::SUCCESS);
}));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"moduleName" => "lotgd/tests",
"setting" => "Setting",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Module lotgd/tests", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("Setting does not exist.", $output);
}
}
@@ -0,0 +1,115 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\ModuleCommands;
use LotGD\Core\Console\Command\Module\ModuleConfigSetCommand;
use LotGD\Core\EventManager;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Game;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class ModuleConfigSetCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "module-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new ModuleConfigSetCommand($this->g));
}
public function testIfCommandEmitsEvent()
{
/** @var Game $game */
$game = $this->g;
$modules = [
["lotgd/tests", "test", "0.126"],
["lotgd/tests-other", "test-other", "hi"],
];
foreach ($modules as [$module, $setting, $value]) {
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->with(
$this->equalTo("h/lotgd/core/cli/module-config-set/{$module}"),
$this->callback(function (EventContextData $eventContextData) use ($module, $setting, $value) {
$pass = 1;
$pass &= $eventContextData->has("module") === true;
$pass &= $eventContextData->get("module")->getLibrary() === $module;
$pass &= $eventContextData->has("io") === true;
$pass &= $eventContextData->get("io") instanceof SymfonyStyle;
$pass &= $eventContextData->has("setting") === true;
$pass &= $eventContextData->get("setting") === $setting;
$pass &= $eventContextData->has("value") === true;
$pass &= $eventContextData->get("value") === $value;
$pass &= $eventContextData->has("return") === true;
$pass &= $eventContextData->get("return") === 1;
$pass &= $eventContextData->has("reason") === true;
$pass &= $eventContextData->get("reason") === "Setting does not exist.";
return $pass == true;
}),
)->will($this->returnArgument(1));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"moduleName" => $module,
"setting" => $setting,
"value" => $value,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("Module {$module}", $output);
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Setting does not exist.", $output);
}
}
public function testIfCommandSucceedsWhenReturnedCallbackIsSetToSuccess()
{
/** @var Game $game */
$game = $this->g;
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->will($this->returnCallback(function (string $a, EventContextData $b) {
return $b->set("return", Command::SUCCESS);
}));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"moduleName" => "lotgd/tests",
"setting" => "Setting",
"value" => 13,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Module lotgd/tests", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("Setting does not exist.", $output);
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Module\ModuleListCommand;
use LotGD\Core\Models\Module;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
class ModuleListCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "module-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new ModuleListCommand($this->g));
}
public function testIfCommandRunsWithModulesInstalled()
{
$command = $this->getCommand();
$command->execute([]);
$output = $command->getDisplay();
// Assertions
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("lotgd/tests", $output);
$this->assertStringContainsString("lotgd/tests-other", $output);
$this->assertStringNotContainsString("No modules installed.", $output);
}
public function testIfCommandRunsWithNoModulesInstalled()
{
// Remove modules first
$modules = $this->g->getEntityManager()->getRepository(Module::class)->findAll();
foreach ($modules as $module) {
$this->g->getEntityManager()->remove($module);
$this->g->getEntityManager()->flush();
}
// Run command
$command = $this->getCommand();
$command->execute([]);
$output = $command->getDisplay();
// Assert
$this->assertStringContainsString("No modules installed.", $output);
}
}
@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\SceneCommands;
use LotGD\Core\Console\Command\Scene\SceneAddCommand;
use LotGD\Core\Models\Scene;
use LotGD\Core\Tests\CoreModelTestCase;
use LotGD\Core\Tests\SceneTemplates\NewSceneSceneTemplate;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
class SceneAddCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneAddCommand($this->g));
}
public function testIfCommandFailsIfNoNameWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([]);
}
public function testIfSceneGetsCreatedWithOnlyName()
{
$repository = $this->g->getEntityManager()->getRepository(Scene::class);
$command = $this->getCommand();
$command->execute([
"title" => "A scene.",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("was successfully created.", $output);
// Check the database, too.
$this->g->getEntityManager()->clear();
/** @var Scene $scene */
$scene = $repository->findOneBy(["title" => "A scene."]);
$this->assertNotNull($scene);
$this->assertSame("A scene.", $scene->getTitle());
$this->assertSame("", $scene->getDescription());
$this->assertNull($scene->getTemplate());
}
public function testIfSceneGetsCreatedWithDescription()
{
$repository = $this->g->getEntityManager()->getRepository(Scene::class);
$command = $this->getCommand();
$command->execute([
"title" => "Another scene.",
"description" => "The scenery is nice."
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("was successfully created.", $output);
// Check the database, too.
$this->g->getEntityManager()->clear();
/** @var Scene $scene */
$scene = $repository->findOneBy(["title" => "Another scene."]);
$this->assertNotNull($scene);
$this->assertSame("Another scene.", $scene->getTitle());
$this->assertSame("The scenery is nice.", $scene->getDescription());
$this->assertNull($scene->getTemplate());
}
public function testIfSceneGetsCreatedWithValidSceneTemplate()
{
$repository = $this->g->getEntityManager()->getRepository(Scene::class);
$command = $this->getCommand();
$command->execute([
"title" => "A templated scene.",
"description" => "The scenery is nice.",
"--template" => "LotGD\\Core\\Tests\\SceneTemplates\\NewSceneSceneTemplate"
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("was successfully created.", $output);
$this->assertStringNotContainsString("[WARNING]", $output);
$this->assertStringNotContainsString("has not been found. Set to NULL instead.", $output);
// Check the database, too.
$this->g->getEntityManager()->clear();
/** @var Scene $scene */
$scene = $repository->findOneBy(["title" => "A templated scene."]);
$this->assertNotNull($scene);
$this->assertSame("A templated scene.", $scene->getTitle());
$this->assertSame("The scenery is nice.", $scene->getDescription());
$this->assertSame(NewSceneSceneTemplate::class, $scene->getTemplate()?->getClass());
}
public function testIfSceneGetsCreatedWithInvalidSceneTemplateButThrowsWarning()
{
$repository = $this->g->getEntityManager()->getRepository(Scene::class);
$command = $this->getCommand();
$command->execute([
"title" => "A wrongly templated scene.",
"description" => "The scenery is nice.",
"--template" => "LotGD\\Core\\Tests\\SceneTemplates\\Darn"
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("was successfully created.", $output);
$this->assertStringContainsString("[WARNING]", $output);
$this->assertStringContainsString("has not been found. Set to NULL instead.", $output);
// Check the database, too.
$this->g->getEntityManager()->clear();
/** @var Scene $scene */
$scene = $repository->findOneBy(["title" => "A wrongly templated scene."]);
$this->assertNotNull($scene);
$this->assertSame("A wrongly templated scene.", $scene->getTitle());
$this->assertSame("The scenery is nice.", $scene->getDescription());
$this->assertNull($scene->getTemplate());
}
}
@@ -0,0 +1,89 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\SceneCommands;
use LotGD\Core\Console\Command\Scene\SceneAddConnectionGroupCommand;
use LotGD\Core\Models\Scene;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
class SceneAddConnectionGroupCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneAddConnectionGroupCommand($this->g));
}
public function testIfCommandFailsIfNoNameWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([]);
}
public function testIfCommandFailsIfSceneWasNotFound()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000000",
"groupName" => "a/group/name",
"groupTitle" => "The Abyss",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("[OK]", $output);
$this->assertStringContainsString("The requested scene with the ID 30000000-0000-0000-0000-000000000000 was not found.", $output);
}
public function testIfCommandGetsSuccessfullyAddedToScene()
{
$repository = $this->g->getEntityManager()->getRepository(Scene::class);
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
"groupName" => "a/group/name",
"groupTitle" => "The Abyss",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringContainsString("successfully added.", $output);
// Check the database, too.
$this->g->getEntityManager()->clear();
/** @var Scene $scene */
$scene = $repository->find("30000000-0000-0000-0000-000000000001");
$this->assertNotNull($scene);
$this->assertTrue($scene->hasConnectionGroup("a/group/name"));
$this->assertNotNull($scene->getConnectionGroup("a/group/name"));
$this->assertSame("The Abyss", $scene->getConnectionGroup("a/group/name")?->getTitle());
}
public function testIfCommandFailsIfGroupNameIsAlreadyInUse()
{
$repository = $this->g->getEntityManager()->getRepository(Scene::class);
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
"groupName" => "lotgd/tests/village/outside",
"groupTitle" => "The Abyss",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringNotContainsString("[OK]", $output);
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Cannot add a second group with the same name to this scene.", $output);
}
}
@@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
use LotGD\Core\Console\Command\Scene\SceneConfigListCommand;
use LotGD\Core\EventManager;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Game;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class SceneConfigListCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneConfigListCommand($this->g));
}
public function testIfCommandRunsWithoutRegisteredEvents()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("There are no scene settings available.", $output);
}
public function testIfCommandEmitsEvent()
{
/** @var Game $game */
$game = $this->g;
$scenes = [
["30000000-0000-0000-0000-000000000001", "The Village", "tests/village"],
["30000000-0000-0000-0000-000000000002", "The Forest", "tests/forest"],
];
foreach ($scenes as [$scene, $sceneTitle, $path]) {
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(['publish'])
->getMock();
$eventManager->expects($this->once())
->method('publish')
->with(
$this->equalTo("h/lotgd/core/cli/scene-config-list/{$path}"),
$this->callback(function (EventContextData $eventContextData) use ($scene, $sceneTitle) {
$pass = 1;
$pass &= $eventContextData->has("scene") === true;
$pass &= $eventContextData->get("scene")->getTitle() === $sceneTitle;
$pass &= $eventContextData->has("io") === true;
$pass &= $eventContextData->get("io") instanceof SymfonyStyle;
$pass &= $eventContextData->has("settings") === true;
$pass &= $eventContextData->get("settings") === [];
return $pass == true;
}),
)->will($this->returnArgument(1));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => $scene,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Scene {$sceneTitle}", $output);
$this->assertStringContainsString("There are no scene settings available.", $output);
}
}
public function testIfCommandDisplaysSettingsWhenGivenByEvent()
{
/** @var Game $game */
$game = $this->g;
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(['publish'])
->getMock();
$eventManager->expects($this->once())
->method('publish')
->will($this->returnCallback(function (string $a, EventContextData $b) {
$newSettings = [
["setting1", "0.00000000000000000000000001", "float 0..1, chance to succeed"],
["setting2", "DragonMillions", "string, name of the lottery"],
];
$settings = $b->get("settings");
$settings = [...$settings, ...$newSettings];
return $b->set("settings", $settings);
}));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("setting1", $output);
$this->assertStringContainsString("0.00000000000000000000000001", $output);
$this->assertStringContainsString("float 0..1, chance to succeed", $output);
$this->assertStringContainsString("setting2", $output);
$this->assertStringContainsString("DragonMillions", $output);
$this->assertStringContainsString("string, name of the lottery", $output);
$this->assertStringNotContainsString("There are no scene settings available.", $output);
}
}
@@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\SceneCommands;
use LotGD\Core\Console\Command\Character\CharacterConfigResetCommand;
use LotGD\Core\Console\Command\Scene\SceneConfigResetCommand;
use LotGD\Core\EventManager;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Game;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class SceneConfigResetCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneConfigResetCommand($this->g));
}
public function testIfCommandEmitsEvent()
{
/** @var Game $game */
$game = $this->g;
$scenes = [
["30000000-0000-0000-0000-000000000001", "The Village", "tests/village", "test"],
["30000000-0000-0000-0000-000000000002", "The Forest", "tests/forest", "test-other"],
];
foreach ($scenes as [$scene, $sceneTitle, $path, $setting]) {
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->with(
$this->equalTo("h/lotgd/core/cli/scene-config-reset/{$path}"),
$this->callback(function (EventContextData $eventContextData) use ($scene, $sceneTitle, $setting) {
$pass = 1;
$pass &= $eventContextData->has("scene") === true;
$pass &= $eventContextData->get("scene")->getTitle() === $sceneTitle;
$pass &= $eventContextData->has("io") === true;
$pass &= $eventContextData->get("io") instanceof SymfonyStyle;
$pass &= $eventContextData->has("setting") === true;
$pass &= $eventContextData->get("setting") === $setting;
$pass &= $eventContextData->has("return") === true;
$pass &= $eventContextData->get("return") === 1;
$pass &= $eventContextData->has("reason") === true;
$pass &= $eventContextData->get("reason") === "Setting does not exist.";
return $pass == true;
}),
)->will($this->returnArgument(1));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => $scene,
"setting" => $setting,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("Scene {$sceneTitle}", $output);
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("[OK]", $output);
$this->assertStringContainsString("Setting does not exist.", $output);
}
}
public function testIfCommandSucceedsWhenReturnedCallbackIsSetToSuccess()
{
/** @var Game $game */
$game = $this->g;
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->will($this->returnCallback(function (string $a, EventContextData $b) {
return $b->set("return", Command::SUCCESS);
}));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
"setting" => "Setting",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Scene The Village", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("Setting does not exist.", $output);
}
}
@@ -0,0 +1,116 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\CharacterCommands;
use LotGD\Core\Console\Command\Character\CharacterConfigSetCommand;
use LotGD\Core\Console\Command\Scene\SceneConfigSetCommand;
use LotGD\Core\EventManager;
use LotGD\Core\Events\EventContextData;
use LotGD\Core\Game;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Console\Tester\CommandTester;
class SceneConfigSetCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneConfigSetCommand($this->g));
}
public function testIfCommandEmitsEvent()
{
/** @var Game $game */
$game = $this->g;
$characters = [
["30000000-0000-0000-0000-000000000001", "The Village", "tests/village", "test", "0.126"],
["30000000-0000-0000-0000-000000000002", "The Forest", "tests/forest", "test-other", "hi"],
];
foreach ($characters as [$character, $displayName, $path, $setting, $value]) {
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->with(
$this->equalTo("h/lotgd/core/cli/scene-config-set/{$path}"),
$this->callback(function (EventContextData $eventContextData) use ($character, $displayName, $setting, $value) {
$pass = 1;
$pass &= $eventContextData->has("scene") === true;
$pass &= $eventContextData->get("scene")->getTitle() === $displayName;
$pass &= $eventContextData->has("io") === true;
$pass &= $eventContextData->get("io") instanceof SymfonyStyle;
$pass &= $eventContextData->has("setting") === true;
$pass &= $eventContextData->get("setting") === $setting;
$pass &= $eventContextData->has("value") === true;
$pass &= $eventContextData->get("value") === $value;
$pass &= $eventContextData->has("return") === true;
$pass &= $eventContextData->get("return") === 1;
$pass &= $eventContextData->has("reason") === true;
$pass &= $eventContextData->get("reason") === "Setting does not exist.";
return $pass == true;
}),
)->will($this->returnArgument(1));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => $character,
"setting" => $setting,
"value" => $value,
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("Scene {$displayName}", $output);
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Setting does not exist.", $output);
}
}
public function testIfCommandSucceedsWhenReturnedCallbackIsSetToSuccess()
{
/** @var Game $game */
$game = $this->g;
$eventManager = $this->getMockBuilder(EventManager::class)
->disableOriginalConstructor()
->setMethods(array('publish'))
->getMock();
$eventManager->expects($this->once())
->method('publish')
->will($this->returnCallback(function (string $a, EventContextData $b) {
return $b->set("return", Command::SUCCESS);
}));
$game->setEventManager($eventManager);
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
"setting" => "Setting",
"value" => 13,
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Scene The Village", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("Setting does not exist.", $output);
}
}
@@ -0,0 +1,276 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\SceneCommands;
use LotGD\Core\Console\Command\Scene\SceneConnectCommand;
use LotGD\Core\Models\Scene;
use LotGD\Core\Models\SceneConnection;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
class SceneConnectCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneConnectCommand($this->g));
}
public function testIfCommandFailsIfNoOutgoingOrIncomingSceneWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([]);
}
public function testIfCommandFailsIfNoIncomingSceneWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([
"outgoing" => "30000000-0000-0000-0000-000000000005",
]);
}
public function testIfCommandFailsIfNoOutgoingWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([
"incoming" => "30000000-0000-0000-0000-000000000004",
]);
}
public function testIfCommandFailsIfOutgoingSceneDoesNotExist()
{
$command = $this->getCommand();
$command->execute([
"outgoing" => "30000000-0000-0000-0000-000000000000",
"incoming" => "30000000-0000-0000-0000-000000000004",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringNotContainsString("[OK]", $output);
$this->assertStringContainsString("[ERROR]", $output);
}
public function testIfCommandFailsIfIncomingSceneDoesNotExist()
{
$command = $this->getCommand();
$command->execute([
"outgoing" => "30000000-0000-0000-0000-000000000005",
"incoming" => "30000000-0000-0000-0000-000000000000",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringNotContainsString("[OK]", $output);
$this->assertStringContainsString("[ERROR]", $output);
}
public function testIfScenesGetConnected()
{
$command = $this->getCommand();
$command->execute([
"outgoing" => "30000000-0000-0000-0000-000000000005",
"incoming" => "30000000-0000-0000-0000-000000000004",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringContainsString("The two scenes were successfully connected.", $output);
$this->getEntityManager()->clear();
$sceneRepository = $this->getEntityManager()->getRepository(Scene::class);
/** @var Scene $outgoing */
$outgoing = $sceneRepository->find("30000000-0000-0000-0000-000000000005");
/** @var Scene $outgoing */
$incoming = $sceneRepository->find("30000000-0000-0000-0000-000000000004");
$this->assertTrue($outgoing->isConnectedTo($incoming));
}
public function testIfCommandFailsIfScenesAreAlreadyConnected()
{
$command = $this->getCommand();
$command->execute([
"outgoing" => "30000000-0000-0000-0000-000000000001",
"incoming" => "30000000-0000-0000-0000-000000000002",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringNotContainsString("[OK]", $output);
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Scenes were not connected. Reason: The given scene (ID 30000000-0000-0000-0000-000000000002) is already", $output);
$this->assertStringContainsString("connected to this (ID 30000000-0000-0000-0000-000000000001) one..", $output);
}
public function testIfScenesGetConnectedOnOutgoingConnectionGroup()
{
$command = $this->getCommand();
$command->execute([
"outgoing" => "30000000-0000-0000-0000-000000000001",
"incoming" => "30000000-0000-0000-0000-000000000005",
"--outgoingGroupName" => "lotgd/tests/village/outside",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringContainsString("The two scenes were successfully connected.", $output);
$this->getEntityManager()->clear();
$sceneRepository = $this->getEntityManager()->getRepository(Scene::class);
/** @var Scene $outgoing */
$outgoing = $sceneRepository->find("30000000-0000-0000-0000-000000000001");
/** @var Scene $outgoing */
$incoming = $sceneRepository->find("30000000-0000-0000-0000-000000000005");
$this->assertTrue($outgoing->isConnectedTo($incoming));
$thatOne = null;
/** @var SceneConnection $connection */
foreach ($outgoing->getConnections() as $connection) {
if (
$connection->getOutgoingConnectionGroupName() === "lotgd/tests/village/outside"
and $connection->getIncomingScene() === $incoming
) {
$thatOne = true;
}
}
$this->assertTrue($thatOne);
}
public function testIfScenesGetConnectedOnIncomingConnectionGroup()
{
$command = $this->getCommand();
$command->execute([
"outgoing" => "30000000-0000-0000-0000-000000000005",
"incoming" => "30000000-0000-0000-0000-000000000002",
"--incomingGroupName" => "lotgd/tests/forest/category",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringContainsString("The two scenes were successfully connected.", $output);
$this->getEntityManager()->clear();
$sceneRepository = $this->getEntityManager()->getRepository(Scene::class);
/** @var Scene $outgoing */
$outgoing = $sceneRepository->find("30000000-0000-0000-0000-000000000005");
/** @var Scene $outgoing */
$incoming = $sceneRepository->find("30000000-0000-0000-0000-000000000002");
$this->assertTrue($outgoing->isConnectedTo($incoming));
$thatOne = null;
/** @var SceneConnection $connection */
foreach ($outgoing->getConnections() as $connection) {
if (
$connection->getIncomingConnectionGroupName() === "lotgd/tests/forest/category"
and $connection->getIncomingScene() === $incoming
) {
$thatOne = true;
}
}
$this->assertTrue($thatOne);
}
public function testIfScenesGetConnectedOnBothConnectionGroup()
{
$command = $this->getCommand();
$command->execute([
"outgoing" => "30000000-0000-0000-0000-000000000002",
"incoming" => "30000000-0000-0000-0000-000000000003",
"--outgoingGroupName" => "lotgd/tests/forest/category",
"--incomingGroupName" => "lotgd/tests/weaponry/category",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringContainsString("The two scenes were successfully connected.", $output);
$this->getEntityManager()->clear();
$sceneRepository = $this->getEntityManager()->getRepository(Scene::class);
/** @var Scene $outgoing */
$outgoing = $sceneRepository->find("30000000-0000-0000-0000-000000000002");
/** @var Scene $outgoing */
$incoming = $sceneRepository->find("30000000-0000-0000-0000-000000000003");
$this->assertTrue($outgoing->isConnectedTo($incoming));
$thatOne = null;
/** @var SceneConnection $connection */
foreach ($outgoing->getConnections() as $connection) {
if (
$connection->getOutgoingConnectionGroupName() === "lotgd/tests/forest/category"
and $connection->getIncomingConnectionGroupName() === "lotgd/tests/weaponry/category"
and $connection->getIncomingScene() === $incoming
) {
$thatOne = true;
}
}
$this->assertTrue($thatOne);
}
public function testIfUnidirectionalSceneConnectionWorks()
{
$command = $this->getCommand();
$command->execute([
"outgoing" => "30000000-0000-0000-0000-000000000004",
"incoming" => "30000000-0000-0000-0000-000000000002",
"--directionality" => "1",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringContainsString("The two scenes were successfully connected.", $output);
$this->getEntityManager()->clear();
$sceneRepository = $this->getEntityManager()->getRepository(Scene::class);
/** @var Scene $outgoing */
$outgoing = $sceneRepository->find("30000000-0000-0000-0000-000000000004");
/** @var Scene $outgoing */
$incoming = $sceneRepository->find("30000000-0000-0000-0000-000000000002");
$thatOne = null;
/** @var SceneConnection $connection */
foreach ($outgoing->getConnections() as $connection) {
if (
$connection->getIncomingScene() === $incoming
and $connection->isDirectionality(1)
) {
$thatOne = true;
}
}
$this->assertTrue($thatOne);
}
}
@@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\SceneCommands;
use LotGD\Core\Console\Command\Scene\SceneDisconnectCommand;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
class SceneDisconnectCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneDisconnectCommand($this->g));
}
public function testIfCommandFailsIfNoSceneWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([]);
}
public function testIfCommandFailsIfScene1WasNotGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([
"scene2" => "A"
]);
}
public function testIfCommandFailsIfScene2WasNotGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([
"scene1" => "A"
]);
}
public function testIfCommandFailsIfScene1WasNotFound()
{
$command = $this->getCommand();
$command->execute([
"scene1" => "30000000-0000-0000-0000-000000000000",
"scene2" => "30000000-0000-0000-0000-000000000005",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Scene with id 30000000-0000-0000-0000-000000000000 was not found.", $output);
$this->assertStringNotContainsString("[OK]", $output);
}
public function testIfCommandFailsIfScene2WasNotFound()
{
$command = $this->getCommand();
$command->execute([
"scene1" => "30000000-0000-0000-0000-000000000005",
"scene2" => "30000000-0000-0000-0000-000000000000",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("Scene with id 30000000-0000-0000-0000-000000000000 was not found.", $output);
$this->assertStringNotContainsString("[OK]", $output);
}
public function testIfCommandFailsWhenBothScenesDontShareAConnection()
{
$command = $this->getCommand();
$command->execute([
"scene1" => "30000000-0000-0000-0000-000000000005",
"scene2" => "30000000-0000-0000-0000-000000000001",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("The given scenes do not share a connection.", $output);
$this->assertStringNotContainsString("[OK]", $output);
}
public function testIfCommandSucceedsWhenBothScenesShareAConnection()
{
$command = $this->getCommand();
$command->execute([
"scene1" => "30000000-0000-0000-0000-000000000001",
"scene2" => "30000000-0000-0000-0000-000000000002",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("The connections between the two given scenes was removed.", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
}
}
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\SceneCommands;
use LotGD\Core\Console\Command\Scene\SceneListCommand;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Tester\CommandTester;
class SceneListCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneListCommand($this->g));
}
public function testIfCommandSucceedsWithoutAArguments()
{
$command = $this->getCommand();
$command->execute([]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$dataset = $this->getDataSet();
$databaseScenes = $dataset["scenes"];
$databaseSceneConnections = $dataset["scene_connections"];
$connections = [];
foreach ($databaseSceneConnections as $connection) {
if (!isset($connections[$connection["outgoingScene"]])) {
$connections[$connection["outgoingScene"]] = 0;
}
$connections[$connection["outgoingScene"]] += 1;
if (!isset($connections[$connection["incomingScene"]])) {
$connections[$connection["incomingScene"]] = 0;
}
$connections[$connection["incomingScene"]] += 1;
}
foreach ($databaseScenes as $scene) {
// Assert details on the list
$this->assertStringContainsString($scene["id"], $output);
$this->assertStringContainsString($scene["title"], $output);
if ($scene["template"]) {
$this->assertStringContainsString($scene["template"], $output);
}
if (isset($connections[$scene["id"]])) {
$this->assertStringContainsString((string)$connections[$scene["id"]], $output);
} else {
$this->assertStringContainsString("0", $output);
}
}
}
}
@@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\SceneCommands;
use LotGD\Core\Console\Command\Scene\SceneRemoveCommand;
use LotGD\Core\Models\Scene;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
class SceneRemoveCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneRemoveCommand($this->g));
}
public function testIfCommandFailsIfNoSceneIdWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([]);
}
public function testIfCommandFailsIfSceneIdDoesNotExist()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000000",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("The scene with the ID 30000000-0000-0000-0000-000000000000 was not found.", $output);
$this->assertStringNotContainsString("[OK]", $output);
}
public function testIfCommandFailsIfSceneWasMarkedAsNotRemoveable()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000005",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("The scene with the ID 30000000-0000-0000-0000-000000000005 was marked as not ", $output);
$this->assertStringNotContainsString("[OK]", $output);
}
public function testIfCommandSucceeds()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringContainsString("was successfully removed.", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
// Assert database result
$this->getEntityManager()->clear();
$this->assertNull($this->getEntityManager()->getRepository(Scene::class)->find("30000000-0000-0000-0000-000000000001"));
}
}
@@ -0,0 +1,139 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\SceneCommands;
use LotGD\Core\Console\Command\Scene\SceneRemoveConnectionGroupCommand;
use LotGD\Core\Models\Scene;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
class SceneRemoveConnectionGroupCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneRemoveConnectionGroupCommand($this->g));
}
public function testIfCommandFailsIfNoSceneIdWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([]);
}
public function testIfCommandFailsIfSceneIdDoesNotExist()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000000",
"groupName" => "nobody/nothing",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("The scene with the ID 30000000-0000-0000-0000-000000000000 was not found.", $output);
$this->assertStringNotContainsString("[OK]", $output);
}
public function testIfCommandFailsWhenSceneDoesNotHaveGivenConnectionGroup()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
"groupName" => "nobody/nothing",
]);
$output = $command->getDisplay();
$this->assertSame(Command::FAILURE, $command->getStatusCode());
$this->assertStringContainsString("[ERROR]", $output);
$this->assertStringContainsString("The scene 30000000-0000-0000-0000-000000000001 does not have a connection group with the name", $output);
$this->assertStringNotContainsString("[OK]", $output);
}
public function testIfCommandSuccessfullyRemovesGroup()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
"groupName" => "lotgd/tests/village/outside",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringNotContainsString("//", $output);
$this->assertStringContainsString(" was successfully removed", $output);
// Assert on database level (make sure command calls flush)
$this->getEntityManager()->clear();
/** @var Scene $scene */
$scene = $this->getEntityManager()->getRepository(Scene::class)->find("30000000-0000-0000-0000-000000000001");
$this->assertNotNull($scene);
$this->assertFalse($scene->hasConnectionGroup("lotgd/tests/village/outside"));
}
public function testIfCommandSuccessfullyRemovesGroupAndMovesConnectionDirectlyToTheOutgoingScene()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000001",
"groupName" => "lotgd/tests/village/market",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringContainsString("//", $output);
$this->assertStringContainsString("Updated connection to", $output);
$this->assertStringContainsString(" was successfully removed", $output);
// Assert on database level (make sure command calls flush)
$this->getEntityManager()->clear();
/** @var Scene $scene */
$scene = $this->getEntityManager()->getRepository(Scene::class)->find("30000000-0000-0000-0000-000000000001");
$otherScene = $this->getEntityManager()->getRepository(Scene::class)->find("30000000-0000-0000-0000-000000000003");
$this->assertNotNull($scene);
$connection = $scene->getConnectionTo($otherScene);
$this->assertNotNull($connection);
$this->assertNull($connection->getOutgoingConnectionGroupName());
$this->assertFalse($scene->hasConnectionGroup("lotgd/tests/village/market"));
}
public function testIfCommandSuccessfullyRemovesGroupAndMovesConnectionDirectlyToTheIncomingScene()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000003",
"groupName" => "lotgd/tests/weaponry/category",
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("[OK]", $output);
$this->assertStringNotContainsString("[ERROR]", $output);
$this->assertStringContainsString("//", $output);
$this->assertStringContainsString("Updated connection to", $output);
$this->assertStringContainsString(" was successfully removed", $output);
// Assert on database level (make sure command calls flush)
$this->getEntityManager()->clear();
/** @var Scene $scene */
$scene = $this->getEntityManager()->getRepository(Scene::class)->find("30000000-0000-0000-0000-000000000001");
$otherScene = $this->getEntityManager()->getRepository(Scene::class)->find("30000000-0000-0000-0000-000000000003");
$this->assertNotNull($scene);
$connection = $scene->getConnectionTo($otherScene);
$this->assertNotNull($connection);
$this->assertNull($connection->getIncomingConnectionGroupName());
$this->assertFalse($scene->hasConnectionGroup("lotgd/tests/weaponry/category"));
}
}
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\Commands\SceneCommands;
use LotGD\Core\Console\Command\Scene\SceneShowCommand;
use LotGD\Core\Tests\CoreModelTestCase;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Tester\CommandTester;
class SceneShowCommandTest extends CoreModelTestCase
{
/** @var string default data set */
protected $dataset = "scene-2";
protected function getCommand(): CommandTester
{
return new CommandTester(new SceneShowCommand($this->g));
}
public function testIfCommandFailsIfNoSceneIdWasGiven()
{
$command = $this->getCommand();
$this->expectException(RuntimeException::class);
$command->execute([]);
}
public function testIfCommandSucceedsIfIdWasFound()
{
$command = $this->getCommand();
$command->execute([
"id" => "30000000-0000-0000-0000-000000000006"
]);
$output = $command->getDisplay();
$this->assertSame(Command::SUCCESS, $command->getStatusCode());
$this->assertStringContainsString("Connection Test Scene", $output);
$this->assertStringContainsString("30000000-0000-0000-0000-000000000006", $output);
$this->assertStringContainsString("LotGD\\Core\\Tests\\SceneTemplates\\VillageSceneTemplate", $output);
$this->assertStringContainsString("This is a connection test scene", $output);
$this->assertStringContainsString("Group One (id=lotgd/tests/testscene/one)", $output);
$this->assertStringContainsString("Group Two (id=lotgd/tests/testscene/two)", $output);
$this->assertStringContainsString("Group Three (id=lotgd/tests/testscene/three)", $output);
$this->assertStringContainsString("this <=> The Village (id=30000000-0000-0000-0000-000000000001)", $output);
$this->assertStringContainsString("this (on lotgd/tests/testscene/three) => The Weaponry (id=30000000-0000-0000-0000-000000000003)", $output);
$this->assertStringContainsString("this (on lotgd/tests/testscene/three) <= The Forest (id=30000000-0000-0000-0000-000000000002)", $output);
}
}
+1
View File
@@ -20,6 +20,7 @@ use LotGD\Core\{Action,
Game,
GameBuilder,
Tests\SceneTemplates\ParameterTestSceneTemplate,
Tests\SceneTemplates\ForestSceneTemplate,
Tests\SceneTemplates\VillageSceneTemplate,
TimeKeeper,
ModuleManager};
+3 -3
View File
@@ -32,7 +32,7 @@ class CharacterModelTest extends CoreModelTestCase
$this->assertSame(null, $chars);
$allChars = $this->getEntityManager()->getRepository(Character::class)->findAll();
$this->assertSame(2, count($allChars));
$this->assertSame(3, count($allChars));
$char = $this->getEntityManager()->getRepository(Character::class)->find("10000000-0000-0000-0000-000000000001");
$char->delete($this->getEntityManager());
@@ -43,12 +43,12 @@ class CharacterModelTest extends CoreModelTestCase
$allChars = $this->getEntityManager()
->getRepository(Character::class)
->findAll();
$this->assertSame(1, count($allChars));
$this->assertSame(2, count($allChars));
$allChars = $this->getEntityManager()
->getRepository(Character::class)
->findAll(CharacterRepository::INCLUDE_SOFTDELETED);
$this->assertSame(3, count($allChars));
$this->assertSame(4, count($allChars));
}
/**
+30
View File
@@ -27,6 +27,36 @@ class ScenePropertyTest extends CoreModelTestCase
$this->assertTrue($value);
}
public function testIfUnknownScenePropertyCanBeRetrieved()
{
$em = $this->getEntityManager();
# Retrieve scene
$scene = $em->getRepository(Scene::class)->find("30000000-0000-0000-0000-000000000002");
# Fetch property, with default "false".
# Must return true, as this is whats in our dataset
$value = $scene->getProperty("lotgd/core/tests/property/dataset");
# Assert the value
$this->assertNull($value);
}
public function testIfFreshlyCreatedSceneCanGetAccessedProperties()
{
$em = $this->getEntityManager();
# Retrieve scene
$scene = new Scene(title: "A new scene", description: "Hallo Welt", template: null);
# Fetch property, with default "false".
# Must return true, as this is whats in our dataset
$value = $scene->getProperty("lotgd/core/tests/property/dataset");
# Assert the value
$this->assertNull($value);
}
public function testIfPropertyCanBeSaved()
{
$em = $this->getEntityManager();
+26 -6
View File
@@ -22,11 +22,11 @@ class ViewpointRestorationTest extends CoreModelTestCase
protected function getActionGroup()
{
static $actionGroup = null;
if ($actionGroup !== null) {
if ($actionGroup === null) {
$actionGroup = new ActionGroup("main", "Title", 0);
$actionGroup->addAction(new Action(1));
$actionGroup->addAction(new Action(2));
$actionGroup->addAction(new Action(3));
$actionGroup->addAction(new Action("30000000-0000-0000-0000-000000000001"));
$actionGroup->addAction(new Action("30000000-0000-0000-0000-000000000002"));
$actionGroup->addAction(new Action("30000000-0000-0000-0000-000000000003"));
}
return $actionGroup;
@@ -102,7 +102,17 @@ class ViewpointRestorationTest extends CoreModelTestCase
$this->assertSame($viewpoint->getTitle(), $newViewpoint->getTitle());
$this->assertSame($viewpoint->getDescription(), $newViewpoint->getDescription());
$this->assertSame($viewpoint->getTemplate(), $newViewpoint->getTemplate());
$this->assertEquals($viewpoint->getActionGroups(), $newViewpoint->getActionGroups());;
for ($i=0; $i < count($viewpoint->getActionGroups()); $i++) {
$should = $viewpoint->getActionGroups()[$i];
$is = $newViewpoint->getActionGroups()[$i];
$this->assertSame($should->getId(), $is->getId());
$this->assertSame($should->getTitle(), $is->getTitle());
$this->assertSame($should->getSortKey(), $is->getSortKey());
$this->assertSame(count($should->getActions()), count($is->getActions()));
}
$this->assertSame($viewpoint->getData(), $newViewpoint->getData());
$this->assertSame($viewpoint->getAttachments(), $newViewpoint->getAttachments());
}
@@ -120,7 +130,17 @@ class ViewpointRestorationTest extends CoreModelTestCase
$this->assertSame($viewpoint->getTitle(), $newViewpoint->getTitle());
$this->assertSame($viewpoint->getDescription(), $newViewpoint->getDescription());
$this->assertSame($viewpoint->getTemplate(), $newViewpoint->getTemplate());
$this->assertEquals($viewpoint->getActionGroups(), $newViewpoint->getActionGroups());;
for ($i=0; $i < count($viewpoint->getActionGroups()); $i++) {
$should = $viewpoint->getActionGroups()[$i];
$is = $newViewpoint->getActionGroups()[$i];
$this->assertSame($should->getId(), $is->getId());
$this->assertSame($should->getTitle(), $is->getTitle());
$this->assertSame($should->getSortKey(), $is->getSortKey());
$this->assertSame(count($should->getActions()), count($is->getActions()));
}
$this->assertSame($viewpoint->getData(), $newViewpoint->getData());
$this->assertSame($viewpoint->getAttachments(), $newViewpoint->getAttachments());
}
+31 -4
View File
@@ -117,9 +117,18 @@ class ViewpointTest extends CoreModelTestCase
$em->clear();
$output = $em->getRepository(Viewpoint::class)->find("10000000-0000-0000-0000-000000000002");
$this->assertEquals($actionGroups, $output->getActionGroups());
$this->assertEquals($ag2, $input->findActionGroupById('id2'));
for ($i=0; $i < count($actionGroups); $i++) {
$should = $actionGroups[$i];
$is = $output->getActionGroups()[$i];
$this->assertSame($should->getId(), $is->getId());
$this->assertSame($should->getTitle(), $is->getTitle());
$this->assertSame($should->getSortKey(), $is->getSortKey());
$this->assertSame(count($should->getActions()), count($is->getActions()));
}
$this->assertEquals($ag2->getTitle(), $input->findActionGroupById('id2')->getTitle());
$this->assertNull($input->findActionGroupById('not-there'));
$testAction = new Action("30000000-0000-0000-0000-000000000004");
@@ -186,7 +195,16 @@ class ViewpointTest extends CoreModelTestCase
// Not finding the scene ID should change nothing.
$output->removeActionsWithSceneId("30000000-0000-0000-0000-000000001000");
$this->assertEquals($actionGroups, $output->getActionGroups());
for ($i=0; $i < count($actionGroups); $i++) {
$should = $actionGroups[$i];
$is = $output->getActionGroups()[$i];
$this->assertSame($should->getId(), $is->getId());
$this->assertSame($should->getTitle(), $is->getTitle());
$this->assertSame($should->getSortKey(), $is->getSortKey());
$this->assertSame(count($should->getActions()), count($is->getActions()));
}
$ag1_output = new ActionGroup('id1', 'title1', 42);
$ag1_output->setActions([
@@ -199,7 +217,16 @@ class ViewpointTest extends CoreModelTestCase
$ag2
];
$output->removeActionsWithSceneId("30000000-0000-0000-0000-000000000002");
$this->assertEquals($actionGroupsWithout2, $output->getActionGroups());
for ($i=0; $i < count($actionGroups); $i++) {
$should = $actionGroupsWithout2[$i];
$is = $output->getActionGroups()[$i];
$this->assertSame($should->getId(), $is->getId());
$this->assertSame($should->getTitle(), $is->getTitle());
$this->assertSame($should->getSortKey(), $is->getSortKey());
$this->assertSame(count($should->getActions()), count($is->getActions()));
}
}
public function testChangingSceneDescription()
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\SceneTemplates;
use LotGD\Core\SceneTemplates\BasicSceneTemplate;
class ForestSceneTemplate extends BasicSceneTemplate
{
public static function getNavigationEvent(): string
{
return "tests/forest";
}
}
@@ -1,10 +1,8 @@
<?php
declare(strict_types=1);
namespace LotGD\Core\Tests\SceneTemplates;
use LotGD\Core\SceneTemplates\BasicSceneTemplate;
class VillageSceneTemplate extends BasicSceneTemplate
@@ -13,4 +11,4 @@ class VillageSceneTemplate extends BasicSceneTemplate
{
return "tests/village";
}
}
}
+41 -15
View File
@@ -9,6 +9,7 @@ use LotGD\Core\Exceptions\InsecureTwigTemplateError;
use LotGD\Core\Game;
use LotGD\Core\Models\Character;
use LotGD\Core\Models\Scene;
use LotGD\Core\Models\Viewpoint;
use LotGD\Core\PHPUnit\LotGDTestCase;
use LotGD\Core\Services\TwigSceneRenderer;
@@ -43,12 +44,17 @@ class TwigSceneRendererTest extends LotGDTestCase
->disableOriginalConstructor()
->getMock();
return [$game, $scene, $character, $eventManager];
$viewpoint = $this->getMockBuilder(Viewpoint::class)
->disableOriginalConstructor()
->getMock();
$viewpoint->method("getScene")->willReturn($scene);
return [$game, $viewpoint, $character, $eventManager];
}
public function testIfSceneRendererCanBeConstructed()
{
[$game, $scene, $character, $eventManager] = $this->getMockeries();
[$game, $viewpoint, $character, $eventManager] = $this->getMockeries();
$eventManager->method("publish")->willReturnArgument(1);
$renderer = new TwigSceneRenderer($game);
@@ -58,7 +64,7 @@ class TwigSceneRendererTest extends LotGDTestCase
public function testIfTwigSceneRendererReturnsANonTemplateStringUnmodified()
{
[$game, $scene, $character, $eventManager] = $this->getMockeries();
[$game, $viewpoint, $character, $eventManager] = $this->getMockeries();
$eventManager->method("publish")->willReturnArgument(1);
# Get renderer
@@ -68,7 +74,7 @@ class TwigSceneRendererTest extends LotGDTestCase
$template = "You enter a new location.\n\nA new location.";
# Create the result
$renderResult = $renderer->render($template, $scene);
$renderResult = $renderer->render($template, $viewpoint);
# Assert result
$this->assertSame($template, $renderResult);
@@ -76,7 +82,7 @@ class TwigSceneRendererTest extends LotGDTestCase
public function testIfTwigSceneRendererParsesStringsWithCharacters()
{
[$game, $scene, $character, $eventManager] = $this->getMockeries();
[$game, $viewpoint, $character, $eventManager] = $this->getMockeries();
$eventManager->method("publish")->willReturnArgument(1);
# Get renderer
@@ -92,7 +98,27 @@ class TwigSceneRendererTest extends LotGDTestCase
."You are alive.";
# Create the result
$renderResult = $renderer->render($template, $scene);
$renderResult = $renderer->render($template, $viewpoint);
# Assert result
$this->assertSame($result, $renderResult);
}
public function testIfViewpointDataCanBeAccessed()
{
[$game, $viewpoint, $character, $eventManager] = $this->getMockeries();
$eventManager->method("publish")->willReturnArgument(1);
$viewpoint->method("getData")->willReturn(["test" => 7, "other" => "Hi"]);
# Get renderer
$renderer = new TwigSceneRenderer($game);
# Prepare the template string.
$template = "Hi {{ Viewpoint.data.test }}! {{ Viewpoint.data.other }}";
$result = "Hi 7! Hi";
# Create the result
$renderResult = $renderer->render($template, $viewpoint);
# Assert result
$this->assertSame($result, $renderResult);
@@ -100,7 +126,7 @@ class TwigSceneRendererTest extends LotGDTestCase
public function testIfRawTemplateGetsReturnedIfTemplateContainsIllegalTokens()
{
[$game, $scene, $character, $eventManager] = $this->getMockeries();
[$game, $viewpoint, $character, $eventManager] = $this->getMockeries();
$eventManager->method("publish")->willReturnArgument(1);
# Get renderer
@@ -110,7 +136,7 @@ class TwigSceneRendererTest extends LotGDTestCase
$template = "Viewpoint: {{ Character.viewpoint }}";
# Try to parse the result
$renderResult = $renderer->render($template, $scene, true);
$renderResult = $renderer->render($template, $viewpoint, true);
# If there was an error, it should have gotten ignored, giving back the raw template.
$this->assertSame($template, $renderResult);
@@ -118,7 +144,7 @@ class TwigSceneRendererTest extends LotGDTestCase
public function testIfExceptionGetsRaisedIfTemplateContainsIllegalTokens()
{
[$game, $scene, $character, $eventManager] = $this->getMockeries();
[$game, $viewpoint, $character, $eventManager] = $this->getMockeries();
$eventManager->method("publish")->willReturnArgument(1);
# Get renderer
@@ -131,12 +157,12 @@ class TwigSceneRendererTest extends LotGDTestCase
$this->expectException(InsecureTwigTemplateError::class);
# Try to parse the result
$renderResult = $renderer->render($template, $scene, false);
$renderResult = $renderer->render($template, $viewpoint, false);
}
public function testIfPublishedEventCanModifySecurityPolicy()
{
[$game, $scene, $character, $eventManager] = $this->getMockeries();
[$game, $viewpoint, $character, $eventManager] = $this->getMockeries();
# Set up a more complex "publish" method to emulate a real event.
$eventManager->method("publish")->willReturnCallback(function($event, EventContextData $context) {
@@ -164,15 +190,15 @@ class TwigSceneRendererTest extends LotGDTestCase
# Assert that if does not work anymore
$this->expectException(InsecureTwigTemplateError::class);
$renderer->render("{% if 5*1 %}Hallo{%endif%}", $scene, false);
$renderer->render("{% if 5*1 %}Hallo{%endif%}", $viewpoint, false);
$this->expectException(InsecureTwigTemplateError::class);
$renderer->render("{{ Character.name }}", $scene, false);
$renderer->render("{{ Character.name }}", $viewpoint, false);
}
public function testIfPublishedEventCanModifyValueScope()
{
[$game, $scene, $character, $eventManager] = $this->getMockeries();
[$game, $viewpoint, $character, $eventManager] = $this->getMockeries();
# Set up a more complex "publish" method to emulate a real event.
$eventManager->method("publish")->willReturnCallback(function($event, EventContextData $context) {
@@ -192,7 +218,7 @@ class TwigSceneRendererTest extends LotGDTestCase
$renderer = new TwigSceneRenderer($game);
# Assert result
$result = $renderer->render("{{ test }}", $scene, false);
$result = $renderer->render("{{ test }}", $viewpoint, false);
$this->assertSame("A test", $result);
}
}
+6
View File
@@ -18,6 +18,12 @@ characters:
health: 90
maxhealth: 90
deletedAt: "2011-11-11 11:11:11"
-
id: "10000000-0000-0000-0000-000000000004"
name: "Testcharacter 4"
displayName: "Testcharacter 4"
health: 80
maxhealth: 90
character_properties:
-
owner_id: "10000000-0000-0000-0000-000000000001"
+12
View File
@@ -0,0 +1,12 @@
modules:
-
library: "lotgd/tests"
createdAt: "2016-05-01"
-
library: "lotgd/tests-other"
createdAt: "2020-02-07"
module_properties:
-
owner: 'lotgd/tests'
propertyName: "test"
propertyValue: 's:5:"hallo";'
+108
View File
@@ -0,0 +1,108 @@
scenes:
-
id: "30000000-0000-0000-0000-000000000001"
title: "The Village"
description: "This is the village."
template: "LotGD\\Core\\Tests\\SceneTemplates\\VillageSceneTemplate"
removeable: true
-
id: "30000000-0000-0000-0000-000000000002"
title: "The Forest"
description: "This is a very dangerous and dark forest"
template: "LotGD\\Core\\Tests\\SceneTemplates\\ForestSceneTemplate"
removeable: true
-
id: "30000000-0000-0000-0000-000000000003"
title: "The Weaponry"
description: "This is the place where you can buy awesome weapons"
template: null
removeable: true
-
id: "30000000-0000-0000-0000-000000000004"
title: "Another Village"
description: "This is another village"
template: "LotGD\\Core\\Tests\\SceneTemplates\\Village"
removeable: true
-
id: "30000000-0000-0000-0000-000000000005"
title: "Orphan"
description: "This is an orphan scene"
template: null
removeable: false
-
id: "30000000-0000-0000-0000-000000000006"
title: "Connection Test Scene"
description: "This is a connection test scene"
template: "LotGD\\Core\\Tests\\SceneTemplates\\VillageSceneTemplate"
removeable: true
scene_connection_groups:
-
scene: "30000000-0000-0000-0000-000000000001"
name: "lotgd/tests/village/outside"
title: "Outside"
-
scene: "30000000-0000-0000-0000-000000000001"
name: "lotgd/tests/village/market"
title: "Market"
-
scene: "30000000-0000-0000-0000-000000000001"
name: "lotgd/tests/village/empty"
title: "Empty"
-
scene: "30000000-0000-0000-0000-000000000002"
name: "lotgd/tests/forest/category"
title: "Empty"
-
scene: "30000000-0000-0000-0000-000000000003"
name: "lotgd/tests/weaponry/category"
title: "Empty"
-
scene: "30000000-0000-0000-0000-000000000006"
name: "lotgd/tests/testscene/one"
title: "Group One"
-
scene: "30000000-0000-0000-0000-000000000006"
name: "lotgd/tests/testscene/two"
title: "Group Two"
-
scene: "30000000-0000-0000-0000-000000000006"
name: "lotgd/tests/testscene/three"
title: "Group Three"
scene_connections:
-
outgoingScene: "30000000-0000-0000-0000-000000000001"
incomingScene: "30000000-0000-0000-0000-000000000002"
-
outgoingScene: "30000000-0000-0000-0000-000000000001"
outgoingConnectionGroupName: "lotgd/tests/village/market"
incomingScene: "30000000-0000-0000-0000-000000000003"
incomingConnectionGroupName: "lotgd/tests/weaponry/category"
-
outgoingScene: "30000000-0000-0000-0000-000000000001"
incomingScene: "30000000-0000-0000-0000-000000000004"
-
outgoingScene: "30000000-0000-0000-0000-000000000006"
incomingScene: "30000000-0000-0000-0000-000000000001"
-
outgoingScene: "30000000-0000-0000-0000-000000000002"
incomingScene: "30000000-0000-0000-0000-000000000006"
incomingConnectionGroupName: "lotgd/tests/testscene/three"
directionality: 1
-
outgoingScene: "30000000-0000-0000-0000-000000000006"
outgoingConnectionGroupName: "lotgd/tests/testscene/three"
incomingScene: "30000000-0000-0000-0000-000000000003"
directionality: 1
scene_templates:
-
class: "LotGD\\Core\\Tests\\SceneTemplates\\VillageSceneTemplate"
module: "lotgd/core"
userAssignable: true
-
class: "LotGD\\Core\\Tests\\SceneTemplates\\ForestSceneTemplate"
module: "lotgd/core"
userAssignable: true
-
class: "LotGD\\Core\\Tests\\SceneTemplates\\NewSceneSceneTemplate"
module: "lotgd/core"
userAssignable: true
+5
View File
@@ -4,6 +4,11 @@ scenes:
title: "A scene"
description: "This is the village."
template: "lotgd/tests/village"
-
id: "30000000-0000-0000-0000-000000000002"
title: "A scene"
description: "This is the village."
template: "lotgd/tests/village"
scene_properties:
-
owner: "30000000-0000-0000-0000-000000000001"