1
0
mirror of https://github.com/flarum/core.git synced 2025-08-08 09:26:34 +02:00

feat: messages extension (#4028)

* feat: private messages
This commit is contained in:
Sami Mazouz
2024-09-28 11:12:52 +01:00
committed by GitHub
parent bc4356a7f5
commit b74ecbfacf
186 changed files with 5331 additions and 605 deletions

View File

View File

@@ -0,0 +1,128 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Messages\Tests\integration\api;
use Carbon\Carbon;
use Flarum\Messages\Dialog;
use Flarum\Messages\DialogMessage;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Flarum\User\User;
use PHPUnit\Framework\Attributes\DataProvider;
class ListTest extends TestCase
{
use RetrievesAuthorizedUsers;
protected function setUp(): void
{
parent::setUp();
$this->extension('flarum-messages');
$this->prepareDatabase([
User::class => [
['id' => 3, 'username' => 'astarion'],
['id' => 4, 'username' => 'gale'],
['id' => 5, 'username' => 'karlach'],
],
Dialog::class => [
['id' => 102, 'type' => 'direct'],
['id' => 103, 'type' => 'direct'],
['id' => 104, 'type' => 'direct'],
],
DialogMessage::class => [
['id' => 102, 'dialog_id' => 102, 'user_id' => 3, 'content' => 'Hello, Gale!'],
['id' => 103, 'dialog_id' => 102, 'user_id' => 4, 'content' => 'Hello, Astarion!'],
['id' => 104, 'dialog_id' => 103, 'user_id' => 3, 'content' => 'Hello, Karlach!'],
['id' => 105, 'dialog_id' => 103, 'user_id' => 5, 'content' => 'Hello, Astarion!'],
['id' => 106, 'dialog_id' => 104, 'user_id' => 4, 'content' => 'Hello, Karlach!'],
['id' => 107, 'dialog_id' => 104, 'user_id' => 5, 'content' => 'Hello, Gale!'],
],
'dialog_user' => [
['dialog_id' => 102, 'user_id' => 3, 'joined_at' => Carbon::now()],
['dialog_id' => 102, 'user_id' => 4, 'joined_at' => Carbon::now()],
['dialog_id' => 103, 'user_id' => 3, 'joined_at' => Carbon::now()],
['dialog_id' => 103, 'user_id' => 5, 'joined_at' => Carbon::now()],
['dialog_id' => 104, 'user_id' => 4, 'joined_at' => Carbon::now()],
['dialog_id' => 104, 'user_id' => 5, 'joined_at' => Carbon::now()],
],
]);
}
#[DataProvider('dialogsAccessProvider')]
public function test_can_list_accessible_dialogs(int $actorId, array $visibleDialogs): void
{
$response = $this->send(
$this->request('GET', '/api/dialogs', [
'authenticatedAs' => $actorId,
])->withQueryParams(['include' => 'users'])
);
$json = $response->getBody()->getContents();
$prettyJson = json_encode($json, JSON_PRETTY_PRINT);
$this->assertEquals(200, $response->getStatusCode(), $prettyJson);
$this->assertJson($json);
$data = json_decode($json, true)['data'];
$this->assertCount(count($visibleDialogs), $data);
foreach ($visibleDialogs as $dialogId) {
$ids = array_column($data, 'id');
$this->assertContains((string) $dialogId, $ids, json_encode($ids, JSON_PRETTY_PRINT));
}
}
public static function dialogsAccessProvider(): array
{
return [
'Astarion can see dialogs with Gale and Karlach' => [3, [102, 103]],
'Gale can see dialogs with Astarion and Karlach' => [4, [102, 104]],
'Karlach can see dialogs with Astarion and Gale' => [5, [103, 104]],
];
}
#[DataProvider('dialogMessagesAccessProvider')]
public function test_can_list_accessible_dialog_messages(int $actorId, array $visibleDialogMessages): void
{
$response = $this->send(
$this->request('GET', '/api/dialog-messages', [
'authenticatedAs' => $actorId,
])->withQueryParams(['include' => 'dialog']),
);
$json = $response->getBody()->getContents();
$prettyJson = json_encode($json, JSON_PRETTY_PRINT);
$this->assertEquals(200, $response->getStatusCode(), $prettyJson);
$this->assertJson($json);
$data = json_decode($json, true)['data'];
$prettyJson = json_encode(json_decode($json), JSON_PRETTY_PRINT);
$this->assertCount(count($visibleDialogMessages), $data, $prettyJson);
foreach ($visibleDialogMessages as $dialogMessageId) {
$ids = array_column($data, 'id');
$this->assertContains((string) $dialogMessageId, $ids, json_encode($ids, JSON_PRETTY_PRINT));
}
}
public static function dialogMessagesAccessProvider(): array
{
return [
'Astarion can see messages in dialogs with Gale and Karlach' => [3, [102, 103, 104, 105]],
'Gale can see messages in dialogs with Astarion and Karlach' => [4, [102, 103, 106, 107]],
'Karlach can see messages in dialogs with Astarion and Gale' => [5, [104, 105, 106, 107]],
];
}
}

View File

@@ -0,0 +1,112 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Messages\Tests\integration\api\dialog_messages;
use Carbon\Carbon;
use Flarum\Messages\Dialog;
use Flarum\Messages\DialogMessage;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Flarum\User\User;
class CreateTest extends TestCase
{
use RetrievesAuthorizedUsers;
protected function setUp(): void
{
parent::setUp();
$this->extension('flarum-messages');
$this->prepareDatabase([
User::class => [
['id' => 3, 'username' => 'alice'],
['id' => 4, 'username' => 'bob'],
['id' => 5, 'username' => 'karlach'],
],
Dialog::class => [
['id' => 102, 'type' => 'direct'],
],
DialogMessage::class => [
['id' => 102, 'dialog_id' => 102, 'user_id' => 4, 'content' => 'Hello, Karlach!'],
],
'dialog_user' => [
['dialog_id' => 102, 'user_id' => 4, 'joined_at' => Carbon::now()],
['dialog_id' => 102, 'user_id' => 5, 'joined_at' => Carbon::now()],
],
]);
}
public function test_can_create_a_direct_private_conversation_with_someone(): void
{
$response = $this->send(
$this->request('POST', '/api/dialog-messages', [
'authenticatedAs' => 3,
'json' => [
'data' => [
'type' => 'dialog-messages',
'attributes' => [
'content' => 'Hello, Bob!',
'users' => [
['id' => 4],
],
],
],
],
])->withQueryParams(['include' => 'dialog.users,user'])
);
$json = $response->getBody()->getContents();
$data = json_decode($json, true);
$pretty = json_encode($data, JSON_PRETTY_PRINT);
$this->assertEquals(201, $response->getStatusCode(), $pretty);
$this->assertNotEquals(102, $data['data']['relationships']['dialog']['data']['id'], $pretty);
$this->assertEquals('direct', collect($data['included'])->firstWhere('type', 'dialogs')['attributes']['type'], $pretty);
$this->assertEquals('Hello, Bob!', $data['data']['attributes']['contentHtml'], $pretty);
$this->assertEqualsCanonicalizing([3, 4], collect(collect($data['included'])->firstWhere('type', 'dialogs')['relationships']['users']['data'])->pluck('id')->all(), $pretty);
}
public function test_can_create_a_private_message_when_conversation_already_exists(): void
{
$response = $this->send(
$this->request('POST', '/api/dialog-messages', [
'authenticatedAs' => 5,
'json' => [
'data' => [
'type' => 'dialog-messages',
'attributes' => [
'content' => 'Hello, Bob!',
],
'relationships' => [
'dialog' => [
'data' => [
'type' => 'dialogs',
'id' => '102',
],
],
],
],
],
])->withQueryParams(['include' => 'dialog.users,user'])
);
$json = $response->getBody()->getContents();
$data = json_decode($json, true);
$pretty = json_encode($data, JSON_PRETTY_PRINT);
$this->assertEquals(201, $response->getStatusCode(), $pretty);
$this->assertEquals(102, $data['data']['relationships']['dialog']['data']['id'], $pretty);
$this->assertEquals('direct', collect($data['included'])->firstWhere('type', 'dialogs')['attributes']['type'], $pretty);
$this->assertEquals('Hello, Bob!', $data['data']['attributes']['contentHtml'], $pretty);
$this->assertEqualsCanonicalizing([4, 5], collect(collect($data['included'])->firstWhere('type', 'dialogs')['relationships']['users']['data'])->pluck('id')->all(), $pretty);
}
}

View File

@@ -0,0 +1,112 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Messages\Tests\integration\api\dialogs;
use Carbon\Carbon;
use Flarum\Messages\Dialog;
use Flarum\Messages\DialogMessage;
use Flarum\Messages\UserDialogState;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Flarum\User\User;
class UpdateTest extends TestCase
{
use RetrievesAuthorizedUsers;
protected function setUp(): void
{
parent::setUp();
$this->extension('flarum-messages');
$this->prepareDatabase([
User::class => [
['id' => 3, 'username' => 'alice'],
['id' => 4, 'username' => 'bob'],
['id' => 5, 'username' => 'karlach'],
],
Dialog::class => [
['id' => 102, 'type' => 'direct', 'last_message_id' => 111],
],
DialogMessage::class => [
['id' => 102, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
['id' => 103, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
['id' => 104, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
['id' => 105, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
['id' => 106, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
['id' => 107, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
['id' => 108, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
['id' => 109, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
['id' => 110, 'dialog_id' => 102, 'user_id' => 4, 'content' => '<p>Hello, Alice!</p>'],
['id' => 111, 'dialog_id' => 102, 'user_id' => 3, 'content' => '<p>Hello, Bob!</p>'],
],
'dialog_user' => [
['dialog_id' => 102, 'user_id' => 3, 'last_read_message_id' => 0, 'last_read_at' => null, 'joined_at' => Carbon::now()],
['dialog_id' => 102, 'user_id' => 4, 'last_read_message_id' => 0, 'last_read_at' => null, 'joined_at' => Carbon::now()],
],
]);
}
public function test_can_mark_dialog_as_read(): void
{
$response = $this->send(
$this->request('PATCH', '/api/dialogs/102', [
'authenticatedAs' => 3,
'json' => [
'data' => [
'type' => 'dialogs',
'id' => '102',
'attributes' => [
'lastReadMessageId' => 107,
],
],
],
])
);
$this->assertEquals(200, $response->getStatusCode());
$state = UserDialogState::query()
->where('dialog_id', 102)
->where('user_id', 3)
->first();
$this->assertEquals(107, $state->last_read_message_id);
$this->assertNotNull($state->last_read_at);
}
public function test_can_mark_all_as_read(): void
{
$response = $this->send(
$this->request('POST', '/api/dialogs/read', [
'authenticatedAs' => 3,
])
);
$this->assertEquals(204, $response->getStatusCode(), json_encode(json_decode($response->getBody()->getContents()), JSON_PRETTY_PRINT));
$state = UserDialogState::query()
->where('dialog_id', 102)
->where('user_id', 3)
->first();
$nonState = UserDialogState::query()
->where('dialog_id', 102)
->where('user_id', '!=', 3)
->first();
$this->assertNotNull($state->last_read_at);
$this->assertNull($nonState->last_read_at);
$this->assertEquals(111, $state->last_read_message_id);
$this->assertEquals(0, $nonState->last_read_message_id);
}
}

View File

@@ -0,0 +1,12 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
$setup = require __DIR__.'/../../../../php-packages/testing/bootstrap/monorepo.php';
$setup->run();

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
backupGlobals="false"
cacheDirectory=".phpunit.cache"
backupStaticProperties="false"
colors="true"
processIsolation="true"
stopOnFailure="false"
bootstrap="../../../php-packages/testing/bootstrap/monorepo.php"
>
<source>
<include>
<directory suffix=".php">../src/</directory>
</include>
</source>
<testsuites>
<testsuite name="Flarum Integration Tests">
<directory suffix="Test.php">./integration</directory>
<exclude>./integration/tmp</exclude>
</testsuite>
</testsuites>
</phpunit>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
backupGlobals="false"
cacheDirectory=".phpunit.cache"
backupStaticProperties="false"
colors="true"
processIsolation="false"
stopOnFailure="false"
>
<source>
<include>
<directory suffix=".php">../src/</directory>
</include>
</source>
<testsuites>
<testsuite name="Flarum Unit Tests">
<directory suffix="Test.php">./unit</directory>
</testsuite>
</testsuites>
</phpunit>

View File