diff --git a/composer.json b/composer.json index 62fa8ff..2067fbc 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "prettier": true, "typescript": false, "bundlewatch": false, - "backendTesting": false, + "backendTesting": true, "editorConfig": true, "styleci": true } @@ -64,5 +64,28 @@ } ], "minimum-stability": "dev", - "prefer-stable": true + "prefer-stable": true, + "autoload-dev": { + "psr-4": { + "Flarum\\Subscriptions\\Tests\\": "tests/" + } + }, + "scripts": { + "test": [ + "@test:unit", + "@test:integration" + ], + "test:unit": "phpunit -c tests/phpunit.unit.xml", + "test:integration": "phpunit -c tests/phpunit.integration.xml", + "test:setup": "@php tests/integration/setup.php" + }, + "scripts-descriptions": { + "test": "Runs all tests.", + "test:unit": "Runs all unit tests.", + "test:integration": "Runs all integration tests.", + "test:setup": "Sets up a database for use with integration tests. Execute this only once." + }, + "require-dev": { + "flarum/testing": "^1.0.0" + } } diff --git a/src/Job/SendReplyNotification.php b/src/Job/SendReplyNotification.php index 5d87e88..6f3d421 100644 --- a/src/Job/SendReplyNotification.php +++ b/src/Job/SendReplyNotification.php @@ -49,7 +49,7 @@ public function handle(NotificationSyncer $notifications) $notify = $discussion->readers() ->where('users.id', '!=', $post->user_id) ->where('discussion_user.subscription', 'follow') - ->where('discussion_user.last_read_post_number', $this->lastPostNumber) + ->where('discussion_user.last_read_post_number', $this->lastPostNumber - 1) ->get(); $notifications->sync( diff --git a/tests/fixtures/.gitkeep b/tests/fixtures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration/api/discussions/ReplyNotificationTest.php b/tests/integration/api/discussions/ReplyNotificationTest.php new file mode 100644 index 0000000..4dd4a8b --- /dev/null +++ b/tests/integration/api/discussions/ReplyNotificationTest.php @@ -0,0 +1,142 @@ +extension('flarum-subscriptions'); + + $this->prepareDatabase([ + 'users' => [ + $this->normalUser(), + ], + 'discussions' => [ + ['id' => 1, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 1, 'first_post_id' => 1, 'comment_count' => 1, 'last_post_number' => 1, 'last_post_id' => 1], + ['id' => 2, 'title' => __CLASS__, 'created_at' => Carbon::now(), 'last_posted_at' => Carbon::now(), 'user_id' => 1, 'first_post_id' => 2, 'comment_count' => 1, 'last_post_number' => 1, 'last_post_id' => 2], + ], + 'posts' => [ + ['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::createFromDate(1975, 5, 21)->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '

foo bar

', 'number' => 1], + ['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::createFromDate(1975, 5, 21)->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '

foo bar

', 'number' => 1], + ], + 'discussion_user' => [ + ['discussion_id' => 1, 'user_id' => 1, 'last_read_post_number' => 1, 'subscription' => 'follow'], + ['discussion_id' => 2, 'user_id' => 1, 'last_read_post_number' => 1, 'subscription' => 'follow'], + ] + ]); + } + + /** @test */ + public function replying_to_a_discussion_with_comment_post_as_last_post_sends_reply_notification() + { + $this->app(); + + /** @var User $mainUser */ + $mainUser = User::query()->find(1); + + $this->assertEquals(0, $this->getUnreadNotificationCount($mainUser)); + + $this->send( + $this->request('POST', '/api/posts', [ + 'authenticatedAs' => 2, + 'json' => [ + 'data' => [ + 'attributes' => [ + 'content' => 'reply with predetermined content for automated testing - too-obscure', + ], + 'relationships' => [ + 'discussion' => ['data' => ['id' => 1]], + ], + ], + ], + ]) + ); + + $this->assertEquals(1, $this->getUnreadNotificationCount($mainUser)); + } + + /** @test */ + public function replying_to_a_discussion_with_event_post_as_last_post_sends_reply_notification() + { + $this->app(); + + /** @var User $mainUser */ + $mainUser = User::query()->find(1); + + // Rename the discussion to trigger an event post. + $this->send( + $this->request('POST', '/api/discussions/2', [ + 'authenticatedAs' => 1, + 'json' => [ + 'data' => [ + 'attributes' => [ + 'title' => 'ACME', + ], + ], + ], + ]) + ); + + // Mark as read + $this->send( + $this->request('POST', '/api/discussions/2', [ + 'authenticatedAs' => 1, + 'json' => [ + 'data' => [ + 'attributes' => [ + 'lastReadPostNumber' => 2, + ], + ], + ], + ]) + ); + + $this->assertEquals(0, $this->getUnreadNotificationCount($mainUser)); + + $this->send( + $this->request('POST', '/api/posts', [ + 'authenticatedAs' => 2, + 'json' => [ + 'data' => [ + 'attributes' => [ + 'content' => 'reply with predetermined content for automated testing - too-obscure', + ], + 'relationships' => [ + 'discussion' => ['data' => ['id' => 2]], + ], + ], + ], + ]) + ); + + $this->assertEquals(1, $this->getUnreadNotificationCount($mainUser)); + } + + /** @todo change after core no longer statically caches unread notification in the User class */ + protected function getUnreadNotificationCount(User $user) + { + return $user->notifications() + ->where('type', 'newPost') + ->whereNull('read_at') + ->where('is_deleted', false) + ->whereSubjectVisibleTo($user) + ->count(); + } +} diff --git a/tests/integration/setup.php b/tests/integration/setup.php new file mode 100644 index 0000000..67039c0 --- /dev/null +++ b/tests/integration/setup.php @@ -0,0 +1,16 @@ +run(); diff --git a/tests/phpunit.integration.xml b/tests/phpunit.integration.xml new file mode 100644 index 0000000..90fbbff --- /dev/null +++ b/tests/phpunit.integration.xml @@ -0,0 +1,25 @@ + + + + + ../src/ + + + + + ./integration + ./integration/tmp + + + diff --git a/tests/phpunit.unit.xml b/tests/phpunit.unit.xml new file mode 100644 index 0000000..d3a4a3e --- /dev/null +++ b/tests/phpunit.unit.xml @@ -0,0 +1,27 @@ + + + + + ../src/ + + + + + ./unit + + + + + + diff --git a/tests/unit/.gitkeep b/tests/unit/.gitkeep new file mode 100644 index 0000000..e69de29