eca-1.0.x-dev/tests/src/Kernel/Model/Base.php
tests/src/Kernel/Model/Base.php
<?php
namespace Drupal\Tests\eca\Kernel\Model;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Logger\RfcLogLevel;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\KernelTests\KernelTestBase;
use Drupal\eca\Entity\Eca;
use Drupal\user\Entity\User;
use Symfony\Component\ErrorHandler\BufferingLogger;
/**
* Parent class for ECA model tests.
*/
abstract class Base extends KernelTestBase {
/**
* {@inheritdoc}
*/
protected static $modules = [
'user',
'system',
'field',
'text',
'eca',
];
/**
* The service name for a logger implementation that collects anything logged.
*
* @var string
*/
protected static string $testLogServiceName = 'eca_test.logger';
protected const USER_1_NAME = 'eca-test';
/**
* {@inheritdoc}
*
* @throws \Drupal\Core\Entity\EntityStorageException
* @throws \Exception
*/
protected function setUp(): void {
Eca::setTesting();
parent::setUp();
// Install config for modules of this base class.
$this->installConfig(['user', 'system', 'field', 'text', 'eca']);
$this->installEntitySchema('user');
$this->installSchema('system', ['sequences']);
// Install config for modules of the implementing test class.
$this->installConfig(static::$modules);
// Create user 1.
User::create([
'uid' => 1,
'name' => self::USER_1_NAME,
'mail' => 'eca@localhost',
'status' => TRUE,
])->save();
// Prepare the logger for collecting ECA log messages.
$this->container->get(self::$testLogServiceName)->cleanLogs();
// Enable all log levels by default.
$this->setLogLevel(RfcLogLevel::ERROR);
}
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container): void {
parent::register($container);
$container
->register(self::$testLogServiceName, BufferingLogger::class)
->addTag('logger');
}
/**
* Switch to the given user.
*
* @param int $id
* The ID of the user to which to switch.
*/
protected function switchUser(int $id = 1): void {
$user = User::load($id);
\Drupal::service('account_switcher')->switchTo($user);
}
/**
* Configures the ECA log level.
*
* @param int $level
* The RfcLogLevel:: level which should be configured for ECA.
*/
protected function setLogLevel(int $level): void {
\Drupal::service('logger.channel.eca')->updateLogLevel($level);
}
/**
* Disable a given ECA model.
*
* @param string $id
* The ID of the model to be disabled.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function disableEcaModel(string $id): void {
/** @var \Drupal\eca\Entity\Eca $eca */
if ($eca = \Drupal::entityTypeManager()->getStorage('eca')->load($id)) {
$eca->disable()->save();
}
}
/**
* Verify that no error or worse has been logged.
*
* Optionally this can also assert a number of expected log records, that
* need to be present and won't be treated as available errors.
*
* @param \Drupal\Tests\eca\Kernel\Model\LogRecord[] $logRecords
* List of expected log records.
*
* @throws \Exception
*/
protected function assertNoError(array $logRecords = []): void {
foreach ($this->container->get(self::$testLogServiceName)->cleanLogs() as $log_message) {
foreach ($logRecords as $index => $logRecord) {
if ($logRecord->compare($log_message[0], $log_message[2]['channel'], $log_message[1], $log_message[2])) {
$this->assertNotNull('record exists', $logRecord->__toString());
unset($logRecords[$index]);
continue 2;
}
}
$this->assertGreaterThan(RfcLogLevel::ERROR, $log_message[0], strip_tags((string) (new FormattableMarkup($log_message[1], $log_message[2]))));
}
self::assertEmpty($logRecords, 'Expected log records missing: ' . PHP_EOL . implode(PHP_EOL, $logRecords));
}
/**
* Verify that all expected messages are available.
*
* @param string[] $expected
* List of expected messages.
* @param string[]|\Drupal\Component\Render\MarkupInterface[] $messages
* List of actual messages.
*/
protected function assertMessages(array $expected, array $messages): void {
foreach ($messages as $message) {
$key = array_search((string) $message, $expected, TRUE);
self::assertNotFalse($key, "Message '$message' is unexpected.");
if ($key !== FALSE) {
unset($expected[$key]);
}
}
self::assertEmpty($expected, 'Expected messages missing: ' . PHP_EOL . implode(PHP_EOL, $expected));
}
/**
* Verify that all expected status messages are available.
*
* @param string[] $expected
* List of expected messages.
*/
protected function assertStatusMessages(array $expected): void {
$this->assertMessages($expected, \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_STATUS));
}
/**
* Verify that all expected warning messages are available.
*
* @param string[] $expected
* List of expected messages.
*/
protected function assertWarningMessages(array $expected): void {
$this->assertMessages($expected, \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_WARNING));
}
/**
* Verify that all expected error messages are available.
*
* @param string[] $expected
* List of expected messages.
*/
protected function assertErrorMessages(array $expected): void {
$this->assertMessages($expected, \Drupal::messenger()->deleteByType(MessengerInterface::TYPE_ERROR));
}
/**
* Verify that no messages are available.
*/
protected function assertNoMessages(): void {
$this->assertMessages([], \Drupal::messenger()->deleteAll());
}
}
/**
* Helper class to compare log records.
*/
class LogRecord {
/**
* Log severity.
*
* @var int
*/
private int $severity;
/**
* Log channel.
*
* @var string
*/
private string $channel;
/**
* Log message.
*
* @var string
*/
private string $message;
/**
* Log arguments/context.
*
* @var array
*/
private array $arguments;
/**
* Constructs a log record.
*
* @param int $severity
* The log severity.
* @param string $channel
* The log channel.
* @param string $message
* The log message.
* @param array $arguments
* The list of log message arguments.
*/
public function __construct(int $severity, string $channel, string $message, array $arguments = []) {
$this->severity = $severity;
$this->channel = $channel;
$this->message = $message;
$this->arguments = $arguments;
}
/**
* Formats and cleans the log record.
*
* @param string $message
* The log message.
* @param array $arguments
* The log message arguments.
*
* @return string
* The formatted message string.
*/
public static function format(string $message, array $arguments = []): string {
return strip_tags(strtr($message, $arguments));
}
/**
* Compares the current log record to the given values.
*
* @param int $severity
* The log severity.
* @param string $channel
* The log channel.
* @param string $message
* The log message.
* @param array $arguments
* The list of log message arguments.
*
* @return bool
* Returns TRUE, if all components equal the current log record, FALSE
* otherwise.
*/
public function compare(int $severity, string $channel, string $message, array $arguments = []): bool {
return $this->severity === $severity &&
$this->channel === $channel &&
$this->__toString() === self::format($message, $arguments);
}
/**
* Return the formatted version of the current log record.
*
* @return string
* The formatted message string.
*/
public function __toString(): string {
return self::format($this->message, $this->arguments);
}
}
