__  __    __   __  _____      _            _          _____ _          _ _ 
 |  \/  |   \ \ / / |  __ \    (_)          | |        / ____| |        | | |
 | \  / |_ __\ V /  | |__) | __ ___   ____ _| |_ ___  | (___ | |__   ___| | |
 | |\/| | '__|> <   |  ___/ '__| \ \ / / _` | __/ _ \  \___ \| '_ \ / _ \ | |
 | |  | | |_ / . \  | |   | |  | |\ V / (_| | ||  __/  ____) | | | |  __/ | |
 |_|  |_|_(_)_/ \_\ |_|   |_|  |_| \_/ \__,_|\__\___| |_____/|_| |_|\___V 2.1
 if you need WebShell for Seo everyday contact me on Telegram
 Telegram Address : @jackleet
        
        
For_More_Tools: Telegram: @jackleet | Bulk Smtp support mail sender | Business Mail Collector | Mail Bouncer All Mail | Bulk Office Mail Validator | Html Letter private



Upload:

Command:

www-data@216.73.216.10: ~ $
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

namespace mod_forum;

use mod_forum_tests_generator_trait;

defined('MOODLE_INTERNAL') || die();

global $CFG;
require_once(__DIR__ . '/generator_trait.php');
require_once("{$CFG->dirroot}/mod/forum/lib.php");

/**
 * The module forums tests
 *
 * @package    mod_forum
 * @copyright  2013 Frédéric Massart
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
final class subscriptions_test extends \advanced_testcase {
    // Include the mod_forum test helpers.
    // This includes functions to create forums, users, discussions, and posts.
    use mod_forum_tests_generator_trait;

    /**
     * Test setUp.
     */
    public function setUp(): void {
        global $DB;
        parent::setUp();

        // We must clear the subscription caches. This has to be done both before each test, and after in case of other
        // tests using these functions.
        \mod_forum\subscriptions::reset_forum_cache();
        \mod_forum\subscriptions::reset_discussion_cache();
    }

    /**
     * Test tearDown.
     */
    public function tearDown(): void {
        // We must clear the subscription caches. This has to be done both before each test, and after in case of other
        // tests using these functions.
        \mod_forum\subscriptions::reset_forum_cache();
        \mod_forum\subscriptions::reset_discussion_cache();
        parent::tearDown();
    }

    /**
     * Test subscription modes modifications.
     *
     * @covers \mod_forum\event\subscription_mode_updated
     */
    public function test_subscription_modes(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id);
        $forum = $this->getDataGenerator()->create_module('forum', $options);
        $context = \context_module::instance($forum->cmid);

        // Create a user enrolled in the course as a student.
        list($user) = $this->helper_create_users($course, 1);

        // Must be logged in as the current user.
        $this->setUser($user);

        $sink = $this->redirectEvents(); // Capturing the event.
        \mod_forum\subscriptions::set_subscription_mode($forum, FORUM_FORCESUBSCRIBE);
        $forum = $DB->get_record('forum', array('id' => $forum->id));
        $this->assertEquals(FORUM_FORCESUBSCRIBE, \mod_forum\subscriptions::get_subscription_mode($forum));
        $this->assertTrue(\mod_forum\subscriptions::is_forcesubscribed($forum));
        $this->assertFalse(\mod_forum\subscriptions::is_subscribable($forum));
        $this->assertFalse(\mod_forum\subscriptions::subscription_disabled($forum));

        $events = $sink->get_events();
        $this->assertCount(1, $events);
        $event = reset($events);
        $this->assertInstanceOf('\mod_forum\event\subscription_mode_updated', $event);
        $this->assertEquals($context, $event->get_context());
        $this->assertEventContextNotUsed($event);
        $this->assertNotEmpty($event->get_name());

        $sink = $this->redirectEvents(); // Capturing the event.
        \mod_forum\subscriptions::set_subscription_mode($forum, FORUM_DISALLOWSUBSCRIBE);
        $forum = $DB->get_record('forum', array('id' => $forum->id));
        $this->assertEquals(FORUM_DISALLOWSUBSCRIBE, \mod_forum\subscriptions::get_subscription_mode($forum));
        $this->assertTrue(\mod_forum\subscriptions::subscription_disabled($forum));
        $this->assertFalse(\mod_forum\subscriptions::is_subscribable($forum));
        $this->assertFalse(\mod_forum\subscriptions::is_forcesubscribed($forum));

        $events = $sink->get_events();
        $this->assertCount(1, $events);
        $event = reset($events);
        $this->assertInstanceOf('\mod_forum\event\subscription_mode_updated', $event);
        $this->assertEquals($context, $event->get_context());
        $this->assertEventContextNotUsed($event);
        $this->assertNotEmpty($event->get_name());

        $sink = $this->redirectEvents(); // Capturing the event.
        \mod_forum\subscriptions::set_subscription_mode($forum, FORUM_INITIALSUBSCRIBE);
        $forum = $DB->get_record('forum', array('id' => $forum->id));
        $this->assertEquals(FORUM_INITIALSUBSCRIBE, \mod_forum\subscriptions::get_subscription_mode($forum));
        $this->assertTrue(\mod_forum\subscriptions::is_subscribable($forum));
        $this->assertFalse(\mod_forum\subscriptions::subscription_disabled($forum));
        $this->assertFalse(\mod_forum\subscriptions::is_forcesubscribed($forum));

        $events = $sink->get_events();
        $this->assertCount(1, $events);
        $event = reset($events);
        $this->assertInstanceOf('\mod_forum\event\subscription_mode_updated', $event);
        $this->assertEquals($context, $event->get_context());
        $this->assertEventContextNotUsed($event);
        $this->assertNotEmpty($event->get_name());

        $sink = $this->redirectEvents(); // Capturing the event.
        \mod_forum\subscriptions::set_subscription_mode($forum, FORUM_CHOOSESUBSCRIBE);
        $forum = $DB->get_record('forum', array('id' => $forum->id));
        $this->assertEquals(FORUM_CHOOSESUBSCRIBE, \mod_forum\subscriptions::get_subscription_mode($forum));
        $this->assertTrue(\mod_forum\subscriptions::is_subscribable($forum));
        $this->assertFalse(\mod_forum\subscriptions::subscription_disabled($forum));
        $this->assertFalse(\mod_forum\subscriptions::is_forcesubscribed($forum));

        $events = $sink->get_events();
        $this->assertCount(1, $events);
        $event = reset($events);
        $this->assertInstanceOf('\mod_forum\event\subscription_mode_updated', $event);
        $this->assertEquals($context, $event->get_context());
        $this->assertEventContextNotUsed($event);
        $this->assertNotEmpty($event->get_name());
    }

    /**
     * Test fetching unsubscribable forums.
     */
    public function test_unsubscribable_forums(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        // Create a user enrolled in the course as a student.
        list($user) = $this->helper_create_users($course, 1);

        // Must be logged in as the current user.
        $this->setUser($user);

        // Without any subscriptions, there should be nothing returned.
        $result = \mod_forum\subscriptions::get_unsubscribable_forums();
        $this->assertEquals(0, count($result));

        // Create the forums.
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
        $forceforum = $this->getDataGenerator()->create_module('forum', $options);
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
        $disallowforum = $this->getDataGenerator()->create_module('forum', $options);
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $chooseforum = $this->getDataGenerator()->create_module('forum', $options);
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
        $initialforum = $this->getDataGenerator()->create_module('forum', $options);

        // At present the user is only subscribed to the initial forum.
        $result = \mod_forum\subscriptions::get_unsubscribable_forums();
        $this->assertEquals(1, count($result));

        // Ensure that the user is enrolled in all of the forums except force subscribed.
        \mod_forum\subscriptions::subscribe_user($user->id, $disallowforum);
        \mod_forum\subscriptions::subscribe_user($user->id, $chooseforum);

        $result = \mod_forum\subscriptions::get_unsubscribable_forums();
        $this->assertEquals(3, count($result));

        // Hide the forums.
        set_coursemodule_visible($forceforum->cmid, 0);
        set_coursemodule_visible($disallowforum->cmid, 0);
        set_coursemodule_visible($chooseforum->cmid, 0);
        set_coursemodule_visible($initialforum->cmid, 0);
        $result = \mod_forum\subscriptions::get_unsubscribable_forums();
        $this->assertEquals(0, count($result));

        // Add the moodle/course:viewhiddenactivities capability to the student user.
        $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
        $context = \context_course::instance($course->id);
        assign_capability('moodle/course:viewhiddenactivities', CAP_ALLOW, $roleids['student'], $context);

        // All of the unsubscribable forums should now be listed.
        $result = \mod_forum\subscriptions::get_unsubscribable_forums();
        $this->assertEquals(3, count($result));
    }

    /**
     * Test that toggling the forum-level subscription for a different user does not affect their discussion-level
     * subscriptions.
     */
    public function test_forum_subscribe_toggle_as_other(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create a user enrolled in the course as a student.
        list($author) = $this->helper_create_users($course, 1);

        // Post a discussion to the forum.
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);

        // Check that the user is currently not subscribed to the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // Check that the user is unsubscribed from the discussion too.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // Check that we have no records in either of the subscription tables.
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Subscribing to the forum should create a record in the subscriptions table, but not the forum discussion
        // subscriptions table.
        \mod_forum\subscriptions::subscribe_user($author->id, $forum);
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Unsubscribing should remove the record from the forum subscriptions table, and not modify the forum
        // discussion subscriptions table.
        \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Enroling the user in the discussion should add one record to the forum discussion table without modifying the
        // form subscriptions.
        \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Unsubscribing should remove the record from the forum subscriptions table, and not modify the forum
        // discussion subscriptions table.
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Re-subscribe to the discussion so that we can check the effect of forum-level subscriptions.
        \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Subscribing to the forum should have no effect on the forum discussion subscriptions table if the user did
        // not request the change themself.
        \mod_forum\subscriptions::subscribe_user($author->id, $forum);
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Unsubscribing from the forum should have no effect on the forum discussion subscriptions table if the user
        // did not request the change themself.
        \mod_forum\subscriptions::unsubscribe_user($author->id, $forum);
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Subscribing to the forum should remove the per-discussion subscription preference if the user requested the
        // change themself.
        \mod_forum\subscriptions::subscribe_user($author->id, $forum, null, true);
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Now unsubscribe from the current discussion whilst being subscribed to the forum as a whole.
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Unsubscribing from the forum should remove the per-discussion subscription preference if the user requested the
        // change themself.
        \mod_forum\subscriptions::unsubscribe_user($author->id, $forum, null, true);
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Subscribe to the discussion.
        \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Subscribe to the forum without removing the discussion preferences.
        \mod_forum\subscriptions::subscribe_user($author->id, $forum);
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Unsubscribing from the discussion should result in a change.
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

    }

    /**
     * Test that a user unsubscribed from a forum is not subscribed to it's discussions by default.
     */
    public function test_forum_discussion_subscription_forum_unsubscribed(): void {
        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create users enrolled in the course as students.
        list($author) = $this->helper_create_users($course, 1);

        // Check that the user is currently not subscribed to the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // Post a discussion to the forum.
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);

        // Check that the user is unsubscribed from the discussion too.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
    }

    /**
     * Test that the act of subscribing to a forum subscribes the user to it's discussions by default.
     */
    public function test_forum_discussion_subscription_forum_subscribed(): void {
        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create users enrolled in the course as students.
        list($author) = $this->helper_create_users($course, 1);

        // Enrol the user in the forum.
        // If a subscription was added, we get the record ID.
        $this->assertIsInt(\mod_forum\subscriptions::subscribe_user($author->id, $forum));

        // If we already have a subscription when subscribing the user, we get a boolean (true).
        $this->assertTrue(\mod_forum\subscriptions::subscribe_user($author->id, $forum));

        // Check that the user is currently subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // Post a discussion to the forum.
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);

        // Check that the user is subscribed to the discussion too.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
    }

    /**
     * Test that a user unsubscribed from a forum can be subscribed to a discussion.
     */
    public function test_forum_discussion_subscription_forum_unsubscribed_discussion_subscribed(): void {
        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create a user enrolled in the course as a student.
        list($author) = $this->helper_create_users($course, 1);

        // Check that the user is currently not subscribed to the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // Post a discussion to the forum.
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);

        // Attempting to unsubscribe from the discussion should not make a change.
        $this->assertFalse(\mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion));

        // Then subscribe them to the discussion.
        $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));

        // Check that the user is still unsubscribed from the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // But subscribed to the discussion.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
    }

    /**
     * Test that a user subscribed to a forum can be unsubscribed from a discussion.
     */
    public function test_forum_discussion_subscription_forum_subscribed_discussion_unsubscribed(): void {
        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create two users enrolled in the course as students.
        list($author) = $this->helper_create_users($course, 2);

        // Enrol the student in the forum.
        \mod_forum\subscriptions::subscribe_user($author->id, $forum);

        // Check that the user is currently subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // Post a discussion to the forum.
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);

        // Then unsubscribe them from the discussion.
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);

        // Check that the user is still subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // But unsubscribed from the discussion.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));
    }

    /**
     * Test the effect of toggling the discussion subscription status when subscribed to the forum.
     */
    public function test_forum_discussion_toggle_forum_subscribed(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create two users enrolled in the course as students.
        list($author) = $this->helper_create_users($course, 2);

        // Enrol the student in the forum.
        \mod_forum\subscriptions::subscribe_user($author->id, $forum);

        // Check that the user is currently subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // Post a discussion to the forum.
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);

        // Check that the user is initially subscribed to that discussion.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // An attempt to subscribe again should result in a falsey return to indicate that no change was made.
        $this->assertFalse(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));

        // And there should be no discussion subscriptions (and one forum subscription).
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));

        // Then unsubscribe them from the discussion.
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);

        // Check that the user is still subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // An attempt to unsubscribe again should result in a falsey return to indicate that no change was made.
        $this->assertFalse(\mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion));

        // And there should be a discussion subscriptions (and one forum subscription).
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));

        // But unsubscribed from the discussion.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // There should be a record in the discussion subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // And one in the forum subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));

        // Now subscribe the user again to the discussion.
        \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);

        // Check that the user is still subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // And is subscribed to the discussion again.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // There should be no record in the discussion subscription tracking table.
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // And one in the forum subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));

        // And unsubscribe again.
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);

        // Check that the user is still subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // But unsubscribed from the discussion.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // There should be a record in the discussion subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // And one in the forum subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));

        // And subscribe the user again to the discussion.
        \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);

        // Check that the user is still subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // And is subscribed to the discussion again.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // There should be no record in the discussion subscription tracking table.
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // And one in the forum subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));

        // And unsubscribe again.
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);

        // Check that the user is still subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // But unsubscribed from the discussion.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // There should be a record in the discussion subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // And one in the forum subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));

        // Now unsubscribe the user from the forum.
        $this->assertTrue(\mod_forum\subscriptions::unsubscribe_user($author->id, $forum, null, true));

        // This removes both the forum_subscriptions, and the forum_discussion_subs records.
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $author->id,
            'forum'         => $forum->id,
        )));

        // And should have reset the discussion cache value.
        $result = \mod_forum\subscriptions::fetch_discussion_subscription($forum->id, $author->id);
        $this->assertIsArray($result);
        $this->assertFalse(isset($result[$discussion->id]));
    }

    /**
     * Test the effect of toggling the discussion subscription status when unsubscribed from the forum.
     */
    public function test_forum_discussion_toggle_forum_unsubscribed(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create two users enrolled in the course as students.
        list($author) = $this->helper_create_users($course, 2);

        // Check that the user is currently unsubscribed to the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // Post a discussion to the forum.
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);

        // Check that the user is initially unsubscribed to that discussion.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // Then subscribe them to the discussion.
        $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));

        // An attempt to subscribe again should result in a falsey return to indicate that no change was made.
        $this->assertFalse(\mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion));

        // Check that the user is still unsubscribed from the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // But subscribed to the discussion.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // There should be a record in the discussion subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // Now unsubscribe the user again from the discussion.
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);

        // Check that the user is still unsubscribed from the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // And is unsubscribed from the discussion again.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // There should be no record in the discussion subscription tracking table.
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // And subscribe the user again to the discussion.
        \mod_forum\subscriptions::subscribe_user_to_discussion($author->id, $discussion);

        // Check that the user is still unsubscribed from the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // And is subscribed to the discussion again.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // There should be a record in the discussion subscription tracking table.
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));

        // And unsubscribe again.
        \mod_forum\subscriptions::unsubscribe_user_from_discussion($author->id, $discussion);

        // Check that the user is still unsubscribed from the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum));

        // But unsubscribed from the discussion.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($author->id, $forum, $discussion->id));

        // There should be no record in the discussion subscription tracking table.
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $author->id,
            'discussion'    => $discussion->id,
        )));
    }

    /**
     * Test that the correct users are returned when fetching subscribed users from a forum where users can choose to
     * subscribe and unsubscribe.
     */
    public function test_fetch_subscribed_users_subscriptions(): void {
        global $DB, $CFG;

        $this->resetAfterTest(true);

        // Create a course, with a forum. where users are initially subscribed.
        $course = $this->getDataGenerator()->create_course();
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create some user enrolled in the course as a student.
        $usercount = 5;
        $users = $this->helper_create_users($course, $usercount);

        // All users should be subscribed.
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
        $this->assertEquals($usercount, count($subscribers));

        // Subscribe the guest user too to the forum - they should never be returned by this function.
        $this->getDataGenerator()->enrol_user($CFG->siteguest, $course->id);
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
        $this->assertEquals($usercount, count($subscribers));

        // Unsubscribe 2 users.
        $unsubscribedcount = 2;
        for ($i = 0; $i < $unsubscribedcount; $i++) {
            \mod_forum\subscriptions::unsubscribe_user($users[$i]->id, $forum);
        }

        // The subscription count should now take into account those users who have been unsubscribed.
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
        $this->assertEquals($usercount - $unsubscribedcount, count($subscribers));
    }

    /**
     * Test that the correct users are returned hwen fetching subscribed users from a forum where users are forcibly
     * subscribed.
     */
    public function test_fetch_subscribed_users_forced(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum. where users are initially subscribed.
        $course = $this->getDataGenerator()->create_course();
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create some user enrolled in the course as a student.
        $usercount = 5;
        $users = $this->helper_create_users($course, $usercount);

        // All users should be subscribed.
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
        $this->assertEquals($usercount, count($subscribers));
    }

    /**
     * Test that unusual combinations of discussion subscriptions do not affect the subscribed user list.
     */
    public function test_fetch_subscribed_users_discussion_subscriptions(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum. where users are initially subscribed.
        $course = $this->getDataGenerator()->create_course();
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create some user enrolled in the course as a student.
        $usercount = 5;
        $users = $this->helper_create_users($course, $usercount);

        list($discussion, $post) = $this->helper_post_to_forum($forum, $users[0]);

        // All users should be subscribed.
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
        $this->assertEquals($usercount, count($subscribers));
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
        $this->assertEquals($usercount, count($subscribers));

        \mod_forum\subscriptions::unsubscribe_user_from_discussion($users[0]->id, $discussion);

        // All users should be subscribed.
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
        $this->assertEquals($usercount, count($subscribers));

        // All users should be subscribed.
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
        $this->assertEquals($usercount, count($subscribers));

        // Manually insert an extra subscription for one of the users.
        $record = new \stdClass();
        $record->userid = $users[2]->id;
        $record->forum = $forum->id;
        $record->discussion = $discussion->id;
        $record->preference = time();
        $DB->insert_record('forum_discussion_subs', $record);

        // The discussion count should not have changed.
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
        $this->assertEquals($usercount, count($subscribers));
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
        $this->assertEquals($usercount, count($subscribers));

        // Unsubscribe 2 users.
        $unsubscribedcount = 2;
        for ($i = 0; $i < $unsubscribedcount; $i++) {
            \mod_forum\subscriptions::unsubscribe_user($users[$i]->id, $forum);
        }

        // The subscription count should now take into account those users who have been unsubscribed.
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
        $this->assertEquals($usercount - $unsubscribedcount, count($subscribers));
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
        $this->assertEquals($usercount - $unsubscribedcount, count($subscribers));

        // Now subscribe one of those users back to the discussion.
        $subscribeddiscussionusers = 1;
        for ($i = 0; $i < $subscribeddiscussionusers; $i++) {
            \mod_forum\subscriptions::subscribe_user_to_discussion($users[$i]->id, $discussion);
        }
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum);
        $this->assertEquals($usercount - $unsubscribedcount, count($subscribers));
        $subscribers = \mod_forum\subscriptions::fetch_subscribed_users($forum, 0, null, null, true);
        $this->assertEquals($usercount - $unsubscribedcount + $subscribeddiscussionusers, count($subscribers));
    }

    /**
     * Test whether a user is force-subscribed to a forum.
     */
    public function test_force_subscribed_to_forum(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create a user enrolled in the course as a student.
        $roleids = $DB->get_records_menu('role', null, '', 'shortname, id');
        $user = $this->getDataGenerator()->create_user();
        $this->getDataGenerator()->enrol_user($user->id, $course->id, $roleids['student']);

        // Check that the user is currently subscribed to the forum.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));

        // Remove the allowforcesubscribe capability from the user.
        $cm = get_coursemodule_from_instance('forum', $forum->id);
        $context = \context_module::instance($cm->id);
        assign_capability('mod/forum:allowforcesubscribe', CAP_PROHIBIT, $roleids['student'], $context);
        $this->assertFalse(has_capability('mod/forum:allowforcesubscribe', $context, $user->id));

        // Check that the user is no longer subscribed to the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
    }

    /**
     * Test that the subscription cache can be pre-filled.
     */
    public function test_subscription_cache_prefill(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create some users.
        $users = $this->helper_create_users($course, 20);

        // Reset the subscription cache.
        \mod_forum\subscriptions::reset_forum_cache();

        // Filling the subscription cache should use a query.
        $startcount = $DB->perf_get_reads();
        $this->assertNull(\mod_forum\subscriptions::fill_subscription_cache($forum->id));
        $postfillcount = $DB->perf_get_reads();
        $this->assertNotEquals($postfillcount, $startcount);

        // Now fetch some subscriptions from that forum - these should use
        // the cache and not perform additional queries.
        foreach ($users as $user) {
            $this->assertTrue(\mod_forum\subscriptions::fetch_subscription_cache($forum->id, $user->id));
        }
        $finalcount = $DB->perf_get_reads();
        $this->assertEquals(0, $finalcount - $postfillcount);
    }

    /**
     * Test that the subscription cache can filled user-at-a-time.
     */
    public function test_subscription_cache_fill(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create some users.
        $users = $this->helper_create_users($course, 20);

        // Reset the subscription cache.
        \mod_forum\subscriptions::reset_forum_cache();

        // Filling the subscription cache should only use a single query.
        $startcount = $DB->perf_get_reads();

        // Fetch some subscriptions from that forum - these should not use the cache and will perform additional queries.
        foreach ($users as $user) {
            $this->assertTrue(\mod_forum\subscriptions::fetch_subscription_cache($forum->id, $user->id));
        }
        $finalcount = $DB->perf_get_reads();
        $this->assertEquals(20, $finalcount - $startcount);
    }

    /**
     * Test that the discussion subscription cache can filled course-at-a-time.
     */
    public function test_discussion_subscription_cache_fill_for_course(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        // Create the forums.
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE);
        $disallowforum = $this->getDataGenerator()->create_module('forum', $options);
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $chooseforum = $this->getDataGenerator()->create_module('forum', $options);
        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
        $initialforum = $this->getDataGenerator()->create_module('forum', $options);

        // Create some users and keep a reference to the first user.
        $users = $this->helper_create_users($course, 20);
        $user = reset($users);

        // Reset the subscription caches.
        \mod_forum\subscriptions::reset_forum_cache();

        $startcount = $DB->perf_get_reads();
        $result = \mod_forum\subscriptions::fill_subscription_cache_for_course($course->id, $user->id);
        $this->assertNull($result);
        $postfillcount = $DB->perf_get_reads();
        $this->assertNotEquals($postfillcount, $startcount);
        $this->assertFalse(\mod_forum\subscriptions::fetch_subscription_cache($disallowforum->id, $user->id));
        $this->assertFalse(\mod_forum\subscriptions::fetch_subscription_cache($chooseforum->id, $user->id));
        $this->assertTrue(\mod_forum\subscriptions::fetch_subscription_cache($initialforum->id, $user->id));
        $finalcount = $DB->perf_get_reads();
        $this->assertEquals(0, $finalcount - $postfillcount);

        // Test for all users.
        foreach ($users as $user) {
            $result = \mod_forum\subscriptions::fill_subscription_cache_for_course($course->id, $user->id);
            $this->assertFalse(\mod_forum\subscriptions::fetch_subscription_cache($disallowforum->id, $user->id));
            $this->assertFalse(\mod_forum\subscriptions::fetch_subscription_cache($chooseforum->id, $user->id));
            $this->assertTrue(\mod_forum\subscriptions::fetch_subscription_cache($initialforum->id, $user->id));
        }
        $finalcount = $DB->perf_get_reads();
        $this->assertNotEquals($finalcount, $postfillcount);
    }

    /**
     * Test that the discussion subscription cache can be forcibly updated for a user.
     */
    public function test_discussion_subscription_cache_prefill(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create some users.
        $users = $this->helper_create_users($course, 20);

        // Post some discussions to the forum.
        $discussions = array();
        $author = $users[0];
        $userwithnosubs = $users[1];

        for ($i = 0; $i < 20; $i++) {
            list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
            $discussions[] = $discussion;
        }

        // Unsubscribe half the users from the half the discussions.
        $forumcount = 0;
        $usercount = 0;
        $userwithsubs = null;
        foreach ($discussions as $data) {
            // Unsubscribe user from all discussions.
            \mod_forum\subscriptions::unsubscribe_user_from_discussion($userwithnosubs->id, $data);

            if ($forumcount % 2) {
                continue;
            }
            foreach ($users as $user) {
                if ($usercount % 2) {
                    $userwithsubs = $user;
                    continue;
                }
                \mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $data);
                $usercount++;
            }
            $forumcount++;
        }

        // Reset the subscription caches.
        \mod_forum\subscriptions::reset_forum_cache();
        \mod_forum\subscriptions::reset_discussion_cache();

        // A user with no subscriptions should only be fetched once.
        $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id, $userwithnosubs->id));
        $startcount = $DB->perf_get_reads();
        $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id, $userwithnosubs->id));
        $this->assertEquals($startcount, $DB->perf_get_reads());

        // Confirm subsequent calls properly tries to fetch subs.
        $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id, $userwithsubs->id));
        $this->assertNotEquals($startcount, $DB->perf_get_reads());

        // Another read should be performed to get all subscriptions for the forum.
        $startcount = $DB->perf_get_reads();
        $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id));
        $this->assertNotEquals($startcount, $DB->perf_get_reads());

        // Reset the subscription caches.
        \mod_forum\subscriptions::reset_forum_cache();
        \mod_forum\subscriptions::reset_discussion_cache();

        // Filling the discussion subscription cache should only use a single query.
        $startcount = $DB->perf_get_reads();
        $this->assertNull(\mod_forum\subscriptions::fill_discussion_subscription_cache($forum->id));
        $postfillcount = $DB->perf_get_reads();
        $this->assertNotEquals($postfillcount, $startcount);

        // Now fetch some subscriptions from that forum - these should use
        // the cache and not perform additional queries.
        foreach ($users as $user) {
            $result = \mod_forum\subscriptions::fetch_discussion_subscription($forum->id, $user->id);
            $this->assertIsArray($result);
        }
        $finalcount = $DB->perf_get_reads();
        $this->assertEquals(0, $finalcount - $postfillcount);
    }

    /**
     * Test that the discussion subscription cache can filled user-at-a-time.
     */
    public function test_discussion_subscription_cache_fill(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_INITIALSUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create some users.
        $users = $this->helper_create_users($course, 20);

        // Post some discussions to the forum.
        $discussions = array();
        $author = $users[0];
        for ($i = 0; $i < 20; $i++) {
            list($discussion, $post) = $this->helper_post_to_forum($forum, $author);
            $discussions[] = $discussion;
        }

        // Unsubscribe half the users from the half the discussions.
        $forumcount = 0;
        $usercount = 0;
        foreach ($discussions as $data) {
            if ($forumcount % 2) {
                continue;
            }
            foreach ($users as $user) {
                if ($usercount % 2) {
                    continue;
                }
                \mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $discussion);
                $usercount++;
            }
            $forumcount++;
        }

        // Reset the subscription caches.
        \mod_forum\subscriptions::reset_forum_cache();
        \mod_forum\subscriptions::reset_discussion_cache();

        $startcount = $DB->perf_get_reads();

        // Now fetch some subscriptions from that forum - these should use
        // the cache and not perform additional queries.
        foreach ($users as $user) {
            $result = \mod_forum\subscriptions::fetch_discussion_subscription($forum->id, $user->id);
            $this->assertIsArray($result);
        }
        $finalcount = $DB->perf_get_reads();
        $this->assertNotEquals($finalcount, $startcount);
    }

    /**
     * Test that after toggling the forum subscription as another user,
     * the discussion subscription functionality works as expected.
     */
    public function test_forum_subscribe_toggle_as_other_repeat_subscriptions(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_CHOOSESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create a user enrolled in the course as a student.
        list($user) = $this->helper_create_users($course, 1);

        // Post a discussion to the forum.
        list($discussion, $post) = $this->helper_post_to_forum($forum, $user);

        // Confirm that the user is currently not subscribed to the forum.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum));

        // Confirm that the user is unsubscribed from the discussion too.
        $this->assertFalse(\mod_forum\subscriptions::is_subscribed($user->id, $forum, $discussion->id));

        // Confirm that we have no records in either of the subscription tables.
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $user->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $user->id,
            'discussion'    => $discussion->id,
        )));

        // Subscribing to the forum should create a record in the subscriptions table, but not the forum discussion
        // subscriptions table.
        \mod_forum\subscriptions::subscribe_user($user->id, $forum);
        $this->assertEquals(1, $DB->count_records('forum_subscriptions', array(
            'userid'        => $user->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(0, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $user->id,
            'discussion'    => $discussion->id,
        )));

        // Now unsubscribe from the discussion. This should return true.
        $this->assertTrue(\mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $discussion));

        // Attempting to unsubscribe again should return false because no change was made.
        $this->assertFalse(\mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $discussion));

        // Subscribing to the discussion again should return truthfully as the subscription preference was removed.
        $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($user->id, $discussion));

        // Attempting to subscribe again should return false because no change was made.
        $this->assertFalse(\mod_forum\subscriptions::subscribe_user_to_discussion($user->id, $discussion));

        // Now unsubscribe from the discussion. This should return true once more.
        $this->assertTrue(\mod_forum\subscriptions::unsubscribe_user_from_discussion($user->id, $discussion));

        // And unsubscribing from the forum but not as a request from the user should maintain their preference.
        \mod_forum\subscriptions::unsubscribe_user($user->id, $forum);

        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $user->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $user->id,
            'discussion'    => $discussion->id,
        )));

        // Subscribing to the discussion should return truthfully because a change was made.
        $this->assertTrue(\mod_forum\subscriptions::subscribe_user_to_discussion($user->id, $discussion));
        $this->assertEquals(0, $DB->count_records('forum_subscriptions', array(
            'userid'        => $user->id,
            'forum'         => $forum->id,
        )));
        $this->assertEquals(1, $DB->count_records('forum_discussion_subs', array(
            'userid'        => $user->id,
            'discussion'    => $discussion->id,
        )));
    }

    /**
     * Test that providing a context_module instance to is_subscribed does not result in additional lookups to retrieve
     * the context_module.
     */
    public function test_is_subscribed_cm(): void {
        global $DB;

        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();

        $options = array('course' => $course->id, 'forcesubscribe' => FORUM_FORCESUBSCRIBE);
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        // Create a user enrolled in the course as a student.
        list($user) = $this->helper_create_users($course, 1);

        // Retrieve the $cm now.
        $cm = get_fast_modinfo($forum->course)->instances['forum'][$forum->id];

        // Reset get_fast_modinfo.
        get_fast_modinfo(0, 0, true);

        // Call is_subscribed without passing the $cmid - this should result in a lookup and filling of some of the
        // caches. This provides us with consistent data to start from.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));

        // Make a note of the number of DB calls.
        $basecount = $DB->perf_get_reads();

        // Call is_subscribed - it should give return the correct result (False), and result in no additional queries.
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum, null, $cm));

        // The capability check does require some queries, so we don't test it directly.
        // We don't assert here because this is dependant upon linked code which could change at any time.
        $suppliedcmcount = $DB->perf_get_reads() - $basecount;

        // Call is_subscribed without passing the $cmid now - this should result in a lookup.
        get_fast_modinfo(0, 0, true);
        $basecount = $DB->perf_get_reads();
        $this->assertTrue(\mod_forum\subscriptions::is_subscribed($user->id, $forum));
        $calculatedcmcount = $DB->perf_get_reads() - $basecount;

        // There should be more queries than when we performed the same check a moment ago.
        $this->assertGreaterThan($suppliedcmcount, $calculatedcmcount);
    }

    public static function is_subscribable_forums(): array {
        return [
            [
                'forcesubscribe' => FORUM_DISALLOWSUBSCRIBE,
            ],
            [
                'forcesubscribe' => FORUM_CHOOSESUBSCRIBE,
            ],
            [
                'forcesubscribe' => FORUM_INITIALSUBSCRIBE,
            ],
            [
                'forcesubscribe' => FORUM_FORCESUBSCRIBE,
            ],
        ];
    }

    public static function is_subscribable_provider(): array {
        $data = [];
        foreach (self::is_subscribable_forums() as $forum) {
            $data[] = [$forum];
        }

        return $data;
    }

    /**
     * @dataProvider is_subscribable_provider
     */
    public function test_is_subscribable_logged_out($options): void {
        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();
        $options['course'] = $course->id;
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        $this->assertFalse(\mod_forum\subscriptions::is_subscribable($forum));
    }

    /**
     * @dataProvider is_subscribable_provider
     */
    public function test_is_subscribable_is_guest($options): void {
        global $DB;
        $this->resetAfterTest(true);

        $guest = $DB->get_record('user', array('username'=>'guest'));
        $this->setUser($guest);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();
        $options['course'] = $course->id;
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        $this->assertFalse(\mod_forum\subscriptions::is_subscribable($forum));
    }

    public static function is_subscribable_loggedin_provider(): array {
        return [
            [
                ['forcesubscribe' => FORUM_DISALLOWSUBSCRIBE],
                false,
            ],
            [
                ['forcesubscribe' => FORUM_CHOOSESUBSCRIBE],
                true,
            ],
            [
                ['forcesubscribe' => FORUM_INITIALSUBSCRIBE],
                true,
            ],
            [
                ['forcesubscribe' => FORUM_FORCESUBSCRIBE],
                false,
            ],
        ];
    }

    /**
     * @dataProvider is_subscribable_loggedin_provider
     */
    public function test_is_subscribable_loggedin($options, $expect): void {
        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();
        $options['course'] = $course->id;
        $forum = $this->getDataGenerator()->create_module('forum', $options);

        $user = $this->getDataGenerator()->create_user();
        $this->getDataGenerator()->enrol_user($user->id, $course->id);
        $this->setUser($user);

        $this->assertEquals($expect, \mod_forum\subscriptions::is_subscribable($forum));
    }

    public function test_get_user_default_subscription(): void {
        global $DB;
        $this->resetAfterTest(true);

        // Create a course, with a forum.
        $course = $this->getDataGenerator()->create_course();
        $context = \context_course::instance($course->id);
        $options['course'] = $course->id;
        $forum = $this->getDataGenerator()->create_module('forum', $options);
        $cm = get_coursemodule_from_instance("forum", $forum->id, $course->id);

        // Create a user enrolled in the course as a student.
        list($author, $student) = $this->helper_create_users($course, 2, 'student');
        // Post a discussion to the forum.
        list($discussion, $post) = $this->helper_post_to_forum($forum, $author);

        // A guest user.
        $this->setUser(0);
        $this->assertFalse((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
        $this->assertFalse((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));

        // A user enrolled in the course.
        $this->setUser($author->id);
        $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
        $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));

        // Subscribption disabled.
        $this->setUser($student->id);
        \mod_forum\subscriptions::set_subscription_mode($forum, FORUM_DISALLOWSUBSCRIBE);
        $forum = $DB->get_record('forum', array('id' => $forum->id));
        $this->assertFalse((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
        $this->assertFalse((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));

        \mod_forum\subscriptions::set_subscription_mode($forum, FORUM_FORCESUBSCRIBE);
        $forum = $DB->get_record('forum', array('id' => $forum->id));
        $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
        $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));

        // Admin user.
        $this->setAdminUser();
        $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, $discussion->id));
        $this->assertTrue((boolean)\mod_forum\subscriptions::get_user_default_subscription($forum, $context, $cm, null));
    }
}

Filemanager

Name Type Size Permission Actions
backup Folder 0777
behat Folder 0777
event Folder 0777
generator Folder 0777
grade Folder 0777
h5p Folder 0777
privacy Folder 0777
search Folder 0777
backup_forum_activity_task_test.php File 5.12 KB 0777
builders_exported_posts_test.php File 21.87 KB 0777
cron_trait.php File 5.11 KB 0777
custom_completion_test.php File 10.04 KB 0777
dates_test.php File 3.01 KB 0777
entities_author_test.php File 2.24 KB 0777
entities_discussion_summary_test.php File 2.85 KB 0777
entities_discussion_test.php File 5.97 KB 0777
entities_forum_test.php File 6.43 KB 0777
entities_post_read_receipt_collection_test.php File 2.6 KB 0777
entities_post_test.php File 2.81 KB 0777
entities_sorter_test.php File 2.55 KB 0777
exporters_author_test.php File 5.54 KB 0777
exporters_discussion_test.php File 4.28 KB 0777
exporters_forum_test.php File 3.01 KB 0777
exporters_post_test.php File 22.28 KB 0777
externallib_test.php File 153.5 KB 0777
generator_test.php File 11.38 KB 0777
generator_trait.php File 6.1 KB 0777
lib_test.php File 185.86 KB 0777
local_container_test.php File 3.41 KB 0777
locallib_test.php File 2.36 KB 0777
mail_group_test.php File 8.64 KB 0777
mail_test.php File 63.22 KB 0777
maildigest_test.php File 30.88 KB 0777
managers_capability_test.php File 56.41 KB 0777
output_email_test.php File 6.88 KB 0777
portfolio_caller_test.php File 5.17 KB 0777
private_replies_test.php File 9.76 KB 0777
qanda_test.php File 5.27 KB 0777
rsslib_test.php File 3.42 KB 0777
subscriptions_test.php File 66.47 KB 0777
vaults_author_test.php File 2.66 KB 0777
vaults_discussion_list_test.php File 29.94 KB 0777
vaults_discussion_test.php File 4.79 KB 0777
vaults_forum_test.php File 5.73 KB 0777
vaults_post_attachment_test.php File 7.55 KB 0777
vaults_post_read_receipt_collection_test.php File 3.72 KB 0777
vaults_post_test.php File 53.89 KB 0777
Filemanager