Proyectos de Subversion Moodle

Rev

Autoría | Ultima modificación | Ver Log |

<?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 tool_courserating;

use core\event\base;
use tool_courserating\event\flag_created;
use tool_courserating\event\flag_deleted;
use tool_courserating\event\rating_created;
use tool_courserating\event\rating_deleted;
use tool_courserating\event\rating_updated;
use tool_courserating\local\models\rating;
use tool_courserating\local\models\summary;

/**
 * Tests for api class
 *
 * @package     tool_courserating
 * @covers      \tool_courserating\api
 * @copyright   2022 Marina Glancy <marina.glancy@gmail.com>
 * @license     https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 */
final class api_test extends \advanced_testcase {

    /**
     * Set up
     */
    public function setUp(): void {
        $this->resetAfterTest();
        set_config(\tool_courserating\constants::SETTING_RATINGMODE,
            \tool_courserating\constants::RATEBY_ANYTIME, 'tool_courserating');
    }

    /**
     * Generator
     *
     * @return \tool_courserating_generator
     */
    protected function get_generator(): \tool_courserating_generator {
        /** @var \tool_courserating_generator $generator */
        $generator = self::getDataGenerator()->get_plugin_generator('tool_courserating');
        return $generator;
    }

    /**
     * Assert rating in the database matches expectations
     *
     * @param array|null $expected
     * @param int $userid
     * @param int $courseid
     */
    protected function assert_rating(?array $expected, int $userid, int $courseid) {
        global $DB;

        $params = ['courseid' => $courseid, 'userid' => $userid];
        if (empty($expected)) {
            $this->assertEmpty($DB->get_records(rating::TABLE, $params));
        } else {
            $fields = join(', ', array_keys($expected));
            $this->assertEquals($expected,
                (array)$DB->get_record(rating::TABLE, $params, $fields, MUST_EXIST));
        }
    }

    /**
     * Assert summary in the database matches expectations
     *
     * @param array $expected
     * @param int $courseid
     */
    protected function assert_summary(array $expected, int $courseid) {
        global $DB;

        $cfield = $DB->get_field_sql('SELECT d.value FROM {customfield_field} f
            JOIN {customfield_data} d ON d.fieldid = f.id
            JOIN {customfield_category} c ON f.categoryid = c.id
            WHERE f.shortname = ? AND d.instanceid = ?
                  AND c.component = ? AND c.area = ?',
            [constants::CFIELD_RATING, $courseid, 'core_course', 'course']);

        $params = ['courseid' => $courseid];
        if (empty($expected)) {
            $this->assertEmpty($DB->get_records(summary::TABLE, $params));
            $this->assertEmpty($cfield);
        } else {
            $record = (array)$DB->get_record(summary::TABLE, $params, '*', MUST_EXIST);
            $this->assertEquals($expected, array_intersect_key($record, $expected));
        }

        if ($record['cntall'] ?? 0) {
            $this->assertStringContainsString("({$record['cntall']})", strip_tags($cfield));
        } else {
            $this->assertEmpty($cfield);
        }
    }

    /**
     * Assert contents of an event
     *
     * @param \phpunit_event_sink $sink
     * @param string $classname
     * @param int $courseid
     * @param string $namematch
     * @param string $descriptionmatch
     * @return void
     */
    protected function assert_event(\phpunit_event_sink $sink, string $classname, int $courseid,
                                    string $namematch, string $descriptionmatch) {
        $events = $sink->get_events();
        $this->assertEquals(1, count($events));
        /** @var base $event */
        $event = reset($events);
        $this->assertEquals($classname, ltrim($event->eventname, '\\'));
        $this->assertEquals($courseid, $event->courseid);
        $this->assertEquals(\context_course::instance($courseid)->id, $event->contextid);
        $this->assertStringMatchesFormat($namematch, $event->get_name());
        $this->assertStringMatchesFormat($descriptionmatch, $event->get_description());

        $sink->clear();
    }

    public function test_set_rating(): void {
        $user = $this->getDataGenerator()->create_user();
        $user2 = $this->getDataGenerator()->create_user();
        $course = $this->getDataGenerator()->create_course();

        $this->assert_rating([], $user->id, $course->id);
        $this->assert_rating([], $user2->id, $course->id);
        $this->assert_summary(['cntall' => 0], $course->id);

        // Set rating as the first user.
        $this->setUser($user);
        $sink = $this->redirectEvents();
        api::set_rating($course->id, (object)['rating' => 4]);
        $this->assert_event($sink, rating_created::class, $course->id, 'Course rating created',
            '%ahas rated the course with 4 stars');

        $this->assert_rating(['rating' => 4, 'review' => ''], $user->id, $course->id);
        $expected = ['cntall' => 1, 'avgrating' => 4, 'sumrating' => 4, 'cnt02' => 0, 'cnt03' => 0, 'cnt04' => 1];
        $this->assert_summary($expected, $course->id);

        // Set rating as the second user.
        $this->setUser($user2);
        api::set_rating($course->id, (object)['rating' => 2]);

        $this->assert_rating(['rating' => 4, 'review' => ''], $user->id, $course->id);
        $this->assert_rating(['rating' => 2, 'review' => ''], $user2->id, $course->id);
        $expected = ['cntall' => 2, 'avgrating' => 3, 'sumrating' => 6, 'cnt02' => 1, 'cnt03' => 0, 'cnt04' => 1];
        $this->assert_summary($expected, $course->id);

        // Change rating as the first user.
        $this->setUser($user);
        $sink->clear();
        api::set_rating($course->id, (object)['rating' => 3, 'review' => 'hello']);
        $this->assert_event($sink, rating_updated::class, $course->id, 'Course rating updated',
            'User %a has changed the rating for the course from 4 to 3');

        $this->assert_rating(['rating' => 3, 'review' => 'hello', 'hasreview' => 1], $user->id, $course->id);
        $this->assert_rating(['rating' => 2, 'review' => '', 'hasreview' => 0], $user2->id, $course->id);
        $expected = ['cntall' => 2, 'avgrating' => 2.5, 'sumrating' => 5, 'cnt02' => 1, 'cnt03' => 1, 'cnt04' => 0,
            'cntreviews' => 1, ];
        $this->assert_summary($expected, $course->id);

        summary::get_for_course($course->id)->recalculate();
        $this->assert_summary($expected, $course->id);
    }

