diff --git a/Gruntfile.js b/Gruntfile.js
index edd64de..8433fc9 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,6 +1,8 @@
const path = require("path");
module.exports = grunt => {
+ const testTarget = grunt.option("test-target") || ".";
+
grunt.initConfig({
pkg: grunt.file.readJSON("package.json"),
clean: {
@@ -69,7 +71,7 @@ module.exports = grunt => {
exec: "composer.phar install --no-dev"
},
phpunit: {
- exec: "cd dist/ && chmod +x .vendor/bin/phpunit && .vendor/bin/phpunit --testdox ."
+ exec: `cd dist/ && chmod +x .vendor/bin/phpunit && .vendor/bin/phpunit --testdox ${testTarget}`
},
stan: {
exec: "vendor/bin/phpstan analyse -l 8 src/main src/test"
diff --git a/README.md b/README.md
index 7874bdb..823af40 100644
--- a/README.md
+++ b/README.md
@@ -44,8 +44,10 @@ $> npm install
```shell script
# Run static analysis
$> npm run analyze
-# Run tests
+# Run all tests
$> npm run test
+# Run all tests in package
+$> npm run test -- --test-target=com/fwdekker/deathnotifier/wikipedia/
# Run static analysis and tests
$> npm run check
```
diff --git a/composer.json b/composer.json
index a5ce7f2..5d8ef61 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,7 @@
{
"name": "fwdekker/death-notifier",
"description": "Get notified when a famous person dies.",
- "version": "0.19.13", "_comment_version": "Also update version in `package.json`!",
+ "version": "0.19.14", "_comment_version": "Also update version in `package.json`!",
"type": "project",
"license": "MIT",
"homepage": "https://git.fwdekker.com/tools/death-notifier",
diff --git a/composer.lock b/composer.lock
index 3332fa7..dc5ca96 100644
Binary files a/composer.lock and b/composer.lock differ
diff --git a/package-lock.json b/package-lock.json
index 2f44808..48bcc31 100644
Binary files a/package-lock.json and b/package-lock.json differ
diff --git a/package.json b/package.json
index 2de37a7..6dfee23 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "death-notifier",
- "version": "0.19.13", "_comment_version": "Also update version in `composer.json`!",
+ "version": "0.19.14", "_comment_version": "Also update version in `composer.json`!",
"description": "Get notified when a famous person dies.",
"author": "Florine W. Dekker",
"browser": "dist/bundle.js",
diff --git a/src/main/index.html b/src/main/index.html
index f798ae1..d4d10f8 100644
--- a/src/main/index.html
+++ b/src/main/index.html
@@ -348,6 +348,7 @@
+
diff --git a/src/main/php/com/fwdekker/deathnotifier/mailer/EmailQueue.php b/src/main/php/com/fwdekker/deathnotifier/mailer/EmailQueue.php
index 85d56a9..1e1da77 100644
--- a/src/main/php/com/fwdekker/deathnotifier/mailer/EmailQueue.php
+++ b/src/main/php/com/fwdekker/deathnotifier/mailer/EmailQueue.php
@@ -56,6 +56,19 @@ class EmailQueue
}
+ /**
+ * Returns all queued emails.
+ *
+ * @return array all queued
+ * emails
+ */
+ public function get_queue(): array
+ {
+ $stmt = $this->database->conn->prepare("SELECT type_key, recipient, subject, body FROM email_tasks;");
+ $stmt->execute();
+ return $stmt->fetchAll(PDO::FETCH_ASSOC);
+ }
+
/**
* Adds {@see Email Emails} to the queue.
*
@@ -66,7 +79,7 @@ class EmailQueue
* @param array $emails the emails to queue
* @return void
*/
- public function queue_emails(array $emails): void
+ public function enqueue(array $emails): void
{
$stmt = $this->database->conn->prepare("INSERT OR REPLACE INTO email_tasks (type_key, recipient, subject, body)
VALUES (:type_key, :recipient, :subject, :body);");
@@ -85,26 +98,13 @@ class EmailQueue
}
}
- /**
- * Returns all queued emails.
- *
- * @return array all queued
- * emails
- */
- public function get_queue(): array
- {
- $stmt = $this->database->conn->prepare("SELECT type_key, recipient, subject, body FROM email_tasks;");
- $stmt->execute();
- return $stmt->fetchAll(PDO::FETCH_ASSOC);
- }
-
/**
* Removes mails from the queue.
*
* @param array $emails the emails to remove from the queue
* @return void
*/
- public function unqueue_emails(array $emails): void
+ public function unqueue(array $emails): void
{
$stmt = $this->database->conn->prepare("DELETE FROM email_tasks
WHERE type_key=:type_key AND recipient=:recipient;");
diff --git a/src/main/php/com/fwdekker/deathnotifier/mailer/ProcessEmailQueueAction.php b/src/main/php/com/fwdekker/deathnotifier/mailer/ProcessEmailQueueAction.php
index 47e8074..53681a0 100644
--- a/src/main/php/com/fwdekker/deathnotifier/mailer/ProcessEmailQueueAction.php
+++ b/src/main/php/com/fwdekker/deathnotifier/mailer/ProcessEmailQueueAction.php
@@ -65,7 +65,7 @@ class ProcessEmailQueueAction extends Action
$mailer->clearAddresses();
}
- $this->email_queue->unqueue_emails($emails);
+ $this->email_queue->unqueue($emails);
return null;
}
diff --git a/src/main/php/com/fwdekker/deathnotifier/tracking/UpdateTrackingsAction.php b/src/main/php/com/fwdekker/deathnotifier/tracking/UpdateTrackingsAction.php
index ee2393f..558b640 100644
--- a/src/main/php/com/fwdekker/deathnotifier/tracking/UpdateTrackingsAction.php
+++ b/src/main/php/com/fwdekker/deathnotifier/tracking/UpdateTrackingsAction.php
@@ -133,7 +133,7 @@ class UpdateTrackingsAction extends Action
$emails[] = new NotifyStatusChangedEmail($user_email, $person_name, $person_status->value);
}
- $this->mailer->queue_emails($emails);
+ $this->mailer->enqueue($emails);
return null;
}
}
diff --git a/src/main/php/com/fwdekker/deathnotifier/user/ChangeEmailAction.php b/src/main/php/com/fwdekker/deathnotifier/user/ChangeEmailAction.php
index 16001ff..4a679a0 100644
--- a/src/main/php/com/fwdekker/deathnotifier/user/ChangeEmailAction.php
+++ b/src/main/php/com/fwdekker/deathnotifier/user/ChangeEmailAction.php
@@ -79,7 +79,7 @@ class ChangeEmailAction extends Action
throw new InvalidValueException("That email address is already in use by someone else.", "email");
$token = $this->user_list->set_email($_SESSION["uuid"], $inputs["email"]);
- $this->email_queue->queue_emails([
+ $this->email_queue->enqueue([
new ChangeEmailFromEmail($user_data["email"], $inputs["email"]),
new ChangeEmailToEmail($inputs["email"], $user_data["email"], $token)
]);
diff --git a/src/main/php/com/fwdekker/deathnotifier/user/ChangePasswordAction.php b/src/main/php/com/fwdekker/deathnotifier/user/ChangePasswordAction.php
index a4d1e24..a1e861f 100644
--- a/src/main/php/com/fwdekker/deathnotifier/user/ChangePasswordAction.php
+++ b/src/main/php/com/fwdekker/deathnotifier/user/ChangePasswordAction.php
@@ -77,7 +77,7 @@ class ChangePasswordAction extends Action
throw new InvalidValueException("Incorrect old password.", "password_old");
$this->user_list->set_password($_SESSION["uuid"], $inputs["password_new"]);
- $this->email_queue->queue_emails([new ChangePasswordEmail($user_data["email"])]);
+ $this->email_queue->enqueue([new ChangePasswordEmail($user_data["email"])]);
});
return null;
diff --git a/src/main/php/com/fwdekker/deathnotifier/user/RegisterAction.php b/src/main/php/com/fwdekker/deathnotifier/user/RegisterAction.php
index 79593a6..7e9a5ce 100644
--- a/src/main/php/com/fwdekker/deathnotifier/user/RegisterAction.php
+++ b/src/main/php/com/fwdekker/deathnotifier/user/RegisterAction.php
@@ -71,7 +71,7 @@ class RegisterAction extends Action
throw new InvalidValueException("That email address already in use.", "email");
$token = $this->user_list->add_user($inputs["email"], $inputs["password"]);
- $this->email_queue->queue_emails([new RegisterEmail($inputs["email"], $token)]);
+ $this->email_queue->enqueue([new RegisterEmail($inputs["email"], $token)]);
});
return null;
diff --git a/src/main/php/com/fwdekker/deathnotifier/user/ResendVerifyEmailAction.php b/src/main/php/com/fwdekker/deathnotifier/user/ResendVerifyEmailAction.php
index f58e79e..a3455b3 100644
--- a/src/main/php/com/fwdekker/deathnotifier/user/ResendVerifyEmailAction.php
+++ b/src/main/php/com/fwdekker/deathnotifier/user/ResendVerifyEmailAction.php
@@ -82,7 +82,7 @@ class ResendVerifyEmailAction extends Action
}
$token = $this->user_list->set_email($_SESSION["uuid"], $user_data["email"]);
- $this->email_queue->queue_emails([new ResendVerifyEmailEmail($user_data["email"], $token)]);
+ $this->email_queue->enqueue([new ResendVerifyEmailEmail($user_data["email"], $token)]);
});
return null;
diff --git a/src/main/php/com/fwdekker/deathnotifier/user/ResetPasswordAction.php b/src/main/php/com/fwdekker/deathnotifier/user/ResetPasswordAction.php
index 1e97b86..e87ed35 100644
--- a/src/main/php/com/fwdekker/deathnotifier/user/ResetPasswordAction.php
+++ b/src/main/php/com/fwdekker/deathnotifier/user/ResetPasswordAction.php
@@ -81,7 +81,7 @@ class ResetPasswordAction extends Action
throw new IllegalStateError("User data is `null` despite previous validation.");
$this->user_list->set_password($user_data["uuid"], $inputs["password"]);
- $this->email_queue->queue_emails([new ResetPasswordEmail($inputs["email"])]);
+ $this->email_queue->enqueue([new ResetPasswordEmail($inputs["email"])]);
});
return null;
diff --git a/src/main/php/com/fwdekker/deathnotifier/user/SendPasswordResetAction.php b/src/main/php/com/fwdekker/deathnotifier/user/SendPasswordResetAction.php
index 98b0a79..37bf7b2 100644
--- a/src/main/php/com/fwdekker/deathnotifier/user/SendPasswordResetAction.php
+++ b/src/main/php/com/fwdekker/deathnotifier/user/SendPasswordResetAction.php
@@ -80,7 +80,7 @@ class SendPasswordResetAction extends Action
}
$token = $this->user_list->register_password_reset($inputs["email"]);
- $this->email_queue->queue_emails([new SendPasswordResetEmail($inputs["email"], $token)]);
+ $this->email_queue->enqueue([new SendPasswordResetEmail($inputs["email"], $token)]);
});
return null;
diff --git a/src/test/php/com/fwdekker/deathnotifier/DatabaseTestCase.php b/src/test/php/com/fwdekker/deathnotifier/DatabaseTestCase.php
index d16b49d..6e81118 100644
--- a/src/test/php/com/fwdekker/deathnotifier/DatabaseTestCase.php
+++ b/src/test/php/com/fwdekker/deathnotifier/DatabaseTestCase.php
@@ -10,10 +10,11 @@ use PHPUnit\Framework\TestCase;
/**
- * For tests that use a fully-installed database.
+ * Provides a fresh database for each test.
*
- * By default, only the main schema of the database is installed. Override the getters to additionally install the
- * corresponding parts of the schema.
+ * By default, the fields {@see DatabaseTestCase::$email_queue}, {@see DatabaseTestCase::$user_list}, and
+ * {@see DatabaseTestCase::$tracking_list} are mocks, and hence their `install` methods are not called. To change this,
+ * override {@see DatabaseTestCase::setUpWithDatabase()}.
*/
abstract class DatabaseTestCase extends TestCase
{
@@ -26,31 +27,18 @@ abstract class DatabaseTestCase extends TestCase
* @var Database the database
*/
public Database $database;
-
-
/**
- * @return EmailQueue the `EmailQueue` to install the database schema of
+ * @var EmailQueue the `EmailQueue` to install the database schema of
*/
- function get_email_queue(): EmailQueue
- {
- return $this->createStub(EmailQueue::class);
- }
-
+ public EmailQueue $email_queue;
/**
- * @return TrackingList the `TrackingList` to install the database schema of
+ * @var UserList the `UserList` to install the database schema of
*/
- function get_tracking_list(): TrackingList
- {
- return $this->createStub(TrackingList::class);
- }
-
+ public UserList $user_list;
/**
- * @return UserList the `UserList` to install the database schema of
+ * @var TrackingList the `TrackingList` to install the database schema of
*/
- function get_user_list(): UserList
- {
- return $this->createStub(UserList::class);
- }
+ public TrackingList $tracking_list;
/**
@@ -87,6 +75,22 @@ abstract class DatabaseTestCase extends TestCase
if ($db_tmp_file === false) throw new Exception("Failed to create temporary database file.");
$this->database = new Database($db_tmp_file);
- $this->database->auto_install($this->get_email_queue(), $this->get_user_list(), $this->get_tracking_list());
+ $this->setUpWithDatabase($this->database);
+ $this->database->auto_install($this->email_queue, $this->user_list, $this->tracking_list);
+ }
+
+ /**
+ * Creates the {@see EmailQueue}, {@see UserList}, and {@see TrackingList} using {@see $database}.
+ *
+ * By default, all three structures are stubs. Override this method to change that.
+ *
+ * @param Database $database the `Database` with which to instantiate the structures
+ * @return void
+ */
+ public function setUpWithDatabase(Database $database): void
+ {
+ $this->email_queue = $this->createStub(EmailQueue::class);
+ $this->user_list = $this->createStub(UserList::class);
+ $this->tracking_list = $this->createStub(TrackingList::class);
}
}
diff --git a/src/test/php/com/fwdekker/deathnotifier/mailer/EmailQueueTest.php b/src/test/php/com/fwdekker/deathnotifier/mailer/EmailQueueTest.php
new file mode 100644
index 0000000..2bcf702
--- /dev/null
+++ b/src/test/php/com/fwdekker/deathnotifier/mailer/EmailQueueTest.php
@@ -0,0 +1,132 @@
+email_queue = new EmailQueue($database);
+ }
+
+
+ public function test_get_queue_initially_empty(): void
+ {
+ self::assertEmpty($this->email_queue->get_queue());
+ }
+
+ public function test_get_queue_returns_added_emails(): void
+ {
+ $this->email_queue->enqueue([
+ new TestEmail("key1", "recipient1", "subject1", "body1"),
+ new TestEmail("key2", "recipient2", "subject2", "body2"),
+ ]);
+
+ self::assertSame(
+ [
+ ["type_key" => "key1", "recipient" => "recipient1", "subject" => "subject1", "body" => "body1"],
+ ["type_key" => "key2", "recipient" => "recipient2", "subject" => "subject2", "body" => "body2"],
+ ],
+ $this->email_queue->get_queue()
+ );
+ }
+
+
+ public function test_enqueue_adds_emails(): void
+ {
+ $this->email_queue->enqueue([new TestEmail(type_key: "key1"), new TestEmail(type_key: "key2")]);
+
+ self::assertSame(["key1", "key2"], array_column($this->email_queue->get_queue(), "type_key"));
+ }
+
+ public function test_enqueue_appends_emails(): void
+ {
+ $this->email_queue->enqueue([new TestEmail(type_key: "key1"), new TestEmail(type_key: "key2")]);
+
+ $this->email_queue->enqueue([new TestEmail(type_key: "key3"), new TestEmail(type_key: "key4")]);
+
+ self::assertSame(["key1", "key2", "key3", "key4"], array_column($this->email_queue->get_queue(), "type_key"));
+ }
+
+ public function test_enqueue_appends_email_if_only_type_key_is_different(): void
+ {
+ $this->email_queue->enqueue([new TestEmail(type_key: "key1", recipient: "recipient")]);
+
+ $this->email_queue->enqueue([new TestEmail(type_key: "key2", recipient: "recipient")]);
+
+ $queue = $this->email_queue->get_queue();
+ self::assertSame(["key1", "key2"], array_column($queue, "type_key"));
+ self::assertSame(["recipient", "recipient"], array_column($queue, "recipient"));
+ }
+
+ public function test_enqueue_appends_email_if_only_recipient_is_different(): void
+ {
+ $this->email_queue->enqueue([new TestEmail(type_key: "key", recipient: "recipient1")]);
+
+ $this->email_queue->enqueue([new TestEmail(type_key: "key", recipient: "recipient2")]);
+
+ $queue = $this->email_queue->get_queue();
+ self::assertSame(["key", "key"], array_column($queue, "type_key"));
+ self::assertSame(["recipient1", "recipient2"], array_column($queue, "recipient"));
+ }
+
+ public function test_enqueue_replaces_email_if_both_type_key_and_recipient_are_same(): void
+ {
+ $this->email_queue->enqueue([new TestEmail(type_key: "key", recipient: "recipient", subject: "old_subject")]);
+
+ $this->email_queue->enqueue([new TestEmail(type_key: "key", recipient: "recipient", subject: "new_subject")]);
+
+ $queue = $this->email_queue->get_queue();
+ self::assertSame(["key"], array_column($queue, "type_key"));
+ self::assertSame(["recipient"], array_column($queue, "recipient"));
+ self::assertSame(["new_subject"], array_column($queue, "subject"));
+ }
+
+
+ public function test_unqueue_does_not_remove_if_both_type_key_and_recipient_are_different(): void
+ {
+ $this->email_queue->enqueue([new TestEmail(type_key: "key1", recipient: "recipient1")]);
+
+ $this->email_queue->unqueue([["type_key" => "key2", "recipient" => "recipient2"]]);
+
+ $queue = $this->email_queue->get_queue();
+ self::assertSame(["key1"], array_column($queue, "type_key"));
+ self::assertSame(["recipient1"], array_column($queue, "recipient"));
+ }
+
+ public function test_unqueue_does_not_remove_if_type_key_is_different(): void
+ {
+ $this->email_queue->enqueue([new TestEmail(type_key: "key1", recipient: "recipient")]);
+
+ $this->email_queue->unqueue([["type_key" => "key2", "recipient" => "recipient"]]);
+
+ self::assertSame(["key1"], array_column($this->email_queue->get_queue(), "type_key"));
+ }
+
+ public function test_unqueue_does_not_remove_if_recipient_is_different(): void
+ {
+ $this->email_queue->enqueue([new TestEmail(type_key: "key", recipient: "recipient1")]);
+
+ $this->email_queue->unqueue([["type_key" => "key", "recipient" => "recipient2"]]);
+
+ self::assertSame(["recipient1"], array_column($this->email_queue->get_queue(), "recipient"));
+ }
+
+ public function test_unqueue_removes_email_with_same_key_and_recipient(): void
+ {
+ $this->email_queue->enqueue([new TestEmail(type_key: "key", recipient: "recipient")]);
+
+ $this->email_queue->unqueue([["type_key" => "key", "recipient" => "recipient"]]);
+
+ self::assertEmpty($this->email_queue->get_queue());
+ }
+}
diff --git a/src/test/php/com/fwdekker/deathnotifier/mailer/TestEmail.php b/src/test/php/com/fwdekker/deathnotifier/mailer/TestEmail.php
new file mode 100644
index 0000000..c691c63
--- /dev/null
+++ b/src/test/php/com/fwdekker/deathnotifier/mailer/TestEmail.php
@@ -0,0 +1,34 @@
+subject = $subject;
+ $this->body = $body;
+ }
+
+
+ public function get_subject(): string
+ {
+ return $this->subject;
+ }
+
+ public function get_body(): string
+ {
+ return $this->body;
+ }
+}
diff --git a/src/test/php/com/fwdekker/deathnotifier/wikipedia/RedirectsTest.php b/src/test/php/com/fwdekker/deathnotifier/wikipedia/RedirectsTest.php
index bc6782f..15fc873 100644
--- a/src/test/php/com/fwdekker/deathnotifier/wikipedia/RedirectsTest.php
+++ b/src/test/php/com/fwdekker/deathnotifier/wikipedia/RedirectsTest.php
@@ -163,6 +163,6 @@ class RedirectsTest extends TestCase
foreach ($redirects as $from => $to)
$extracted[$from] = $to;
- self::assertTrue($extracted === $inserted);
+ self::assertSame($extracted, $inserted);
}
}
diff --git a/src/test/php/com/fwdekker/deathnotifier/wikipedia/WikipediaTest.php b/src/test/php/com/fwdekker/deathnotifier/wikipedia/WikipediaTest.php
index 4fd1823..9a53d1f 100644
--- a/src/test/php/com/fwdekker/deathnotifier/wikipedia/WikipediaTest.php
+++ b/src/test/php/com/fwdekker/deathnotifier/wikipedia/WikipediaTest.php
@@ -6,13 +6,16 @@ use PHPUnit\Framework\TestCase;
/**
- * Unit tests for {@see Wikipedia}.
+ * Integration tests for {@see Wikipedia}.
*
* These tests are slow because they directly communicate with Wikipedia. No mocking going on here. This also means that
* in several years, some of these tests will fail.
*/
class WikipediaTest extends TestCase
{
+ /**
+ * @var Wikipedia the `Wikipedia` instance to test on
+ */
private Wikipedia $wikipedia;