Every $hub->publish() call site (chat, hints, security alerts, virus alerts, game_finished, and the pre-game lobby events) now dispatches a PushMercureMessageEvent instead of publishing directly. Two listeners handle it: PublishMercureMessageListener does the actual Mercure publish exactly as before (same wire format, no client changes needed), and LogMercureMessageListener appends it to the activity log of whichever player(s) it was actually delivered to. A private /chat to one agent only gets logged for that agent, never broadcast into everyone's transcript - the event carries an explicit targetScreen (null = everyone) rather than leaving listeners to guess from the payload shape. The per-player log format moved from flat text to JSON Lines (one timestamped, structured entry per line) via a new shared SessionActivityLogger service, since a flat string can't carry an event's type or exact payload - both needed for a future feature to replay a player's full session history, not just their own commands, on page reload. The admin session log viewer now parses and formats these entries back into readable lines. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
111 lines
3.8 KiB
PHP
111 lines
3.8 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Game;
|
|
|
|
use App\Game\Entity\Player;
|
|
use App\Game\Entity\Session;
|
|
use App\Game\Entity\SessionSetting;
|
|
use App\Game\Enum\SessionSettingType;
|
|
use App\Game\Repository\SessionSettingRepository;
|
|
use App\Game\Service\GameResponseService;
|
|
use App\Game\Service\PlayerService;
|
|
use App\Game\Service\SessionActivityLogger;
|
|
use App\Tech\Entity\User;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Symfony\Bundle\SecurityBundle\Security;
|
|
use Symfony\Component\EventDispatcher\EventDispatcher;
|
|
|
|
class SessionLoggingTest extends TestCase
|
|
{
|
|
private string $tempDir;
|
|
private $security;
|
|
private $playerService;
|
|
private $sessionSettingRepository;
|
|
private $entityManager;
|
|
private $service;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->tempDir = sys_get_temp_dir() . '/escapepage_test_' . uniqid();
|
|
mkdir($this->tempDir, 0777, true);
|
|
|
|
$this->security = $this->createMock(Security::class);
|
|
$this->playerService = $this->createMock(PlayerService::class);
|
|
$this->sessionSettingRepository = $this->createMock(SessionSettingRepository::class);
|
|
$this->entityManager = $this->createMock(EntityManagerInterface::class);
|
|
|
|
$this->service = new GameResponseService(
|
|
$this->security,
|
|
$this->playerService,
|
|
$this->sessionSettingRepository,
|
|
$this->entityManager,
|
|
new EventDispatcher(),
|
|
new SessionActivityLogger($this->tempDir),
|
|
$this->tempDir
|
|
);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
$this->removeDir($this->tempDir);
|
|
}
|
|
|
|
private function removeDir(string $dir): void
|
|
{
|
|
if (!is_dir($dir)) return;
|
|
$files = array_diff(scandir($dir), ['.', '..']);
|
|
foreach ($files as $file) {
|
|
(is_dir("$dir/$file")) ? $this->removeDir("$dir/$file") : unlink("$dir/$file");
|
|
}
|
|
rmdir($dir);
|
|
}
|
|
|
|
public function testLogging(): void
|
|
{
|
|
$user = new User();
|
|
$user->setUsername('player1');
|
|
|
|
$session = $this->createMock(Session::class);
|
|
$session->method('getId')->willReturn(456);
|
|
|
|
$player = $this->createMock(Player::class);
|
|
$player->method('getUser')->willReturn($user);
|
|
$player->method('getSession')->willReturn($session);
|
|
$player->method('getScreen')->willReturn(1);
|
|
$player->method('getLogFileBasename')->willReturn('player1');
|
|
|
|
$this->security->method('getUser')->willReturn($user);
|
|
$this->playerService->method('GetCurrentlyActiveAsPlayer')->willReturn($player);
|
|
|
|
// Mock rights
|
|
$rightsSetting = new SessionSetting();
|
|
$rightsSetting->setValue(json_encode(['chat']));
|
|
$this->sessionSettingRepository->method('getSetting')
|
|
->willReturnMap([
|
|
[$session, SessionSettingType::RIGHTS_FOR_PLAYER1, $player, $rightsSetting],
|
|
]);
|
|
|
|
// Simulate 'help' command (always returns something)
|
|
$raw = json_encode(['message' => 'help', 'ts' => '123']);
|
|
$result = $this->service->getGameResponse($raw);
|
|
|
|
$this->assertNotEmpty($result);
|
|
|
|
$logFilePath = $this->tempDir . '/var/log/sessions/456/player1.txt';
|
|
$this->assertFileExists($logFilePath);
|
|
|
|
$lines = array_filter(explode("\n", file_get_contents($logFilePath)));
|
|
$entries = array_map(fn(string $line) => json_decode($line, true), $lines);
|
|
|
|
$playerEntry = current(array_filter($entries, fn(array $e) => $e['source'] === 'player'));
|
|
$this->assertNotFalse($playerEntry);
|
|
$this->assertSame('help', $playerEntry['content']);
|
|
|
|
$serverEntry = current(array_filter($entries, fn(array $e) => $e['source'] === 'server'));
|
|
$this->assertNotFalse($serverEntry);
|
|
$this->assertNotEmpty($serverEntry['content']);
|
|
}
|
|
}
|