    public function test_delete_rating(): void {
        $user = $this->getDataGenerator()->create_user();
        $user2 = $this->getDataGenerator()->create_user();
        $course = $this->getDataGenerator()->create_course();

        // Set rating as the first user.
        $this->setUser($user);
        api::set_rating($course->id, (object)['rating' => 4]);

        // Set rating as the second user.
        $this->setUser($user2);
        api::set_rating($course->id, (object)['rating' => 2]);

        $this->assert_rating(['rating' => 4, 'review' => ''], $user->id, $course->id);
        $this->assert_rating(['rating' => 2, 'review' => ''], $user2->id, $course->id);
        $expected = ['cntall' => 2, 'avgrating' => 3, 'sumrating' => 6, 'cnt02' => 1, 'cnt03' => 0, 'cnt04' => 1];
        $this->assert_summary($expected, $course->id);
        $rating = rating::get_record(['userid' => $user->id, 'courseid' => $course->id]);

        // Delete rating for the first user.
        $sink = $this->redirectEvents();
        api::delete_rating($rating->get('id'));
        $this->assert_event($sink, rating_deleted::class, $course->id, 'Course rating deleted',
            '%ahas deleted course rating%a');

        $this->assert_rating(null, $user->id, $course->id);
        $this->assert_rating(['rating' => 2, 'review' => ''], $user2->id, $course->id);
        $expected = ['cntall' => 1, 'avgrating' => 2, 'sumrating' => 2, 'cnt02' => 1, 'cnt03' => 0, 'cnt04' => 0];
        $this->assert_summary($expected, $course->id);
    }

    public function test_flag_rating(): void {
        $user = $this->getDataGenerator()->create_user();
        $user2 = $this->getDataGenerator()->create_user();
        $course = $this->getDataGenerator()->create_course();

        // Set rating as the first user.
        $this->setUser($user);
        $rating = api::set_rating($course->id, (object)['rating' => 4]);

        // Flag rating as the second user.
        $this->setUser($user2);
        $sink = $this->redirectEvents();
        api::flag_review($rating->get('id'));
        $this->assert_event($sink, flag_created::class, $course->id, 'Course rating flagged',
            '%ahas flagged the course rating%a');

        // Revoke.
        api::revoke_review_flag($rating->get('id'));
        $this->assert_event($sink, flag_deleted::class, $course->id, 'Course rating flag revoked',
            '%ahas revoked their flag%a');

        // Flag again and delete rating as admin.
        api::flag_review($rating->get('id'));
        $this->assert_event($sink, flag_created::class, $course->id, 'Course rating flagged',
            '%ahas flagged the course rating%a');
        $this->setAdminUser();
        api::delete_rating($rating->get('id'), 'spam');
        $this->assert_event($sink, rating_deleted::class, $course->id, 'Course rating deleted',
            '%ahas deleted course rating%a. Reason provided: spam');
    }

    public function test_reindex(): void {
        $user = $this->getDataGenerator()->create_user();
        $user2 = $this->getDataGenerator()->create_user();
        $course = $this->getDataGenerator()->create_course();

        $this->assert_rating([], $user->id, $course->id);
        $this->assert_rating([], $user2->id, $course->id);
        $this->assert_summary(['cntall' => 0], $course->id);

        // Set rating as the first user.
        $this->setUser($user);
        $sink = $this->redirectEvents();
        api::set_rating($course->id, (object)['rating' => 4]);
        $this->assert_event($sink, rating_created::class, $course->id, 'Course rating created',
            '%ahas rated the course with 4 stars');

        $this->assert_rating(['rating' => 4, 'review' => ''], $user->id, $course->id);
        $expected = ['cntall' => 1, 'avgrating' => 4, 'sumrating' => 4, 'cnt02' => 0, 'cnt03' => 0, 'cnt04' => 1];
        $this->assert_summary($expected, $course->id);

        api::reindex();

        $this->assert_rating(['rating' => 4, 'review' => ''], $user->id, $course->id);
        $expected = ['cntall' => 1, 'avgrating' => 4, 'sumrating' => 4, 'cnt02' => 0, 'cnt03' => 0, 'cnt04' => 1];
        $this->assert_summary($expected, $course->id);
    }

    public function test_create_rating(): void {
        $user = $this->getDataGenerator()->create_user();
        $course = $this->getDataGenerator()->create_course();
        $this->get_generator()->create_rating($user->id, $course->id, 3);
        $this->assertEquals(3, summary::get_for_course($course->id)->get('avgrating'));
    }
}