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 communication_matrix;
use communication_matrix\local\command;
use communication_matrix\local\spec\v1p7;
use communication_matrix\local\spec\features;
use communication_matrix\tests\fixtures\mocked_matrix_client;
use core\http_client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Response;
use moodle_exception;
defined('MOODLE_INTERNAL') || die();
require_once(__DIR__ . '/matrix_client_test_trait.php');
/**
* Tests for the matrix_client class.
*
* @package communication_matrix
* @category test
* @copyright 2023 Andrew Lyons <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \communication_matrix\matrix_client
* @coversDefaultClass \communication_matrix\matrix_client
*/
class matrix_client_test extends \advanced_testcase {
use matrix_client_test_trait;
/**
* Data provider for valid calls to ::instance.
* @return array
*/
public static function instance_provider(): array {
$testcases = [
'Standard versions' => [
null,
v1p7::class,
],
];
// Remove a couple of versions.
$versions = self::get_current_versions();
array_pop($versions);
array_pop($versions);
$testcases['Older server'] = [
$versions,
array_key_last($versions),
];
// Limited version compatibility, including newer than we support now.
$testcases['Newer versions with crossover'] = [
[
'v1.6',
'v1.7',
'v7.9',
],
\communication_matrix\local\spec\v1p7::class,
];
return $testcases;
}
/**
* Test that the instance method returns a valid instance for the given versions.
*
* @dataProvider instance_provider
* @param array|null $versions
* @param string $expectedversion
*/
public function test_instance(
?array $versions,
string $expectedversion,
): void {
// Create a mock and queue two responses.
$mock = new MockHandler([
$this->get_mocked_version_response($versions),
]);
$handlerstack = HandlerStack::create($mock);
$container = [];
$history = Middleware::history($container);
$handlerstack->push($history);
$client = new http_client(['handler' => $handlerstack]);
mocked_matrix_client::set_client($client);
$instance = mocked_matrix_client::instance(
'https://example.com',
'testtoken',
);
$this->assertInstanceOf(matrix_client::class, $instance);
// Only the version API has been called.
$this->assertCount(1, $container);
$request = reset($container);
$this->assertEquals('/_matrix/client/versions', $request['request']->getUri()->getPath());
// The client should be a v1p7 client as that is the highest compatible version.
$this->assertInstanceOf($expectedversion, $instance);
}
/**
* Test that the instance method returns a valid instance for the given versions.
*/
public function test_instance_cached(): void {
$mock = new MockHandler([
$this->get_mocked_version_response(),
$this->get_mocked_version_response(),
]);
$handlerstack = HandlerStack::create($mock);
$container = [];
$history = Middleware::history($container);
$handlerstack->push($history);
$client = new http_client(['handler' => $handlerstack]);
mocked_matrix_client::set_client($client);
$instance = mocked_matrix_client::instance('https://example.com', 'testtoken');
$this->assertInstanceOf(matrix_client::class, $instance);
// Only the version API has been called.
$this->assertCount(1, $container);
// Call the API again. It should not lead to additional fetches.
$instance = mocked_matrix_client::instance('https://example.com', 'testtoken');
$instance = mocked_matrix_client::instance('https://example.com', 'testtoken');
$this->assertCount(1, $container);
// But a different endpoint will.
$instance = mocked_matrix_client::instance('https://example.org', 'testtoken');
$this->assertCount(2, $container);
}
/**
* Test that the instance method throws an appropriate exception if no support is found.
*/
public function test_instance_no_support(): void {
// Create a mock and queue two responses.
$mock = new MockHandler([
$this->get_mocked_version_response(['v99.9']),
]);
$handlerstack = HandlerStack::create($mock);
$container = [];
$history = Middleware::history($container);
$handlerstack->push($history);
$client = new http_client(['handler' => $handlerstack]);
mocked_matrix_client::set_client($client);
$this->expectException(moodle_exception::class);
$this->expectExceptionMessage('No supported Matrix API versions found.');
mocked_matrix_client::instance(
'https://example.com',
'testtoken',
);
}
/**
* Test the feature implementation check methods.
*
* @covers ::implements_feature
* @covers ::get_supported_versions
* @dataProvider implements_feature_provider
* @param string $version
* @param array|string $features
* @param bool $expected
*/
public function test_implements_feature(
string $version,
array|string $features,
bool $expected,
): void {
$instance = $this->get_mocked_instance_for_version($version);
$this->assertEquals($expected, $instance->implements_feature($features));
}
/**
* Test the feature implementation requirement methods.
*
* @covers ::implements_feature
* @covers ::get_supported_versions
* @covers ::require_feature
* @dataProvider implements_feature_provider
* @param string $version
* @param array|string $features
* @param bool $expected
*/
public function test_require_feature(
string $version,
array|string $features,
bool $expected,
): void {
$instance = $this->get_mocked_instance_for_version($version);
if ($expected) {
$this->assertEmpty($instance->require_feature($features));
} else {
$this->expectException('moodle_exception');
$instance->require_feature($features);
}
}
/**
* Test the feature implementation requirement methods for a require all.
*
* @covers ::implements_feature
* @covers ::get_supported_versions
* @covers ::require_feature
* @covers ::require_features
* @dataProvider require_features_provider
* @param string $version
* @param array|string $features
* @param bool $expected
*/
public function test_require_features(
string $version,
array|string $features,
bool $expected,
): void {
$instance = $this->get_mocked_instance_for_version($version);
if ($expected) {
$this->assertEmpty($instance->require_features($features));
} else {
$this->expectException('moodle_exception');
$instance->require_features($features);
}
}
/**
* Data provider for feature implementation check tests.
*
* @return array
*/
public static function implements_feature_provider(): array {
return [
'Basic supported feature' => [
'v1.7',
features\matrix\media_create_v1::class,
true,
],
'Basic unsupported feature' => [
'v1.6',
features\matrix\media_create_v1::class,
false,
],
'[supported] as array' => [
'v1.6',
[features\matrix\create_room_v3::class],
true,
],
'[supported, supported] as array' => [
'v1.6',
[
features\matrix\create_room_v3::class,
features\matrix\update_room_avatar_v3::class,
],
true,
],
'[unsupported] as array' => [
'v1.6',
[
features\matrix\media_create_v1::class,
],
false,
],
'[unsupported, supported] as array' => [
'v1.6',
[
features\matrix\media_create_v1::class,
features\matrix\update_room_avatar_v3::class,
],
true,
],
];
}
/**
* Data provider for feature implementation check tests.
*
* @return array
*/
public static function require_features_provider(): array {
// We'll just add to the standard testcases.
$testcases = array_map(static function (array $testcase): array {
$testcase[1] = [$testcase[1]];
return $testcase;
}, self::implements_feature_provider());
$testcases['Require many supported features'] = [
'v1.6',
[
features\matrix\create_room_v3::class,
features\matrix\update_room_avatar_v3::class,
],
true,
];
$testcases['Require many including an unsupported feature'] = [
'v1.6',
[
features\matrix\create_room_v3::class,
features\matrix\media_create_v1::class,
],
false,
];
$testcases['Require many including an unsupported feature which has an alternate'] = [
'v1.6',
[
features\matrix\create_room_v3::class,
[
features\matrix\media_create_v1::class,
features\matrix\update_room_avatar_v3::class,
],
],
true,
];
return $testcases;
}
/**
* Test the get_version method.
*
* @param string $version
* @param string $expectedversion
* @dataProvider get_version_provider
* @covers ::get_version
* @covers ::get_version_from_classname
*/
public function test_get_version(
string $version,
string $expectedversion,
): void {
$instance = $this->get_mocked_instance_for_version($version);
$this->assertEquals($expectedversion, $instance->get_version());
}
/**
* Data provider for get_version tests.
*
* @return array
*/
public static function get_version_provider(): array {
return [
['v1.1', '1.1'],
['v1.7', '1.7'],
];
}
/**
* Tests the meets_version method.
*
* @param string $version The version of the API to test against
* @param string $testversion The version to test
* @param bool $expected Whether the version meets the requirement
* @dataProvider meets_version_provider
* @covers ::meets_version
*/
public function test_meets_version(
string $version,
string $testversion,
bool $expected,
): void {
$instance = $this->get_mocked_instance_for_version($version);
$this->assertEquals($expected, $instance->meets_version($testversion));
}
/**
* Tests the requires_version method.
*
* @param string $version The version of the API to test against
* @param string $testversion The version to test
* @param bool $expected Whether the version meets the requirement
* @dataProvider meets_version_provider
* @covers ::requires_version
*/
public function test_requires_version(
string $version,
string $testversion,
bool $expected,
): void {
$instance = $this->get_mocked_instance_for_version($version);
if ($expected) {
$this->assertEmpty($instance->requires_version($testversion));
} else {
$this->expectException('moodle_exception');
$instance->requires_version($testversion);
}
}
/**
* Data provider for meets_version tests.
*
* @return array
*/
public static function meets_version_provider(): array {
return [
'Same version' => ['v1.1', '1.1', true],
'Same version latest' => ['v1.7', '1.7', true],
'Newer version rejected' => ['v1.1', '1.7', false],
'Older version accepted' => ['v1.7', '1.1', true],
];
}
/**
* Test the execute method with a command.
*
* @covers ::execute
*/
public function test_command_is_executed(): void {
$historycontainer = [];
$mock = new MockHandler();
$instance = $this->get_mocked_instance_for_version('v1.6', $historycontainer, $mock);
$command = new command(
$instance,
method: 'GET',
endpoint: 'test/endpoint',
params: [
'test' => 'test',
],
);
$mock->append(new Response(200));
$rc = new \ReflectionClass($instance);
$rcm = $rc->getMethod('execute');
$result = $rcm->invoke($instance, $command);
$this->assertEquals(200, $result->getStatusCode());
$this->assertCount(1, $historycontainer);
$request = array_shift($historycontainer);
$this->assertEquals('GET', $request['request']->getMethod());
$this->assertEquals('/test/endpoint', $request['request']->getUri()->getPath());
}
}