MDL-82627 AI: Extra PHPUnit tests for OpenAI provider
This commit is contained in:
+1
-1
@@ -77,7 +77,7 @@ class get_policy_status extends external_api {
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => manager::get_user_policy($userid),
|
||||
'status' => manager::get_user_policy_status($userid),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ class set_policy_status extends external_api {
|
||||
require_capability('moodle/ai:acceptpolicy', $usercontext);
|
||||
|
||||
return [
|
||||
'success' => manager::set_user_policy($USER->id, $contextid),
|
||||
'success' => manager::user_policy_accepted($USER->id, $contextid),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -195,13 +195,13 @@ class manager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user policy.
|
||||
* Set the policy acceptance for a given user.
|
||||
*
|
||||
* @param int $userid The user id.
|
||||
* @param int $contextid The context id the policy was accepted in.
|
||||
* @return bool True if the policy was set, false otherwise.
|
||||
*/
|
||||
public static function set_user_policy(int $userid, int $contextid): bool {
|
||||
public static function user_policy_accepted(int $userid, int $contextid): bool {
|
||||
global $DB;
|
||||
|
||||
$record = (object) [
|
||||
@@ -224,7 +224,7 @@ class manager {
|
||||
* @param int $userid The user id.
|
||||
* @return bool True if the policy was accepted, false otherwise.
|
||||
*/
|
||||
public static function get_user_policy(int $userid): bool {
|
||||
public static function get_user_policy_status(int $userid): bool {
|
||||
$policycache = \cache::make('core', 'ai_policy');
|
||||
return $policycache->get($userid);
|
||||
}
|
||||
|
||||
@@ -47,10 +47,25 @@ final class process_generate_image_test extends \advanced_testcase {
|
||||
parent::setUp();
|
||||
// Load a response body from a file.
|
||||
$this->responsebodyjson = file_get_contents(self::get_fixture_path('aiprovider_openai', 'image_request_success.json'));
|
||||
$this->create_provider();
|
||||
$this->create_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the provider object.
|
||||
*/
|
||||
private function create_provider(): void {
|
||||
$this->provider = new \aiprovider_openai\provider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the action object.
|
||||
* @param int $userid The user id to use in the action.
|
||||
*/
|
||||
private function create_action(int $userid = 1): void {
|
||||
$this->action = new \core_ai\aiactions\generate_image(
|
||||
contextid: 1,
|
||||
userid: 1,
|
||||
userid: $userid,
|
||||
prompttext: 'This is a test prompt',
|
||||
quality: 'hd',
|
||||
aspectratio: 'square',
|
||||
@@ -326,4 +341,308 @@ final class process_generate_image_test extends \advanced_testcase {
|
||||
$this->assertEquals('An image that represents the concept of a \'test\'.', $result->get_response_data()['revisedprompt']);
|
||||
$this->assertEquals($url, $result->get_response_data()['sourceurl']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method with error.
|
||||
*/
|
||||
public function test_process_error(): void {
|
||||
$this->resetAfterTest();
|
||||
// Log in user.
|
||||
$this->setUser($this->getDataGenerator()->create_user());
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
401,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode(['error' => ['message' => 'Invalid Authentication']]),
|
||||
));
|
||||
|
||||
$processor = new process_generate_image($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
|
||||
$this->assertInstanceOf(\core_ai\aiactions\responses\response_base::class, $result);
|
||||
$this->assertFalse($result->get_success());
|
||||
$this->assertEquals('generate_image', $result->get_actionname());
|
||||
$this->assertEquals(401, $result->get_errorcode());
|
||||
$this->assertEquals('Invalid Authentication', $result->get_errormessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method with user rate limiter.
|
||||
*/
|
||||
public function test_process_with_user_rate_limiter(): void {
|
||||
$this->resetAfterTest();
|
||||
// Create users.
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// Mock clock.
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
|
||||
// Set the user rate limiter.
|
||||
set_config('enableuserratelimit', 1, 'aiprovider_openai');
|
||||
set_config('userratelimit', 1, 'aiprovider_openai');
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
$url = 'https://example.com/test.jpg';
|
||||
|
||||
// Case 1: User rate limit has not been reached.
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode([
|
||||
'created' => 1719140500,
|
||||
'data' => [
|
||||
(object) [
|
||||
'revised_prompt' => 'An image that represents the concept of a \'test\'.',
|
||||
'url' => $url,
|
||||
],
|
||||
],
|
||||
]),
|
||||
));
|
||||
// The image downloaded from the server successfully.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'image/jpeg'],
|
||||
\GuzzleHttp\Psr7\Utils::streamFor(fopen(self::get_fixture_path('aiprovider_openai', 'test.jpg'), 'r')),
|
||||
));
|
||||
$processor = new process_generate_image($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
|
||||
// Case 2: User rate limit has been reached.
|
||||
$clock->bump(HOURSECS - 10);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode([
|
||||
'created' => 1719140500,
|
||||
'data' => [
|
||||
(object) [
|
||||
'revised_prompt' => 'An image that represents the concept of a \'test\'.',
|
||||
'url' => $url,
|
||||
],
|
||||
],
|
||||
]),
|
||||
));
|
||||
// The image downloaded from the server successfully.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'image/jpeg'],
|
||||
\GuzzleHttp\Psr7\Utils::streamFor(fopen(self::get_fixture_path('aiprovider_openai', 'test.jpg'), 'r')),
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_generate_image($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertEquals(429, $result->get_errorcode());
|
||||
$this->assertEquals('User rate limit exceeded', $result->get_errormessage());
|
||||
$this->assertFalse($result->get_success());
|
||||
|
||||
// Case 3: User rate limit has not been reached for a different user.
|
||||
// Log in user2.
|
||||
$this->setUser($user2);
|
||||
$this->create_provider();
|
||||
$this->create_action($user2->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode([
|
||||
'created' => 1719140500,
|
||||
'data' => [
|
||||
(object) [
|
||||
'revised_prompt' => 'An image that represents the concept of a \'test\'.',
|
||||
'url' => $url,
|
||||
],
|
||||
],
|
||||
]),
|
||||
));
|
||||
// The image downloaded from the server successfully.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'image/jpeg'],
|
||||
\GuzzleHttp\Psr7\Utils::streamFor(fopen(self::get_fixture_path('aiprovider_openai', 'test.jpg'), 'r')),
|
||||
));
|
||||
$processor = new process_generate_image($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
|
||||
// Case 4: Time window has passed, user rate limit should be reset.
|
||||
$clock->bump(11);
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode([
|
||||
'created' => 1719140500,
|
||||
'data' => [
|
||||
(object) [
|
||||
'revised_prompt' => 'An image that represents the concept of a \'test\'.',
|
||||
'url' => $url,
|
||||
],
|
||||
],
|
||||
]),
|
||||
));
|
||||
// The image downloaded from the server successfully.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'image/jpeg'],
|
||||
\GuzzleHttp\Psr7\Utils::streamFor(fopen(self::get_fixture_path('aiprovider_openai', 'test.jpg'), 'r')),
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_generate_image($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method with global rate limiter.
|
||||
*/
|
||||
public function test_process_with_global_rate_limiter(): void {
|
||||
$this->resetAfterTest();
|
||||
// Create users.
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// Mock clock.
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
|
||||
// Set the global rate limiter.
|
||||
set_config('enableglobalratelimit', 1, 'aiprovider_openai');
|
||||
set_config('globalratelimit', 1, 'aiprovider_openai');
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
$url = 'https://example.com/test.jpg';
|
||||
|
||||
// Case 1: Global rate limit has not been reached.
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode([
|
||||
'created' => 1719140500,
|
||||
'data' => [
|
||||
(object) [
|
||||
'revised_prompt' => 'An image that represents the concept of a \'test\'.',
|
||||
'url' => $url,
|
||||
],
|
||||
],
|
||||
]),
|
||||
));
|
||||
// The image downloaded from the server successfully.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'image/jpeg'],
|
||||
\GuzzleHttp\Psr7\Utils::streamFor(fopen(self::get_fixture_path('aiprovider_openai', 'test.jpg'), 'r')),
|
||||
));
|
||||
$processor = new process_generate_image($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
|
||||
// Case 2: Global rate limit has been reached.
|
||||
$clock->bump(HOURSECS - 10);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode([
|
||||
'created' => 1719140500,
|
||||
'data' => [
|
||||
(object) [
|
||||
'revised_prompt' => 'An image that represents the concept of a \'test\'.',
|
||||
'url' => $url,
|
||||
],
|
||||
],
|
||||
]),
|
||||
));
|
||||
// The image downloaded from the server successfully.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'image/jpeg'],
|
||||
\GuzzleHttp\Psr7\Utils::streamFor(fopen(self::get_fixture_path('aiprovider_openai', 'test.jpg'), 'r')),
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_generate_image($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertEquals(429, $result->get_errorcode());
|
||||
$this->assertEquals('Global rate limit exceeded', $result->get_errormessage());
|
||||
$this->assertFalse($result->get_success());
|
||||
|
||||
// Case 3: Global rate limit has been reached for a different user too.
|
||||
// Log in user2.
|
||||
$this->setUser($user2);
|
||||
$this->create_provider();
|
||||
$this->create_action($user2->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode([
|
||||
'created' => 1719140500,
|
||||
'data' => [
|
||||
(object) [
|
||||
'revised_prompt' => 'An image that represents the concept of a \'test\'.',
|
||||
'url' => $url,
|
||||
],
|
||||
],
|
||||
]),
|
||||
));
|
||||
// The image downloaded from the server successfully.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'image/jpeg'],
|
||||
\GuzzleHttp\Psr7\Utils::streamFor(fopen(self::get_fixture_path('aiprovider_openai', 'test.jpg'), 'r')),
|
||||
));
|
||||
$processor = new process_generate_image($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertFalse($result->get_success());
|
||||
|
||||
// Case 4: Time window has passed, global rate limit should be reset.
|
||||
$clock->bump(11);
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode([
|
||||
'created' => 1719140500,
|
||||
'data' => [
|
||||
(object) [
|
||||
'revised_prompt' => 'An image that represents the concept of a \'test\'.',
|
||||
'url' => $url,
|
||||
],
|
||||
],
|
||||
]),
|
||||
));
|
||||
// The image downloaded from the server successfully.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'image/jpeg'],
|
||||
\GuzzleHttp\Psr7\Utils::streamFor(fopen(self::get_fixture_path('aiprovider_openai', 'test.jpg'), 'r')),
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_generate_image($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +48,25 @@ final class process_generate_text_test extends \advanced_testcase {
|
||||
parent::setUp();
|
||||
// Load a response body from a file.
|
||||
$this->responsebodyjson = file_get_contents(self::get_fixture_path('aiprovider_openai', 'text_request_success.json'));
|
||||
$this->create_provider();
|
||||
$this->create_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the provider object.
|
||||
*/
|
||||
private function create_provider(): void {
|
||||
$this->provider = new \aiprovider_openai\provider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the action object.
|
||||
* @param int $userid The user id to use in the action.
|
||||
*/
|
||||
private function create_action(int $userid = 1): void {
|
||||
$this->action = new \core_ai\aiactions\generate_text(
|
||||
contextid: 1,
|
||||
userid: 1,
|
||||
userid: $userid,
|
||||
prompttext: 'This is a test prompt',
|
||||
);
|
||||
}
|
||||
@@ -216,4 +231,220 @@ final class process_generate_text_test extends \advanced_testcase {
|
||||
$this->assertEquals($response['errorcode'], $result->get_errorcode());
|
||||
$this->assertEquals($response['errormessage'], $result->get_errormessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method.
|
||||
*/
|
||||
public function test_process(): void {
|
||||
$this->resetAfterTest();
|
||||
// Log in user.
|
||||
$this->setUser($this->getDataGenerator()->create_user());
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
|
||||
$this->assertInstanceOf(\core_ai\aiactions\responses\response_base::class, $result);
|
||||
$this->assertTrue($result->get_success());
|
||||
$this->assertEquals('generate_text', $result->get_actionname());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method with error.
|
||||
*/
|
||||
public function test_process_error(): void {
|
||||
$this->resetAfterTest();
|
||||
// Log in user.
|
||||
$this->setUser($this->getDataGenerator()->create_user());
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
401,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode(['error' => ['message' => 'Invalid Authentication']]),
|
||||
));
|
||||
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
|
||||
$this->assertInstanceOf(\core_ai\aiactions\responses\response_base::class, $result);
|
||||
$this->assertFalse($result->get_success());
|
||||
$this->assertEquals('generate_text', $result->get_actionname());
|
||||
$this->assertEquals(401, $result->get_errorcode());
|
||||
$this->assertEquals('Invalid Authentication', $result->get_errormessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method with user rate limiter.
|
||||
*/
|
||||
public function test_process_with_user_rate_limiter(): void {
|
||||
$this->resetAfterTest();
|
||||
// Create users.
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// Mock clock.
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
|
||||
// Set the user rate limiter.
|
||||
set_config('enableuserratelimit', 1, 'aiprovider_openai');
|
||||
set_config('userratelimit', 1, 'aiprovider_openai');
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
|
||||
// Case 1: User rate limit has not been reached.
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
|
||||
// Case 2: User rate limit has been reached.
|
||||
$clock->bump(HOURSECS - 10);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertEquals(429, $result->get_errorcode());
|
||||
$this->assertEquals('User rate limit exceeded', $result->get_errormessage());
|
||||
$this->assertFalse($result->get_success());
|
||||
|
||||
// Case 3: User rate limit has not been reached for a different user.
|
||||
// Log in user2.
|
||||
$this->setUser($user2);
|
||||
$this->create_provider();
|
||||
$this->create_action($user2->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
|
||||
// Case 4: Time window has passed, user rate limit should be reset.
|
||||
$clock->bump(11);
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method with global rate limiter.
|
||||
*/
|
||||
public function test_process_with_global_rate_limiter(): void {
|
||||
$this->resetAfterTest();
|
||||
// Create users.
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// Mock clock.
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
|
||||
// Set the global rate limiter.
|
||||
set_config('enableglobalratelimit', 1, 'aiprovider_openai');
|
||||
set_config('globalratelimit', 1, 'aiprovider_openai');
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
|
||||
// Case 1: Global rate limit has not been reached.
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
|
||||
// Case 2: Global rate limit has been reached.
|
||||
$clock->bump(HOURSECS - 10);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertEquals(429, $result->get_errorcode());
|
||||
$this->assertEquals('Global rate limit exceeded', $result->get_errormessage());
|
||||
$this->assertFalse($result->get_success());
|
||||
|
||||
// Case 3: Global rate limit has been reached for a different user too.
|
||||
// Log in user2.
|
||||
$this->setUser($user2);
|
||||
$this->create_provider();
|
||||
$this->create_action($user2->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertFalse($result->get_success());
|
||||
|
||||
// Case 4: Time window has passed, global rate limit should be reset.
|
||||
$clock->bump(11);
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_generate_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,10 +48,25 @@ final class process_summarise_text_test extends \advanced_testcase {
|
||||
parent::setUp();
|
||||
// Load a response body from a file.
|
||||
$this->responsebodyjson = file_get_contents(self::get_fixture_path('aiprovider_openai', 'text_request_success.json'));
|
||||
$this->create_provider();
|
||||
$this->create_action();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the provider object.
|
||||
*/
|
||||
private function create_provider(): void {
|
||||
$this->provider = new \aiprovider_openai\provider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the action object.
|
||||
* @param int $userid The user id to use in the action.
|
||||
*/
|
||||
private function create_action(int $userid = 1): void {
|
||||
$this->action = new \core_ai\aiactions\summarise_text(
|
||||
contextid: 1,
|
||||
userid: 1,
|
||||
userid: $userid,
|
||||
prompttext: 'This is a test prompt',
|
||||
);
|
||||
}
|
||||
@@ -68,6 +83,8 @@ final class process_summarise_text_test extends \advanced_testcase {
|
||||
|
||||
$body = (object) json_decode($request->getBody()->getContents());
|
||||
|
||||
$this->assertEquals('system', $body->messages[0]->role);
|
||||
$this->assertEquals(get_string('action_summarise_text_instruction', 'core_ai'), $body->messages[0]->content);
|
||||
$this->assertEquals('This is a test prompt', $body->messages[1]->content);
|
||||
$this->assertEquals('user', $body->messages[1]->role);
|
||||
}
|
||||
@@ -208,4 +225,220 @@ final class process_summarise_text_test extends \advanced_testcase {
|
||||
$this->assertEquals($response['errorcode'], $result->get_errorcode());
|
||||
$this->assertEquals($response['errormessage'], $result->get_errormessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method.
|
||||
*/
|
||||
public function test_process(): void {
|
||||
$this->resetAfterTest();
|
||||
// Log in user.
|
||||
$this->setUser($this->getDataGenerator()->create_user());
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
|
||||
$this->assertInstanceOf(\core_ai\aiactions\responses\response_base::class, $result);
|
||||
$this->assertTrue($result->get_success());
|
||||
$this->assertEquals('summarise_text', $result->get_actionname());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method with error.
|
||||
*/
|
||||
public function test_process_error(): void {
|
||||
$this->resetAfterTest();
|
||||
// Log in user.
|
||||
$this->setUser($this->getDataGenerator()->create_user());
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
401,
|
||||
['Content-Type' => 'application/json'],
|
||||
json_encode(['error' => ['message' => 'Invalid Authentication']]),
|
||||
));
|
||||
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
|
||||
$this->assertInstanceOf(\core_ai\aiactions\responses\response_base::class, $result);
|
||||
$this->assertFalse($result->get_success());
|
||||
$this->assertEquals('summarise_text', $result->get_actionname());
|
||||
$this->assertEquals(401, $result->get_errorcode());
|
||||
$this->assertEquals('Invalid Authentication', $result->get_errormessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method with user rate limiter.
|
||||
*/
|
||||
public function test_process_with_user_rate_limiter(): void {
|
||||
$this->resetAfterTest();
|
||||
// Create users.
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// Mock clock.
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
|
||||
// Set the user rate limiter.
|
||||
set_config('enableuserratelimit', 1, 'aiprovider_openai');
|
||||
set_config('userratelimit', 1, 'aiprovider_openai');
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
|
||||
// Case 1: User rate limit has not been reached.
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
|
||||
// Case 2: User rate limit has been reached.
|
||||
$clock->bump(HOURSECS - 10);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertEquals(429, $result->get_errorcode());
|
||||
$this->assertEquals('User rate limit exceeded', $result->get_errormessage());
|
||||
$this->assertFalse($result->get_success());
|
||||
|
||||
// Case 3: User rate limit has not been reached for a different user.
|
||||
// Log in user2.
|
||||
$this->setUser($user2);
|
||||
$this->create_provider();
|
||||
$this->create_action($user2->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
|
||||
// Case 4: Time window has passed, user rate limit should be reset.
|
||||
$clock->bump(11);
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test process method with global rate limiter.
|
||||
*/
|
||||
public function test_process_with_global_rate_limiter(): void {
|
||||
$this->resetAfterTest();
|
||||
// Create users.
|
||||
$user1 = $this->getDataGenerator()->create_user();
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// Mock clock.
|
||||
$clock = $this->mock_clock_with_frozen();
|
||||
|
||||
// Set the global rate limiter.
|
||||
set_config('enableglobalratelimit', 1, 'aiprovider_openai');
|
||||
set_config('globalratelimit', 1, 'aiprovider_openai');
|
||||
|
||||
// Mock the http client to return a successful response.
|
||||
['mock' => $mock] = $this->get_mocked_http_client();
|
||||
|
||||
// Case 1: Global rate limit has not been reached.
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
|
||||
// Case 2: Global rate limit has been reached.
|
||||
$clock->bump(HOURSECS - 10);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertEquals(429, $result->get_errorcode());
|
||||
$this->assertEquals('Global rate limit exceeded', $result->get_errormessage());
|
||||
$this->assertFalse($result->get_success());
|
||||
|
||||
// Case 3: Global rate limit has been reached for a different user too.
|
||||
// Log in user2.
|
||||
$this->setUser($user2);
|
||||
$this->create_provider();
|
||||
$this->create_action($user2->id);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertFalse($result->get_success());
|
||||
|
||||
// Case 4: Time window has passed, global rate limit should be reset.
|
||||
$clock->bump(11);
|
||||
// Log in user1.
|
||||
$this->setUser($user1);
|
||||
// The response from OpenAI.
|
||||
$mock->append(new Response(
|
||||
200,
|
||||
['Content-Type' => 'application/json'],
|
||||
$this->responsebodyjson,
|
||||
));
|
||||
$this->create_provider();
|
||||
$this->create_action($user1->id);
|
||||
$processor = new process_summarise_text($this->provider, $this->action);
|
||||
$result = $processor->process();
|
||||
$this->assertTrue($result->get_success());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,4 @@ defined('MOODLE_INTERNAL') || die();
|
||||
$plugin->component = 'aiprovider_openai';
|
||||
$plugin->version = 2024060900;
|
||||
$plugin->requires = 2024041600;
|
||||
$plugin->maturity = MATURITY_ALPHA;
|
||||
$plugin->maturity = MATURITY_STABLE;
|
||||
|
||||
@@ -179,7 +179,7 @@ final class manager_test extends \advanced_testcase {
|
||||
$this->resetAfterTest();
|
||||
global $DB;
|
||||
|
||||
$result = manager::set_user_policy(1, 1);
|
||||
$result = manager::user_policy_accepted(1, 1);
|
||||
$this->assertTrue($result);
|
||||
|
||||
// Check record exists.
|
||||
@@ -195,7 +195,7 @@ final class manager_test extends \advanced_testcase {
|
||||
global $DB;
|
||||
|
||||
// Should be false for user initially.
|
||||
$result = manager::get_user_policy(1);
|
||||
$result = manager::get_user_policy_status(1);
|
||||
$this->assertFalse($result);
|
||||
|
||||
// Manually add record to the database.
|
||||
@@ -207,7 +207,7 @@ final class manager_test extends \advanced_testcase {
|
||||
$DB->insert_record('ai_policy_register', $record);
|
||||
|
||||
// Should be true for user now.
|
||||
$result = manager::get_user_policy(1);
|
||||
$result = manager::get_user_policy_status(1);
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ final class provider_test extends \advanced_testcase {
|
||||
|
||||
// AI policy.
|
||||
// Set the user policy.
|
||||
manager::set_user_policy($user1->id, $course1context->id);
|
||||
manager::user_policy_accepted($user1->id, $course1context->id);
|
||||
|
||||
// Retrieve the user1's context ids.
|
||||
$contextids = provider::get_contexts_for_userid($user1->id);
|
||||
@@ -185,7 +185,7 @@ final class provider_test extends \advanced_testcase {
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
|
||||
// Set the user policy.
|
||||
manager::set_user_policy($user->id, $coursecontext->id);
|
||||
manager::user_policy_accepted($user->id, $coursecontext->id);
|
||||
|
||||
// Retrieve the user's context ids.
|
||||
$contextlist = provider::get_contexts_for_userid($user->id);
|
||||
@@ -535,9 +535,9 @@ final class provider_test extends \advanced_testcase {
|
||||
$course2context = \context_course::instance($course2->id);
|
||||
|
||||
// Set the user policy.
|
||||
manager::set_user_policy($user1->id, $course1context->id);
|
||||
manager::set_user_policy($user2->id, $course1context->id);
|
||||
manager::set_user_policy($user3->id, $course2context->id);
|
||||
manager::user_policy_accepted($user1->id, $course1context->id);
|
||||
manager::user_policy_accepted($user2->id, $course1context->id);
|
||||
manager::user_policy_accepted($user3->id, $course2context->id);
|
||||
|
||||
provider::delete_data_for_all_users_in_context($course1context);
|
||||
|
||||
@@ -1016,8 +1016,8 @@ final class provider_test extends \advanced_testcase {
|
||||
$course1context = \context_course::instance($course1->id);
|
||||
$course2context = \context_course::instance($course2->id);
|
||||
|
||||
manager::set_user_policy($user1->id, $course1context->id);
|
||||
manager::set_user_policy($user2->id, $course2context->id);
|
||||
manager::user_policy_accepted($user1->id, $course1context->id);
|
||||
manager::user_policy_accepted($user2->id, $course2context->id);
|
||||
|
||||
// The user list for course1context should return user1.
|
||||
$userlist = new \core_privacy\local\request\userlist($course1context, 'core_ai');
|
||||
@@ -1268,9 +1268,9 @@ final class provider_test extends \advanced_testcase {
|
||||
$course2context = \context_course::instance($course2->id);
|
||||
|
||||
// Set the user policy.
|
||||
manager::set_user_policy($user1->id, $course1context->id);
|
||||
manager::set_user_policy($user2->id, $course1context->id);
|
||||
manager::set_user_policy($user3->id, $course2context->id);
|
||||
manager::user_policy_accepted($user1->id, $course1context->id);
|
||||
manager::user_policy_accepted($user2->id, $course1context->id);
|
||||
manager::user_policy_accepted($user3->id, $course2context->id);
|
||||
|
||||
$userlist = new \core_privacy\local\request\userlist($course1context, 'core_ai');
|
||||
provider::get_users_in_context($userlist);
|
||||
|
||||
@@ -77,7 +77,7 @@ class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menu
|
||||
return array_merge([
|
||||
'contextid' => $context->id,
|
||||
'userid' => $userid,
|
||||
'policyagreed' => manager::get_user_policy($userid),
|
||||
'policyagreed' => manager::get_user_policy_status($userid),
|
||||
], $allowedactions);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user