From 5094bb2f1ff2e9b28f12ddba2d480d7589f9a67a Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Tue, 5 Aug 2025 16:42:06 +0100 Subject: [PATCH] MDL-85706 message: implement messages datasource for custom reporting. Create new entities for report data on messages/conversations, joined to the user entity, providing data for the reportbuilder editor. --- public/lang/en/message.php | 3 + .../reportbuilder/datasource/messages.php | 149 +++++++++ .../local/entities/conversation.php | 205 ++++++++++++ .../reportbuilder/local/entities/message.php | 173 ++++++++++ .../datasource/messages_test.php | 300 ++++++++++++++++++ 5 files changed, 830 insertions(+) create mode 100644 public/message/classes/reportbuilder/datasource/messages.php create mode 100644 public/message/classes/reportbuilder/local/entities/conversation.php create mode 100644 public/message/classes/reportbuilder/local/entities/message.php create mode 100644 public/message/tests/reportbuilder/datasource/messages_test.php diff --git a/public/lang/en/message.php b/public/lang/en/message.php index 9734a8ce3a0..869db5c3a55 100644 --- a/public/lang/en/message.php +++ b/public/lang/en/message.php @@ -49,7 +49,9 @@ $string['contactblocked'] = 'Contact blocked'; $string['contactrequests'] = 'Contact requests'; $string['contactrequestsent'] = 'Contact request sent'; $string['contacts'] = 'Contacts'; +$string['conversation'] = 'Conversation'; $string['conversationactions'] = 'Conversation actions menu'; +$string['conversationtype'] = 'Type'; $string['decline'] = 'Decline'; $string['defaultmessageoutputs'] = 'Notification settings'; $string['deleteallconfirm'] = "Are you sure you would like to delete this entire conversation? This will not delete it for other conversation participants."; @@ -214,6 +216,7 @@ $string['processorsettings'] = 'Processor settings'; $string['providerenabled'] = 'Sending "{$a}" enabled status'; $string['providerprocesorislocked'] = '"{$a->provider}" on "{$a->processor}" is locked on'; $string['providerprocesorisdisallowed'] = '"{$a->provider}" on "{$a->processor}" is locked off'; +$string['recipient'] = 'Recipient'; $string['removecontact'] = 'Remove contact'; $string['removecontactconfirm'] = 'Are you sure you want to remove {$a} from your contacts?'; $string['removecoursefilter'] = 'Remove filter for course {$a}'; diff --git a/public/message/classes/reportbuilder/datasource/messages.php b/public/message/classes/reportbuilder/datasource/messages.php new file mode 100644 index 00000000000..81f1987cccd --- /dev/null +++ b/public/message/classes/reportbuilder/datasource/messages.php @@ -0,0 +1,149 @@ +. + +declare(strict_types=1); + +namespace core_message\reportbuilder\datasource; + +use core\lang_string; +use core_message\reportbuilder\local\entities\{conversation, message}; +use core_reportbuilder\datasource; +use core_reportbuilder\local\entities\user; +use core_reportbuilder\local\filters\text; +use core_reportbuilder\local\helpers\database; + +/** + * Messages datasource + * + * @package core_message + * @copyright 2025 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class messages extends datasource { + /** + * Return user friendly name of the datasource + * + * @return string + */ + public static function get_name(): string { + return get_string('messages', 'core_message'); + } + + /** + * Initialise report + */ + protected function initialise(): void { + // Message. + $messageentity = new message(); + $messagealias = $messageentity->get_table_alias('messages'); + + $this->set_main_table('messages', $messagealias); + $this->add_entity($messageentity); + + // Conversation. + $conversationentity = new conversation(); + $conversationalias = $conversationentity->get_table_alias('message_conversations'); + $this->add_entity($conversationentity + ->add_join("LEFT JOIN {message_conversations} {$conversationalias} + ON {$conversationalias}.id = {$messagealias}.conversationid")); + + // Author. + $authorentity = new user(); + $authoralias = $authorentity->get_table_alias('user'); + $this->add_entity($authorentity + ->add_join("LEFT JOIN {user} {$authoralias} ON {$authoralias}.id = {$messagealias}.useridfrom")); + + // Recipient. + $recipiententity = new user(); + $recipientalias = $recipiententity->get_table_alias('user'); + $membersalias = database::generate_alias(); + $this->add_entity($recipiententity + ->set_entity_name('recipient') + ->set_entity_title(new lang_string('recipient', 'core_message')) + ->add_joins($conversationentity->get_joins()) + ->add_joins([ + "LEFT JOIN {message_conversation_members} {$membersalias} + ON {$membersalias}.conversationid = {$conversationalias}.id + AND {$membersalias}.userid != {$messagealias}.useridfrom", + "LEFT JOIN {user} {$recipientalias} ON {$recipientalias}.id = {$membersalias}.userid", + ])); + + // Add all columns/filters/conditions from entities to be available in custom reports. + $this->add_all_from_entities(); + } + + /** + * Return the columns that will be added to the report as part of default setup + * + * @return string[] + */ + public function get_default_columns(): array { + return [ + 'user:fullname', + 'recipient:fullname', + 'message:message', + 'message:timecreated', + ]; + } + + /** + * Return the default sorting that will be added to the report upon creation + * + * @return int[] + */ + public function get_default_column_sorting(): array { + return [ + 'user:fullname' => SORT_ASC, + 'recipient:fullname' => SORT_ASC, + 'message:timecreated' => SORT_ASC, + ]; + } + + /** + * Return the filters that will be added to the report upon creation + * + * @return string[] + */ + public function get_default_filters(): array { + return [ + 'user:fullname', + 'recipient:fullname', + 'message:message', + ]; + } + + /** + * Return the conditions that will be added to the report upon creation + * + * @return string[] + */ + public function get_default_conditions(): array { + return [ + 'message:message', + ]; + } + + /** + * Return the condition values that will be set for the report upon creation + * + * @return array + */ + public function get_default_condition_values(): array { + return [ + 'message:message_operator' => text::IS_NOT_EMPTY, + ]; + } +} diff --git a/public/message/classes/reportbuilder/local/entities/conversation.php b/public/message/classes/reportbuilder/local/entities/conversation.php new file mode 100644 index 00000000000..06aed8ef1e7 --- /dev/null +++ b/public/message/classes/reportbuilder/local/entities/conversation.php @@ -0,0 +1,205 @@ +. + +declare(strict_types=1); + +namespace core_message\reportbuilder\local\entities; + +use core\lang_string; +use core_message\api; +use core_reportbuilder\local\entities\base; +use core_reportbuilder\local\filters\{boolean_select, date, select, text}; +use core_reportbuilder\local\helpers\format; +use core_reportbuilder\local\report\{column, filter}; + +/** + * Conversation entity + * + * @package core_message + * @copyright 2025 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class conversation extends base { + /** + * Database tables that this entity uses + * + * @return string[] + */ + protected function get_default_tables(): array { + return [ + 'message_conversations', + ]; + } + + /** + * The default title for this entity + * + * @return lang_string + */ + protected function get_default_entity_title(): lang_string { + return new lang_string('conversation', 'core_message'); + } + + /** + * Initialise the entity + * + * @return base + */ + public function initialise(): base { + $columns = $this->get_all_columns(); + foreach ($columns as $column) { + $this->add_column($column); + } + + // All the filters defined by the entity can also be used as conditions. + $filters = $this->get_all_filters(); + foreach ($filters as $filter) { + $this + ->add_filter($filter) + ->add_condition($filter); + } + + return $this; + } + + /** + * Returns list of all available columns + * + * @return column[] + */ + protected function get_all_columns(): array { + $conversationalias = $this->get_table_alias('message_conversations'); + + // Type. + $columns[] = (new column( + 'type', + new lang_string('conversationtype', 'core_message'), + $this->get_entity_name(), + )) + ->add_joins($this->get_joins()) + ->add_field("{$conversationalias}.type") + ->set_is_sortable(true) + ->add_callback(static function (?string $type): string { + $types = [ + api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new lang_string('individualconversations', 'core_message'), + api::MESSAGE_CONVERSATION_TYPE_GROUP => new lang_string('groupconversations', 'core_message'), + api::MESSAGE_CONVERSATION_TYPE_SELF => new lang_string('selfconversation', 'core_message'), + ]; + if ($type === null || !array_key_exists($type, $types)) { + return ''; + } + return (string) $types[$type]; + }); + + // Name. + $columns[] = (new column( + 'name', + new lang_string('name'), + $this->get_entity_name(), + )) + ->add_joins($this->get_joins()) + ->add_field("{$conversationalias}.name") + ->set_is_sortable(true) + ->add_callback(static function (?string $name): string { + if ($name === null) { + return ''; + } + return format_string($name); + }); + + // Enabled. + $columns[] = (new column( + 'enabled', + new lang_string('enabled', 'core_message'), + $this->get_entity_name(), + )) + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_BOOLEAN) + ->add_field("{$conversationalias}.enabled") + ->set_is_sortable(true) + ->add_callback([format::class, 'boolean_as_text']); + + // Time created. + $columns[] = (new column( + 'timecreated', + new lang_string('timecreated', 'core_reportbuilder'), + $this->get_entity_name(), + )) + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->add_field("{$conversationalias}.timecreated") + ->set_is_sortable(true) + ->add_callback([format::class, 'userdate']); + + return $columns; + } + + /** + * Return list of all available filters + * + * @return filter[] + */ + protected function get_all_filters(): array { + $conversationalias = $this->get_table_alias('message_conversations'); + + // Type. + $filters[] = (new filter( + select::class, + 'type', + new lang_string('conversationtype', 'core_message'), + $this->get_entity_name(), + "{$conversationalias}.type", + )) + ->add_joins($this->get_joins()) + ->set_options([ + api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL => new lang_string('individualconversations', 'core_message'), + api::MESSAGE_CONVERSATION_TYPE_GROUP => new lang_string('groupconversations', 'core_message'), + api::MESSAGE_CONVERSATION_TYPE_SELF => new lang_string('selfconversation', 'core_message'), + ]); + + // Name. + $filters[] = (new filter( + text::class, + 'name', + new lang_string('name'), + $this->get_entity_name(), + "{$conversationalias}.name", + )) + ->add_joins($this->get_joins()); + + // Enabled. + $filters[] = (new filter( + boolean_select::class, + 'enabled', + new lang_string('enabled', 'core_message'), + $this->get_entity_name(), + "{$conversationalias}.enabled", + )) + ->add_joins($this->get_joins()); + + // Time created. + $filters[] = (new filter( + date::class, + 'timecreated', + new lang_string('timecreated', 'core_reportbuilder'), + $this->get_entity_name(), + "{$conversationalias}.timecreated", + )) + ->add_joins($this->get_joins()); + + return $filters; + } +} diff --git a/public/message/classes/reportbuilder/local/entities/message.php b/public/message/classes/reportbuilder/local/entities/message.php new file mode 100644 index 00000000000..28dad806a4d --- /dev/null +++ b/public/message/classes/reportbuilder/local/entities/message.php @@ -0,0 +1,173 @@ +. + +declare(strict_types=1); + +namespace core_message\reportbuilder\local\entities; + +use core\lang_string; +use core_reportbuilder\local\entities\base; +use core_reportbuilder\local\filters\{date, text}; +use core_reportbuilder\local\helpers\format; +use core_reportbuilder\local\report\{column, filter}; +use stdClass; + +/** + * Message entity + * + * @package core_message + * @copyright 2025 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class message extends base { + /** + * Database tables that this entity uses + * + * @return string[] + */ + protected function get_default_tables(): array { + return [ + 'messages', + ]; + } + + /** + * The default title for this entity + * + * @return lang_string + */ + protected function get_default_entity_title(): lang_string { + return new lang_string('message', 'core_message'); + } + + /** + * Initialise the entity + * + * @return base + */ + public function initialise(): base { + $columns = $this->get_all_columns(); + foreach ($columns as $column) { + $this->add_column($column); + } + + // All the filters defined by the entity can also be used as conditions. + $filters = $this->get_all_filters(); + foreach ($filters as $filter) { + $this + ->add_filter($filter) + ->add_condition($filter); + } + + return $this; + } + + /** + * Returns list of all available columns + * + * @return column[] + */ + protected function get_all_columns(): array { + $messagealias = $this->get_table_alias('messages'); + + // Subject. + $columns[] = (new column( + 'subject', + new lang_string('subject'), + $this->get_entity_name(), + )) + ->add_joins($this->get_joins()) + ->add_field("{$messagealias}.subject") + ->set_is_sortable(true) + ->add_callback(static function (?string $subject): string { + if ($subject === null) { + return ''; + } + return format_string($subject); + }); + + // Message. + $columns[] = (new column( + 'message', + new lang_string('message', 'core_message'), + $this->get_entity_name(), + )) + ->add_joins($this->get_joins()) + ->add_fields("{$messagealias}.fullmessage, {$messagealias}.fullmessageformat, {$messagealias}.fullmessagetrust") + ->set_is_sortable(true) + ->add_callback(static function (?string $fullmessage, stdClass $message): string { + if ($fullmessage === null) { + return ''; + } + return format_text($fullmessage, $message->fullmessageformat, ['trusted' => $message->fullmessagetrust]); + }); + + // Time created. + $columns[] = (new column( + 'timecreated', + new lang_string('timecreated', 'core_reportbuilder'), + $this->get_entity_name(), + )) + ->add_joins($this->get_joins()) + ->set_type(column::TYPE_TIMESTAMP) + ->add_field("{$messagealias}.timecreated") + ->set_is_sortable(true) + ->add_callback([format::class, 'userdate']); + + return $columns; + } + + /** + * Return list of all available filters + * + * @return filter[] + */ + protected function get_all_filters(): array { + $messagealias = $this->get_table_alias('messages'); + + // Subject. + $filters[] = (new filter( + text::class, + 'subject', + new lang_string('subject'), + $this->get_entity_name(), + "{$messagealias}.subject", + )) + ->add_joins($this->get_joins()); + + // Message. + $filters[] = (new filter( + text::class, + 'message', + new lang_string('message', 'core_message'), + $this->get_entity_name(), + "{$messagealias}.fullmessage", + )) + ->add_joins($this->get_joins()); + + // Time created. + $filters[] = (new filter( + date::class, + 'timecreated', + new lang_string('timecreated', 'core_reportbuilder'), + $this->get_entity_name(), + "{$messagealias}.timecreated", + )) + ->add_joins($this->get_joins()); + + return $filters; + } +} diff --git a/public/message/tests/reportbuilder/datasource/messages_test.php b/public/message/tests/reportbuilder/datasource/messages_test.php new file mode 100644 index 00000000000..a5bbb1ea8cf --- /dev/null +++ b/public/message/tests/reportbuilder/datasource/messages_test.php @@ -0,0 +1,300 @@ +. + +declare(strict_types=1); + +namespace core_message\reportbuilder\datasource; + +use core\clock; +use core_message\api; +use core_message\tests\helper; +use core_reportbuilder_generator; +use core_reportbuilder\local\filters\{boolean_select, date, select, text}; +use core_reportbuilder\tests\core_reportbuilder_testcase; + +/** + * Unit tests for messages datasource + * + * @package core_message + * @covers \core_message\reportbuilder\datasource\messages + * @copyright 2025 Paul Holden + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +final class messages_test extends core_reportbuilder_testcase { + /** @var clock $clock */ + private readonly clock $clock; + + /** + * Mock the clock + */ + protected function setUp(): void { + parent::setUp(); + + $this->clock = $this->mock_clock_with_frozen(1622502000); + } + + /** + * Test default datasource + */ + public function test_datasource_default(): void { + $this->resetAfterTest(); + + // Test subject. + $course = $this->getDataGenerator()->create_course(); + $userone = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Zoe', 'lastname' => 'Zebra']); + $usertwo = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Morris', 'lastname' => 'Moo']); + $userthree = $this->getDataGenerator()->create_and_enrol($course, 'student', ['firstname' => 'Aaron', 'lastname' => 'Ant']); + + $groupconversation = api::create_conversation( + api::MESSAGE_CONVERSATION_TYPE_GROUP, + [$userone->id, $usertwo->id, $userthree->id], + ); + helper::send_fake_message_to_conversation($userone, $groupconversation->id, 'Are you somewhere feeling lonely?'); + + $this->clock->bump(HOURSECS); + + $privateconversation = api::create_conversation( + api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + [$userone->id, $usertwo->id], + ); + helper::send_fake_message_to_conversation($usertwo, $privateconversation->id, 'Or is someone loving you?'); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + $report = $generator->create_report(['name' => 'Messages', 'source' => messages::class, 'default' => 1]); + + $content = $this->get_custom_report_content($report->get('id')); + + // Default columns are author, recipient, message, time created. Sorted by author, recipient, time created. + $this->assertEquals([ + [ + fullname($usertwo), + fullname($userone), + '
Or is someone loving you?
', + 'Tuesday, 1 June 2021, 8:00 AM', + ], + [ + fullname($userone), + fullname($userthree), + '
Are you somewhere feeling lonely?
', + 'Tuesday, 1 June 2021, 7:00 AM', + ], + [ + fullname($userone), + fullname($usertwo), + '
Are you somewhere feeling lonely?
', + 'Tuesday, 1 June 2021, 7:00 AM', + ], + ], array_map('array_values', $content)); + } + + /** + * Test datasource columns that aren't added by default + */ + public function test_datasource_non_default_columns(): void { + $this->resetAfterTest(); + + // Test subject. + $userone = $this->getDataGenerator()->create_user(); + $usertwo = $this->getDataGenerator()->create_user(); + + $conversation = api::create_conversation( + api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + [$userone->id, $usertwo->id], + 'My conversation', + ); + helper::send_fake_message_to_conversation($userone, $conversation->id); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + $report = $generator->create_report(['name' => 'Messages', 'source' => messages::class, 'default' => 0]); + + // Message. + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'message:subject']); + + // Conversation. + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:type']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:name']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:enabled']); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'conversation:timecreated']); + + $content = $this->get_custom_report_content($report->get('id')); + + $this->assertEquals([ + [ + 'No subject', + 'Private', + 'My conversation', + 'Yes', + 'Tuesday, 1 June 2021, 7:00 AM', + ], + ], array_map('array_values', $content)); + } + + /** + * Data provider for {@see test_datasource_filters} + * + * @return array[] + */ + public static function datasource_filters_provider(): array { + return [ + // Message. + 'Message subject' => ['message:subject', [ + 'message:subject_operator' => text::IS_EQUAL_TO, + 'message:subject_value' => 'No subject', + ], true], + 'Message subject (no match)' => ['message:subject', [ + 'message:subject_operator' => text::IS_EQUAL_TO, + 'message:subject_value' => 'Another subject', + ], false], + 'Message content' => ['message:message', [ + 'message:message_operator' => text::IS_EQUAL_TO, + 'message:message_value' => 'Hi', + ], true], + 'Message content (no match)' => ['message:message', [ + 'message:message_operator' => text::IS_EQUAL_TO, + 'message:message_value' => 'Bye', + ], false], + 'Message time created' => ['message:timecreated', [ + 'message:timecreated_operator' => date::DATE_RANGE, + 'message:timecreated_from' => 1622502000, + ], true], + 'Message time created (no match)' => ['message:timecreated', [ + 'message:timecreated_operator' => date::DATE_RANGE, + 'message:timecreated_to' => 1622502000, + ], false], + + // Conversation. + 'Conversation type' => ['conversation:type', [ + 'conversation:type_operator' => select::EQUAL_TO, + 'conversation:type_value' => api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + ], true], + 'Conversation type (no match)' => ['conversation:type', [ + 'conversation:type_operator' => select::EQUAL_TO, + 'conversation:type_value' => api::MESSAGE_CONVERSATION_TYPE_GROUP, + ], false], + 'Conversation name' => ['conversation:name', [ + 'conversation:name_operator' => text::IS_EQUAL_TO, + 'conversation:name_value' => 'My conversation', + ], true], + 'Conversation name (no match)' => ['conversation:name', [ + 'conversation:name_operator' => text::IS_EQUAL_TO, + 'conversation:name_value' => 'Another conversation', + ], false], + 'Conversation enabled' => ['conversation:enabled', [ + 'conversation:enabled_operator' => boolean_select::CHECKED, + ], true], + 'Conversation enabled (no match)' => ['conversation:enabled', [ + 'conversation:enabled_operator' => boolean_select::NOT_CHECKED, + ], false], + 'Conversation time created' => ['conversation:timecreated', [ + 'conversation:timecreated_operator' => date::DATE_RANGE, + 'conversation:timecreated_from' => 1622502000, + ], true], + 'Conversation time created (no match)' => ['conversation:timecreated', [ + 'conversation:timecreated_operator' => date::DATE_RANGE, + 'conversation:timecreated_to' => 1622502000, + ], false], + + // Author. + 'User firstname' => ['user:firstname', [ + 'user:firstname_operator' => text::IS_EQUAL_TO, + 'user:firstname_value' => 'Zoe', + ], true], + 'User firstname (no match)' => ['user:firstname', [ + 'user:firstname_operator' => text::IS_EQUAL_TO, + 'user:firstname_value' => 'Aaron', + ], false], + + // Recipient. + 'Recipient firstname' => ['recipient:firstname', [ + 'recipient:firstname_operator' => text::IS_EQUAL_TO, + 'recipient:firstname_value' => 'Aaron', + ], true], + 'Recipient firstname (no match)' => ['recipient:firstname', [ + 'recipient:firstname_operator' => text::IS_EQUAL_TO, + 'recipient:firstname_value' => 'Zoe', + ], false], + ]; + } + + /** + * Test datasource filters + * + * @param string $filtername + * @param array $filtervalues + * @param bool $expectmatch + * + * @dataProvider datasource_filters_provider + */ + public function test_datasource_filters(string $filtername, array $filtervalues, bool $expectmatch): void { + $this->resetAfterTest(); + + // Test subject. + $userone = $this->getDataGenerator()->create_user(['firstname' => 'Zoe']); + $usertwo = $this->getDataGenerator()->create_user(['firstname' => 'Aaron']); + + $conversation = api::create_conversation( + api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, + [$userone->id, $usertwo->id], + 'My conversation', + ); + helper::send_fake_message_to_conversation($userone, $conversation->id, 'Hi'); + + /** @var core_reportbuilder_generator $generator */ + $generator = $this->getDataGenerator()->get_plugin_generator('core_reportbuilder'); + + // Create report containing single column, and given filter. + $report = $generator->create_report(['name' => 'Messages', 'source' => messages::class, 'default' => 0]); + $generator->create_column(['reportid' => $report->get('id'), 'uniqueidentifier' => 'message:subject']); + + // Add filter, set it's values. + $generator->create_filter(['reportid' => $report->get('id'), 'uniqueidentifier' => $filtername]); + $content = $this->get_custom_report_content($report->get('id'), 0, $filtervalues); + + if ($expectmatch) { + $this->assertEquals([ + ['No subject'], + ], array_map('array_values', $content)); + } else { + $this->assertEmpty($content); + } + } + + /** + * Stress test datasource + * + * In order to execute this test PHPUNIT_LONGTEST should be defined as true in phpunit.xml or directly in config.php + */ + public function test_stress_datasource(): void { + if (!PHPUNIT_LONGTEST) { + $this->markTestSkipped('PHPUNIT_LONGTEST is not defined'); + } + + $this->resetAfterTest(); + + // Test subject. + $userone = $this->getDataGenerator()->create_user(); + $usertwo = $this->getDataGenerator()->create_user(); + + $conversation = api::create_conversation(api::MESSAGE_CONVERSATION_TYPE_INDIVIDUAL, [$userone->id, $usertwo->id]); + helper::send_fake_message_to_conversation($userone, $conversation->id); + + $this->datasource_stress_test_columns(messages::class); + $this->datasource_stress_test_columns_aggregation(messages::class); + $this->datasource_stress_test_conditions(messages::class, 'message:subject'); + } +}