Add setup for database-based tests
This commit is contained in:
parent
4bca46fb97
commit
379498cfe0
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"name": "fwdekker/death-notifier",
|
||||
"description": "Get notified when a famous person dies.",
|
||||
"version": "0.14.7", "_comment_version": "Also update version in `package.json`!",
|
||||
"version": "0.14.9",
|
||||
"_comment_version": "Also update version in `package.json`!",
|
||||
"type": "project",
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.fwdekker.com/tools/death-notifier",
|
||||
|
@ -23,14 +24,10 @@
|
|||
"phpmailer/phpmailer": "^6.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.9",
|
||||
"phpunit/phpunit": "^9.5"
|
||||
"phpstan/phpstan": "^1.9.2",
|
||||
"phpunit/phpunit": "^9.5.26"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/main/php/",
|
||||
"src/test/php/"
|
||||
],
|
||||
"psr-4": {
|
||||
"php\\": "php/"
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "death-notifier",
|
||||
"version": "0.14.8", "_comment_version": "Also update version in `composer.json`!",
|
||||
"version": "0.14.9", "_comment_version": "Also update version in `composer.json`!",
|
||||
"description": "Get notified when a famous person dies.",
|
||||
"author": "Florine W. Dekker",
|
||||
"browser": "dist/bundle.js",
|
||||
|
|
|
@ -26,7 +26,7 @@ $db = new Database($logger->withName("Database"), $config["database"]["filename"
|
|||
|
||||
$mailer = new Mailer($config, $logger->withName("Mailer"), $db->conn);
|
||||
$mediawiki = new Mediawiki($logger->withName("Mediawiki"));
|
||||
$user_manager = new UserManager($db->conn, $mailer);
|
||||
$user_manager = new UserManager($logger->withName("UserManager"), $db->conn, $mailer);
|
||||
$tracking_manager = new TrackingManager($logger->withName("TrackingManager"), $db->conn, $mailer, $mediawiki);
|
||||
|
||||
$db->auto_install($mailer, $user_manager, $tracking_manager);
|
||||
|
|
|
@ -138,7 +138,7 @@ class Database
|
|||
* Migrates the database from a previous version to one compatible with v0.8.0.
|
||||
*
|
||||
* @return void
|
||||
* @noinspection SqlResolve Function necessarily refers to old scheme which is not detected by tools
|
||||
* @noinspection SqlResolve Function necessarily refers to old schema which is not detected by tools
|
||||
*/
|
||||
private function migrate_0_8_0(): void
|
||||
{
|
||||
|
@ -166,7 +166,7 @@ class Database
|
|||
* Migrates the database from a previous version to one compatible with v0.10.0.
|
||||
*
|
||||
* @return void
|
||||
* @noinspection SqlResolve Function necessarily refers to old scheme which is not detected by tools
|
||||
* @noinspection SqlResolve Function necessarily refers to old schema which is not detected by tools
|
||||
*/
|
||||
private function migrate_0_10_0(): void
|
||||
{
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
|
||||
namespace php;
|
||||
|
||||
use Exception;
|
||||
use Monolog\Logger;
|
||||
use PDO;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
use PHPMailer\PHPMailer\Exception as MailerException;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\SMTP;
|
||||
|
||||
|
@ -98,7 +99,7 @@ class Mailer
|
|||
$mailer->Password = $this->config["mail"]["password"];
|
||||
try {
|
||||
$mailer->setFrom($this->config["mail"]["username"], $this->config["mail"]["from_name"]);
|
||||
} catch (Exception $exception) {
|
||||
} catch (MailerException $exception) {
|
||||
$this->logger->error("Failed to set 'from' address while processing queue.", ["cause" => $exception]);
|
||||
$mailer->smtpClose();
|
||||
}
|
||||
|
@ -124,7 +125,7 @@ class Mailer
|
|||
try {
|
||||
$mailer->addAddress($recipient);
|
||||
$mailer->send();
|
||||
} catch (Exception $exception) {
|
||||
} catch (MailerException $exception) {
|
||||
$this->logger->error(
|
||||
"Failed to send mail.",
|
||||
["cause" => $exception, "email" => $email]
|
||||
|
@ -133,7 +134,7 @@ class Mailer
|
|||
}
|
||||
|
||||
$mailer->clearAddresses();
|
||||
} catch (\Exception $exception) {
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->error(
|
||||
"Failed to send mail.",
|
||||
["cause" => $exception]
|
||||
|
@ -199,7 +200,7 @@ abstract class Email
|
|||
* @param string $arg1 the first argument to construct the email
|
||||
* @param string $arg2 the second argument to construct the email
|
||||
* @return Email a deserialized email
|
||||
* @throws \Exception if the `type` is not recognized
|
||||
* @throws Exception if the `type` is not recognized
|
||||
*/
|
||||
public static function deserialize(string $type, string $recipient, string $arg1, string $arg2): Email
|
||||
{
|
||||
|
@ -212,7 +213,7 @@ abstract class Email
|
|||
NotifyArticleDeletedEmail::TYPE => new NotifyArticleDeletedEmail($recipient, $arg1),
|
||||
NotifyArticleUndeletedEmail::TYPE => new NotifyArticleUndeletedEmail($recipient, $arg1),
|
||||
NotifyStatusChangedEmail::TYPE => new NotifyStatusChangedEmail($recipient, $arg1, $arg2),
|
||||
default => throw new \Exception("Unknown email type $type."),
|
||||
default => throw new Exception("Unknown email type $type."),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -386,7 +387,8 @@ class ChangedEmailEmail extends Email
|
|||
/**
|
||||
* An email informing a user that their password has been changed.
|
||||
*/
|
||||
class ChangedPasswordEmail extends Email {
|
||||
class ChangedPasswordEmail extends Email
|
||||
{
|
||||
/**
|
||||
* A string identifying the type of email.
|
||||
*/
|
||||
|
@ -483,7 +485,8 @@ class ResetPasswordEmail extends Email
|
|||
/**
|
||||
* An email to inform a user that a tracked article has been deleted.
|
||||
*/
|
||||
class NotifyArticleDeletedEmail extends Email {
|
||||
class NotifyArticleDeletedEmail extends Email
|
||||
{
|
||||
/**
|
||||
* A string identifying the type of email.
|
||||
*/
|
||||
|
@ -537,7 +540,8 @@ class NotifyArticleDeletedEmail extends Email {
|
|||
/**
|
||||
* An email to inform a user that a tracked article has been re-created.
|
||||
*/
|
||||
class NotifyArticleUndeletedEmail extends Email {
|
||||
class NotifyArticleUndeletedEmail extends Email
|
||||
{
|
||||
/**
|
||||
* A string identifying the type of email.
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace php;
|
||||
|
||||
use Exception;
|
||||
use Monolog\Logger;
|
||||
use PDO;
|
||||
|
||||
|
@ -97,7 +98,7 @@ class TrackingManager
|
|||
{
|
||||
try {
|
||||
$info = $this->mediawiki->query_page_info([$person_name]);
|
||||
} catch (\Exception $exception) {
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->error("Failed to query page info.", ["cause" => $exception, "name" => $person_name]);
|
||||
return Response::unsatisfied("Could not reach Wikipedia. Maybe the website is down?");
|
||||
}
|
||||
|
@ -254,7 +255,7 @@ class TrackingManager
|
|||
|
||||
try {
|
||||
$people_statuses = $this->mediawiki->query_page_info($names);
|
||||
} catch (\Exception $exception) {
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->error("Failed to retrieve page information.", ["cause" => $exception, "pages" => $names]);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -37,6 +37,11 @@ class UserManager
|
|||
*/
|
||||
public const MINUTES_VALID_PASSWORD_RESET = 60;
|
||||
|
||||
|
||||
/**
|
||||
* @var Logger The logger to use for logging.
|
||||
*/
|
||||
private Logger $logger; // @phpstan-ignore-line Unused, but useful for debugging
|
||||
/**
|
||||
* @var PDO The database connection to interact with.
|
||||
*/
|
||||
|
@ -50,11 +55,13 @@ class UserManager
|
|||
/**
|
||||
* Constructs a new user manager.
|
||||
*
|
||||
* @param Logger $logger the logger to use for logging
|
||||
* @param PDO $conn the database connection to interact with
|
||||
* @param Mailer $mailer the mailer to send emails with
|
||||
*/
|
||||
public function __construct(PDO $conn, Mailer $mailer)
|
||||
public function __construct(Logger $logger, PDO $conn, Mailer $mailer)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->conn = $conn;
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
<?php
|
||||
|
||||
namespace php;
|
||||
|
||||
use Exception;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Test\TestCase;
|
||||
|
||||
|
||||
/**
|
||||
* For tests that use a fully-installed database.
|
||||
*
|
||||
* By default, only the main schema of the database is installed. Override the getters to additionally install the
|
||||
* corresponding parts of the schema.
|
||||
*/
|
||||
abstract class DatabaseTestCase extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var string the temporary directory to store database files in
|
||||
*/
|
||||
private static string $db_tmp_dir;
|
||||
|
||||
/**
|
||||
* @var Logger the logger
|
||||
*/
|
||||
public Logger $logger;
|
||||
/**
|
||||
* @var Database the database
|
||||
*/
|
||||
public Database $database;
|
||||
|
||||
|
||||
/**
|
||||
* @return Mailer the `Mailer` to install the database schema of
|
||||
*/
|
||||
function getMailer(): Mailer
|
||||
{
|
||||
return $this->createStub(Mailer::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TrackingManager the `TrackingManager` to install the database schema of
|
||||
*/
|
||||
function getTrackingManager(): TrackingManager
|
||||
{
|
||||
return $this->createStub(TrackingManager::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return UserManager the `UserManager` to install the database schema of
|
||||
*/
|
||||
function getUserManager(): UserManager
|
||||
{
|
||||
return $this->createStub(UserManager::class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets up a directory for temporary files.
|
||||
*
|
||||
* @throws Exception if the directory could not be created
|
||||
*/
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
parent::setUpBeforeClass();
|
||||
|
||||
self::$db_tmp_dir = sys_get_temp_dir() . "/DatabaseTestCase-" . rand();
|
||||
if (!mkdir(self::$db_tmp_dir))
|
||||
throw new Exception("Failed to create temporary directory '" . self::$db_tmp_dir . "'.");
|
||||
|
||||
register_shutdown_function(function () {
|
||||
$files = glob(self::$db_tmp_dir . "/*");
|
||||
if ($files === false) return;
|
||||
|
||||
array_map("unlink", $files);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up and installs a database.
|
||||
*
|
||||
* @throws Exception if the database file could not be created
|
||||
*/
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$db_tmp_file = tempnam(self::$db_tmp_dir, "database-");
|
||||
if ($db_tmp_file === false) throw new Exception("Failed to create temporary database file.");
|
||||
|
||||
$this->logger = new Logger("DatabaseTestCase");
|
||||
$this->database = new Database($this->logger, $db_tmp_file);
|
||||
|
||||
$this->database->auto_install($this->getMailer(), $this->getUserManager(), $this->getTrackingManager());
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
|
||||
use php\IsEmailRule;
|
||||
use php\Rule;
|
||||
namespace php;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
|
||||
use php\IsNotBlankRule;
|
||||
use php\Rule;
|
||||
namespace php;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
|
||||
use php\IsSetRule;
|
||||
use php\Rule;
|
||||
namespace php;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?php
|
||||
|
||||
use php\LengthRule;
|
||||
use php\Rule;
|
||||
namespace php;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace php;
|
||||
|
||||
use Monolog\Test\TestCase;
|
||||
use php\Rule;
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
namespace php;
|
||||
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
|
||||
|
||||
/**
|
||||
* Unit tests for `UserManager`.
|
||||
*/
|
||||
class UserManagerTest extends DatabaseTestCase
|
||||
{
|
||||
private UserManager $user_manager;
|
||||
private Mailer&MockObject $mailer;
|
||||
|
||||
|
||||
public function getUserManager(): UserManager
|
||||
{
|
||||
return new UserManager($this->logger, $this->database->conn, $this->mailer);
|
||||
}
|
||||
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->mailer = $this->createMock(Mailer::class);
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$this->user_manager = $this->getUserManager();
|
||||
}
|
||||
|
||||
|
||||
public function test_register_user_returns_an_unsatisfied_response_if_the_email_is_used(): void
|
||||
{
|
||||
$this->mailer->method("queue_email")->willReturn(Response::satisfied());
|
||||
$this->user_manager->register_user("test@test.test", "password");
|
||||
|
||||
$response = $this->user_manager->register_user("test@test.test", "password");
|
||||
|
||||
self::assertFalse($response->satisfied);
|
||||
}
|
||||
|
||||
public function test_register_user_returns_a_satisfied_response_if_the_email_is_unused(): void
|
||||
{
|
||||
$this->mailer->method("queue_email")->willReturn(Response::satisfied());
|
||||
|
||||
$response = $this->user_manager->register_user("test@test.test", "password");
|
||||
|
||||
self::assertTrue($response->satisfied);
|
||||
}
|
||||
|
||||
public function test_register_user_sends_an_email_with_the_verification_token(): void
|
||||
{
|
||||
$this->mailer
|
||||
->expects($this->once())
|
||||
->method("queue_email")
|
||||
->with($this->callback(fn($email) => $email instanceof RegisterEmail && $email->recipient === "test@test.test"))
|
||||
->willReturn(Response::satisfied());
|
||||
|
||||
$response = $this->user_manager->register_user("test@test.test", "password");
|
||||
|
||||
self::assertTrue($response->satisfied);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace php;
|
||||
|
||||
use Monolog\Test\TestCase;
|
||||
use php\IsEmailRule;
|
||||
use php\IsNotBlankRule;
|
||||
use php\IsSetRule;
|
||||
use php\Validator;
|
||||
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue