ÿØÿà JFIF    ÿÛ „  ( %!1"%)+...383-7(-.+  -%%--------+------------/----------------------------ÿÀ  ¶" ÿÄ     ÿÄ @    !1AQaq‘¡"±ÁÑð2’BRráS‚ñ3b¢CT²Ò #cÿÄ    ÿÄ '   1!AQ2a"‘±#ÿÚ   ? ò(/p‘—–¢·,M|¯G»R£üešT. ôŸEoCFñ‡ 4ÔkåóxR|µÁ¨ÓDà!Ç´µ˜‡p ÍQ‹§!þž^õWsŒy  åÁ§$á«%ºq˜™JyyË0y«^Ϗ -3ÃÏËÈ£Æ\P’ä„s&•‹Üs üô'àWt¹pñCp$bÚF`·7.^)¾!)ÒÙ˜–ÅÏš^h¾ø³·ìe±ŒaeÖ—Í,e=B}Jéߦ$$½—šÙÜCC¯¶è8β–Åkèwx LÉngT±æ¹£Ócœ¿Í/©Éÿ ‰ÚCÒ­mq;Tt¶–}0"zÖPcd1š­¤tˆ„ÐÓâqú[™öUCŠ2޿د­É(¸ßúçšmW,Ò ºf‹Ån™ÄLêÁ8üÁkÁ€L¥ÍuJ1œôrÂSŒÓöt!7EUíïb)AËi¬â-ÙHk8òÁiXYwÅ)O^"ÇØriÛô{»wRUKÙÏÚlÓ¬€‚’שT ] ªb—ÊîKC^×M“xùäñz¬zJ× {4[¹M'IÆeH1LCWÕ]œûº WDæà°OÅ‚¹c²ÞuA–rL`HšpCez‡V’— Ž‚Ë„–ñ­'µR0ÕÖ=À:±G»C\nÆÉ5*¢ †3šhQ4ýÒi$Õ1"Ü]¢p-q3zcQ$ªö¨¥æf›‘»´ÝÚHá„e²JÇ–iÊ:·à¬Øc5Åk»Laª*wi [0Ówk ¦a¦0Õ²ÅÅ‚T¸’³q:Æ<¹ú Ò˜Æ}2VE•Ѐp¡Ô3Úº …h³^LJºçì¥Ánó|˜ÖˆÎˆ+« ¦dø‘Mº–d\ªDðÛ„Ì×LÝ——Ð9Ç|•FèçVm™¨€™3õS–9Äå¢<̉Ìm¢±`·º˜&Z²ž Ë®D«¶»6™‰©­HÈ Àá«h­i³04æ’hxœi3ï¨(ÓL·†‹¶ËpŒ ƒÜÒ? :õdsÀ –3æÓ·Üjâ¬9—]8n¼&]I F£¼…ì–—Œ0v°LǐÅfÛ2TÖ’aµ˜N@ÖBSÏ^à´4-±°b×nÈÈœÆD=KdŒø…ÄÈK,›ÖR§fjü$^ÔÎ!„6^ s:Üun\ôkCÞò÷<—Lúl Í›G¹ÀPá¼Nc¡šÒ…ÙWͳœ±vÊáЄ×9‹P‰cEØ‘™›µŸP<×g1 º©Ø¬wiÁ^l5× I/';Ë+´Z‡ÓÄFÕp[Èð’ HÄ ó 9¬Dl4“é±Ï”V=^Hð;žLädÉa‚°Ë,#/Ž~²BÔÛ QãñQtIeórVB,ÏÂf‹4#Μc‹É3ZIÖ Œ¢¥·üxa‡Ãçè„jŠ[5 Ä!U?,3žÎ×…ðdÓ÷háŠA‰ìÄ4÷ÃÜZÃE{‰®+W\ZÍE[‰»µk»MÝ­`¢©†˜ÃV‹-f¢¡†¢a«eŠ%«Y¨©q%jâd,Ôq`) ¤¦…šˆ ¦E>0Ùb²t–Ÿ  ÷jsςҒKÈcø(é›;k(’#ôŒQ ™C> õ³M9óºÆµ¤çâ;uÀ)5>RÔ¸fÓ~Ȧ—’-$fe«"FâUˆÊwµPÏu w+"åÉó¨6s™Û9(À¨ù©hE°½†n`m~“@©›p!Vkhµ Á1šbEqqÊ@ºèl›2 –Yã%‘‚hÛ{a¶‡ÅÞM²¬çL34®Çl]®‹ŽØŒm'RÜe35æöˆ­Á­ cP9šeÁköo´L³Ì=p8]”‡B¶,šº|ɏdzXˆØk*ÇÚ{#ñ‰pã(€¶‡9ý'šÜ³½¯kƒ†Â’ëSOƒ•Å®H6+a¢µˆ‹Y¨b˜b+XˆÖ!°u!¢"µˆˆn2ˆ Å0Ä`Å Ô®c(ÓÜGº•Ä»Œ âWî%q.áÐÄ÷î%umÍ£+ÜQ,Vn¦-GphU¸¢X­&,Gt ·K¢ÅÅ·¥RÄÊÉbKn N­R2g$VµÕó Qr#–ÓZUÏð0ÎtÙÀœz ëöo“Îk¯n„T°"SÞ+³/…€½"ižãO%Í%&Δâ‘ËØ´IˆÓ°Ë‰ÞM3.™yçÁzit›`4C`k¢¼á!álÄï sÂ[×!¤l¾æo"ƒIÔ'@ªu)3›ô®“@Z à¾] üœ'‡4¸PÞkÁ-qÄH‰ÓÍBd´ÿ —¹°ÄA&ÊUC„öz‚‚RæŠtf¬>˜ÑidšEçÑL÷áý¡V±h§­c¿¥¨ƒ@g¼…Ýö^ÞËc.ž„æ“I[ ª+·zÐÒÚ,½Ð»¶€C›7jk$๖€Ž¾ÅÙpp¶ÍþéΤ ½ØÎŒq ñpæžÅ¥#Øâ49ÃÂK^MYV“,¶Éz_òæ:…;Õ«¦I3âJ£¤û5$7Èxün\â]Êe¯(Ÿ&Ž‹¶2<6Ćf× æ¢ƱqWı[(ÙÂŽç‚T7¨@ÈÉÍß‚ïÙ"&3TYlGށ†)†#5ŠA©^T2ÆÁ†)©†©†¤yGXÁ†©"†)©¼ÅV WS†#©]R–r‹q5ÕbâWþBh¯u+ªÅÄ×ü„nÑ^êkªÁj‰jeœWˆÕÕ`µDµ2Ì+Ä µDµµ5Ô{¤Þ0RF’H÷AÛ<ÊÁ§ ƈæ0Ñ£ê4 öÚµ[¬/1²€â<-¬šö²r×0o×w ´xÐÒË„ g01¬„åEM蛇Ã\6µE›‘fÙO k÷CºnÓ1`iîÒ² áÂ!ÑLÄÿ ³¬\õ*:M=Ç»c‹2[‰ØsÕLò\ŒK L^™üÄN\¶#» Ç\……ßïIœÉ$¸ÎñÍ®ÔHÇ Ûí/´>Li”„†g^$ 7NÍÑÑ2M2ªNg=k½Ð}Ÿl6‡ —JWˆ”ÞéT·+µ¦üÂFè¢Vs6ýÆ4»2+2IÝ9ô9®ÇGÙÇ1+Ä8;*Õ§ž½D*îÑWÚçá3Ma¤·ˆ¥t}š²Ü…Q+Æüµ8HíS”üxGéÁˆ¦Ël„éÈ 5 µÄƒ6äÜ(kIš¯S†fÖ&¸®×v|Äyˆ*禌Éù@7-®Ä[]ÎÖ¿ê`º…=:…žJVnݳvðÖKE¼‚ »mà ÷f®:Ë ™–4rð¡4` ܹçž~‹C=œ…¾Ïj|VDtK.ƒ"9 Ï ­–éXá³6WÒïIL-Ї Ç PKX\&&provider_test.phpnu[. namespace core_ai\provider; use core_ai\aiactions\generate_image; use core_ai\aiactions\generate_text; use core_ai\aiactions\responses\response_generate_image; use core_ai\aiactions\responses\response_generate_text; use core_ai\aiactions\responses\response_summarise_text; use core_ai\aiactions\summarise_text; use core_ai\manager; use core_ai\privacy\provider; use core_privacy\local\request\approved_contextlist; use core_privacy\local\request\approved_userlist; use core_privacy\local\request\writer; use core_privacy\tests\request\content_writer; /** * Unit tests for \core_ai\provider * * @package core_ai * @copyright 2024 Matt Porritt * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @covers \core_ai\provider */ final class provider_test extends \advanced_testcase { /** @var \core_ai\manager */ private $manager; /** @var \core_ai\provider */ private $provider; /** * Overriding setUp() function to always reset after tests. */ public function setUp(): void { parent::setUp(); $this->resetAfterTest(); // Create the provider instance. $this->manager = \core\di::get(\core_ai\manager::class); $config = ['data' => 'goeshere']; $this->provider = $this->manager->create_provider_instance( classname: '\aiprovider_openai\provider', name: 'dummy', config: $config, ); } /** * Test get_contexts_for_userid(). */ public function test_get_contexts_for_userid(): void { $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $syscontext = \context_system::instance(); // AI policy. // Set the user policy. manager::user_policy_accepted($user1->id, $course1context->id); // Retrieve the user1's context ids. $contextids = provider::get_contexts_for_userid($user1->id); $this->assertNotEmpty($contextids->get_contextids()); $this->assertCount(1, $contextids->get_contextids()); $this->assertTrue(in_array($course1context->id, $contextids->get_contextids())); // Retrieve the user2's context ids. $contextids = provider::get_contexts_for_userid($user2->id); $this->assertEmpty($contextids->get_contextids()); // AI generate text. $action = new generate_text( contextid: $course2context->id, userid: $user1->id, prompttext: 'This is a test prompt', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); // Retrieve the user1's context ids. $contextids = provider::get_contexts_for_userid($user1->id); $this->assertNotEmpty($contextids->get_contextids()); $this->assertCount(2, $contextids->get_contextids()); $this->assertTrue(in_array($course1context->id, $contextids->get_contextids())); $this->assertTrue(in_array($course2context->id, $contextids->get_contextids())); // AI generate image. $action = new generate_image( contextid: $syscontext->id, userid: $user2->id, prompttext: 'This is a test prompt', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); // Retrieve the user1's context ids. $contextids = provider::get_contexts_for_userid($user1->id); $this->assertNotEmpty($contextids->get_contextids()); $this->assertCount(2, $contextids->get_contextids()); $this->assertTrue(in_array($course1context->id, $contextids->get_contextids())); $this->assertTrue(in_array($course2context->id, $contextids->get_contextids())); // Retrieve the user2's context ids. $contextids = provider::get_contexts_for_userid($user2->id); $this->assertNotEmpty($contextids->get_contextids()); $this->assertCount(1, $contextids->get_contextids()); $this->assertFalse(in_array($course2context->id, $contextids->get_contextids())); $this->assertTrue(in_array($syscontext->id, $contextids->get_contextids())); // AI summarise text. $action = new summarise_text( contextid: $course2context->id, userid: $user2->id, prompttext: 'This is a test prompt', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); // Retrieve the user2's context ids. $contextids = provider::get_contexts_for_userid($user2->id); $this->assertNotEmpty($contextids->get_contextids()); $this->assertCount(2, $contextids->get_contextids()); $this->assertTrue(in_array($course2context->id, $contextids->get_contextids())); $this->assertTrue(in_array($syscontext->id, $contextids->get_contextids())); } /** * Test export_user_data() for AI policy. */ public function test_export_user_data_for_policy(): void { $user = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $coursecontext = \context_course::instance($course->id); // Set the user policy. manager::user_policy_accepted($user->id, $coursecontext->id); // Retrieve the user's context ids. $contextlist = provider::get_contexts_for_userid($user->id); $approvedcontextlist = new approved_contextlist($user, 'core_ai', $contextlist->get_contextids()); provider::export_user_data($approvedcontextlist); foreach ($contextlist as $context) { /** @var content_writer $writer */ $writer = writer::with_context($context); $this->assertTrue($writer->has_any_data()); $subcontexts = [ get_string('ai', 'core_ai'), ]; $name = 'policy'; $data = $writer->get_related_data($subcontexts, $name); $this->assertEquals($coursecontext->id, $data->contextid); } } /** * Test export_user_data() for generate text. */ public function test_export_user_data_for_generate_text(): void { $user = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $timecreated1 = $clock->time(); $action = new generate_text( contextid: $course1context->id, userid: $user->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $timecreated2 = $clock->time(); $action = new generate_text( contextid: $course2context->id, userid: $user->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method->invoke($this->manager, $this->provider, $action, $actionresponse); // Retrieve the user's context ids. $contextlist = provider::get_contexts_for_userid($user->id); $approvedcontextlist = new approved_contextlist($user, 'core_ai', $contextlist->get_contextids()); provider::export_user_data($approvedcontextlist); foreach ($contextlist as $context) { /** @var content_writer $writer */ $writer = writer::with_context($context); $this->assertTrue($writer->has_any_data()); if ($context->instanceid == $course1context->instanceid) { $subcontexts = [ get_string('ai', 'core_ai'), get_string('action_generate_text', 'core_ai'), date('c', $timecreated1), ]; $name = 'action_generate_text'; $data = $writer->get_related_data($subcontexts, $name); $this->assertEquals('generate_text', $data->actionname); $this->assertEquals($course1context->id, $data->contextid); $this->assertEquals('This is a test prompt 1', $data->prompt); $this->assertEquals('This is the generated content 1', $data->generatedcontent); $this->assertEquals('9', $data->prompttokens); $this->assertEquals('12', $data->completiontoken); $this->assertEquals(get_string('yes'), $data->success); $this->assertEquals('gpt-4o', $data->model); } if ($context->instanceid == $course2context->instanceid) { $subcontexts = [ get_string('ai', 'core_ai'), get_string('action_generate_text', 'core_ai'), date('c', $timecreated2), ]; $name = 'action_generate_text'; $data = $writer->get_related_data($subcontexts, $name); $this->assertEquals('generate_text', $data->actionname); $this->assertEquals($course2context->id, $data->contextid); $this->assertEquals('This is a test prompt 2', $data->prompt); $this->assertEquals('This is the generated content 2', $data->generatedcontent); $this->assertEquals('10', $data->prompttokens); $this->assertEquals('15', $data->completiontoken); $this->assertEquals(get_string('yes'), $data->success); $this->assertEquals('gpt-4o', $data->model); } } } /** * Test export_user_data() for generate image. */ public function test_export_user_data_for_generate_image(): void { $user = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $timecreated1 = $clock->time(); $action = new generate_image( contextid: $course1context->id, userid: $user->id, prompttext: 'This is a test prompt 1', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image1.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $timecreated2 = $clock->time(); $action = new generate_image( contextid: $course2context->id, userid: $user->id, prompttext: 'This is a test prompt 2', quality: 'hd', aspectratio: 'portrait', numimages: 2, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image2.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method->invoke($this->manager, $this->provider, $action, $actionresponse); // Retrieve the user's context ids. $contextlist = provider::get_contexts_for_userid($user->id); $approvedcontextlist = new approved_contextlist($user, 'core_ai', $contextlist->get_contextids()); provider::export_user_data($approvedcontextlist); foreach ($contextlist as $context) { /** @var content_writer $writer */ $writer = writer::with_context($context); $this->assertTrue($writer->has_any_data()); if ($context->instanceid == $course1context->instanceid) { $subcontexts = [ get_string('ai', 'core_ai'), get_string('action_generate_image', 'core_ai'), date('c', $timecreated1), ]; $name = 'action_generate_image'; $data = $writer->get_related_data($subcontexts, $name); $this->assertEquals('generate_image', $data->actionname); $this->assertEquals($course1context->id, $data->contextid); $this->assertEquals('This is a test prompt 1', $data->prompt); $this->assertEquals('1', $data->numberimages); $this->assertEquals('hd', $data->quality); $this->assertEquals('square', $data->aspectratio); $this->assertEquals('vivid', $data->style); $this->assertEquals('https://example.com/image1.png', $data->sourceurl); $this->assertEquals(get_string('yes'), $data->success); $this->assertEquals('dall-e-3', $data->model); } if ($context->instanceid == $course2context->instanceid) { $subcontexts = [ get_string('ai', 'core_ai'), get_string('action_generate_image', 'core_ai'), date('c', $timecreated2), ]; $name = 'action_generate_image'; $data = $writer->get_related_data($subcontexts, $name); $this->assertEquals('generate_image', $data->actionname); $this->assertEquals($course2context->id, $data->contextid); $this->assertEquals('This is a test prompt 2', $data->prompt); $this->assertEquals('2', $data->numberimages); $this->assertEquals('hd', $data->quality); $this->assertEquals('portrait', $data->aspectratio); $this->assertEquals('vivid', $data->style); $this->assertEquals('https://example.com/image2.png', $data->sourceurl); $this->assertEquals(get_string('yes'), $data->success); $this->assertEquals('dall-e-3', $data->model); } } } /** * Test export_user_data() for summarise text. */ public function test_export_user_data_for_summarise_text(): void { $user = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $timecreated1 = $clock->time(); $action = new summarise_text( contextid: $course1context->id, userid: $user->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $timecreated2 = $clock->time(); $action = new summarise_text( contextid: $course2context->id, userid: $user->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method->invoke($this->manager, $this->provider, $action, $actionresponse); // Retrieve the user's context ids. $contextlist = provider::get_contexts_for_userid($user->id); $approvedcontextlist = new approved_contextlist($user, 'core_ai', $contextlist->get_contextids()); provider::export_user_data($approvedcontextlist); foreach ($contextlist as $context) { /** @var content_writer $writer */ $writer = writer::with_context($context); $this->assertTrue($writer->has_any_data()); if ($context->instanceid == $course1context->instanceid) { $subcontexts = [ get_string('ai', 'core_ai'), get_string('action_summarise_text', 'core_ai'), date('c', $timecreated1), ]; $name = 'action_summarise_text'; $data = $writer->get_related_data($subcontexts, $name); $this->assertEquals('summarise_text', $data->actionname); $this->assertEquals($course1context->id, $data->contextid); $this->assertEquals('This is a test prompt 1', $data->prompt); $this->assertEquals('This is the generated content 1', $data->generatedcontent); $this->assertEquals('9', $data->prompttokens); $this->assertEquals('12', $data->completiontoken); $this->assertEquals(get_string('yes'), $data->success); $this->assertEquals('gpt-4o', $data->model); } if ($context->instanceid == $course2context->instanceid) { $subcontexts = [ get_string('ai', 'core_ai'), get_string('action_summarise_text', 'core_ai'), date('c', $timecreated2), ]; $name = 'action_summarise_text'; $data = $writer->get_related_data($subcontexts, $name); $this->assertEquals('summarise_text', $data->actionname); $this->assertEquals($course2context->id, $data->contextid); $this->assertEquals('This is a test prompt 2', $data->prompt); $this->assertEquals('This is the generated content 2', $data->generatedcontent); $this->assertEquals('10', $data->prompttokens); $this->assertEquals('15', $data->completiontoken); $this->assertEquals(get_string('yes'), $data->success); $this->assertEquals('gpt-4o', $data->model); } } } /** * Test delete_data_for_all_users_in_context() for AI policy. */ public function test_delete_data_for_all_users_in_context_for_policy(): void { global $DB; $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $user3 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); // Set the user policy. 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); // Verify all policy data for Course 1 has been deleted. $datas = $DB->get_records('ai_policy_register', ['contextid' => $course1context->id]); $this->assertCount(0, $datas); // Verify policy data for Course 2 are still present. $datas = $DB->get_records('ai_policy_register', ['contextid' => $course2context->id]); $this->assertCount(1, $datas); } /** * Test delete_data_for_all_users_in_context() for generate text. */ public function test_delete_data_for_all_users_in_context_for_generate_text(): void { global $DB; $user = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $action = new generate_text( contextid: $course1context->id, userid: $user->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult1 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new generate_text( contextid: $course2context->id, userid: $user->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult2 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); provider::delete_data_for_all_users_in_context($course1context); $actionid1 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult1]); $actionid2 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult2]); $record = $DB->get_record('ai_action_generate_text', ['id' => $actionid1]); $this->assertEquals('', $record->prompt); $this->assertEquals('', $record->responseid); $this->assertEquals('', $record->fingerprint); $this->assertEquals('', $record->generatedcontent); $record = $DB->get_record('ai_action_generate_text', ['id' => $actionid2]); $this->assertNotEquals('', $record->prompt); $this->assertNotEquals('', $record->responseid); $this->assertNotEquals('', $record->fingerprint); $this->assertNotEquals('', $record->generatedcontent); } /** * Test delete_data_for_all_users_in_context() for generate image. */ public function test_delete_data_for_all_users_in_context_for_generate_image(): void { global $DB; $user = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $action = new generate_image( contextid: $course1context->id, userid: $user->id, prompttext: 'This is a test prompt 1', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image1.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult1 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new generate_image( contextid: $course2context->id, userid: $user->id, prompttext: 'This is a test prompt 2', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image2.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult2 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); provider::delete_data_for_all_users_in_context($course1context); $actionid1 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult1]); $actionid2 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult2]); $record = $DB->get_record('ai_action_generate_image', ['id' => $actionid1]); $this->assertEquals('', $record->prompt); $this->assertEquals('', $record->sourceurl); $this->assertEquals('', $record->revisedprompt); $record = $DB->get_record('ai_action_generate_image', ['id' => $actionid2]); $this->assertNotEquals('', $record->prompt); $this->assertNotEquals('', $record->sourceurl); $this->assertNotEquals('', $record->revisedprompt); } /** * Test delete_data_for_all_users_in_context() for summarise text. */ public function test_delete_data_for_all_users_in_context_for_summarise_text(): void { global $DB; $user = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $action = new summarise_text( contextid: $course1context->id, userid: $user->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult1 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new summarise_text( contextid: $course2context->id, userid: $user->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult2 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); provider::delete_data_for_all_users_in_context($course1context); $actionid1 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult1]); $actionid2 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult2]); $record = $DB->get_record('ai_action_summarise_text', ['id' => $actionid1]); $this->assertEquals('', $record->prompt); $this->assertEquals('', $record->responseid); $this->assertEquals('', $record->fingerprint); $this->assertEquals('', $record->generatedcontent); $record = $DB->get_record('ai_action_summarise_text', ['id' => $actionid2]); $this->assertNotEquals('', $record->prompt); $this->assertNotEquals('', $record->responseid); $this->assertNotEquals('', $record->fingerprint); $this->assertNotEquals('', $record->generatedcontent); } /** * Test delete_data_for_all_users_in_context() for generate text. */ public function test_delete_data_for_user_for_generate_text(): void { global $DB; $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $coursecontext = \context_course::instance($course->id); $clock = $this->mock_clock_with_frozen(); $action = new generate_text( contextid: $coursecontext->id, userid: $user1->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult1 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new generate_text( contextid: $coursecontext->id, userid: $user2->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult2 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $contextlist = provider::get_contexts_for_userid($user1->id); $approvedcontextlist = new approved_contextlist($user1, 'core_ai', $contextlist->get_contextids()); provider::delete_data_for_user($approvedcontextlist); $actionid1 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult1]); $actionid2 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult2]); $record = $DB->get_record('ai_action_generate_text', ['id' => $actionid1]); $this->assertEquals('', $record->prompt); $this->assertEquals('', $record->responseid); $this->assertEquals('', $record->fingerprint); $this->assertEquals('', $record->generatedcontent); $record = $DB->get_record('ai_action_generate_text', ['id' => $actionid2]); $this->assertNotEquals('', $record->prompt); $this->assertNotEquals('', $record->responseid); $this->assertNotEquals('', $record->fingerprint); $this->assertNotEquals('', $record->generatedcontent); } /** * Test delete_data_for_all_users_in_context() for generate image. */ public function test_delete_data_for_user_for_generate_image(): void { global $DB; $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $coursecontext = \context_course::instance($course->id); $clock = $this->mock_clock_with_frozen(); $action = new generate_image( contextid: $coursecontext->id, userid: $user1->id, prompttext: 'This is a test prompt 1', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image1.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult1 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new generate_image( contextid: $coursecontext->id, userid: $user2->id, prompttext: 'This is a test prompt 2', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image2.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult2 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $contextlist = provider::get_contexts_for_userid($user1->id); $approvedcontextlist = new approved_contextlist($user1, 'core_ai', $contextlist->get_contextids()); provider::delete_data_for_user($approvedcontextlist); $actionid1 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult1]); $actionid2 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult2]); $record = $DB->get_record('ai_action_generate_image', ['id' => $actionid1]); $this->assertEquals('', $record->prompt); $this->assertEquals('', $record->sourceurl); $this->assertEquals('', $record->revisedprompt); $record = $DB->get_record('ai_action_generate_image', ['id' => $actionid2]); $this->assertNotEquals('', $record->prompt); $this->assertNotEquals('', $record->sourceurl); $this->assertNotEquals('', $record->revisedprompt); } /** * Test delete_data_for_all_users_in_context() for summarise text. */ public function test_delete_data_for_user_for_summarise_text(): void { global $DB; $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course = $this->getDataGenerator()->create_course(); $coursecontext = \context_course::instance($course->id); $clock = $this->mock_clock_with_frozen(); $action = new summarise_text( contextid: $coursecontext->id, userid: $user1->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult1 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new summarise_text( contextid: $coursecontext->id, userid: $user2->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult2 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $contextlist = provider::get_contexts_for_userid($user1->id); $approvedcontextlist = new approved_contextlist($user1, 'core_ai', $contextlist->get_contextids()); provider::delete_data_for_user($approvedcontextlist); $actionid1 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult1]); $actionid2 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult2]); $record = $DB->get_record('ai_action_summarise_text', ['id' => $actionid1]); $this->assertEquals('', $record->prompt); $this->assertEquals('', $record->responseid); $this->assertEquals('', $record->fingerprint); $this->assertEquals('', $record->generatedcontent); $record = $DB->get_record('ai_action_summarise_text', ['id' => $actionid2]); $this->assertNotEquals('', $record->prompt); $this->assertNotEquals('', $record->responseid); $this->assertNotEquals('', $record->fingerprint); $this->assertNotEquals('', $record->generatedcontent); } /** * Test get_users_in_context() for policy. */ public function test_get_users_in_context_for_policy(): void { $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->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'); provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $this->assertTrue(in_array($user1->id, $userlist->get_userids())); $this->assertFalse(in_array($user2->id, $userlist->get_userids())); // The user list for course2context should return user2. $userlist = new \core_privacy\local\request\userlist($course2context, 'core_ai'); provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $this->assertTrue(in_array($user2->id, $userlist->get_userids())); $this->assertFalse(in_array($user1->id, $userlist->get_userids())); } /** * Test get_users_in_context() for generate text. */ public function test_get_users_in_context_for_generate_text(): void { global $DB; $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $action = new generate_text( contextid: $course1context->id, userid: $user1->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new generate_text( contextid: $course2context->id, userid: $user2->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); // The user list for course1context should return user1. $userlist = new \core_privacy\local\request\userlist($course1context, 'core_ai'); provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $this->assertTrue(in_array($user1->id, $userlist->get_userids())); $this->assertFalse(in_array($user2->id, $userlist->get_userids())); // The user list for course2context should return user2. $userlist = new \core_privacy\local\request\userlist($course2context, 'core_ai'); provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $this->assertTrue(in_array($user2->id, $userlist->get_userids())); $this->assertFalse(in_array($user1->id, $userlist->get_userids())); } /** * Test get_users_in_context() for generate image. */ public function test_get_users_in_context_for_generate_image(): void { $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $action = new generate_image( contextid: $course1context->id, userid: $user1->id, prompttext: 'This is a test prompt 1', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image1.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new generate_image( contextid: $course2context->id, userid: $user2->id, prompttext: 'This is a test prompt 2', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image2.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); // The user list for course1context should return user1. $userlist = new \core_privacy\local\request\userlist($course1context, 'core_ai'); provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $this->assertTrue(in_array($user1->id, $userlist->get_userids())); $this->assertFalse(in_array($user2->id, $userlist->get_userids())); // The user list for course2context should return user2. $userlist = new \core_privacy\local\request\userlist($course2context, 'core_ai'); provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $this->assertTrue(in_array($user2->id, $userlist->get_userids())); $this->assertFalse(in_array($user1->id, $userlist->get_userids())); } /** * Test get_users_in_context() for summarise text. */ public function test_get_users_in_context_for_summarise_text(): void { $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $action = new summarise_text( contextid: $course1context->id, userid: $user1->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new summarise_text( contextid: $course2context->id, userid: $user2->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $method->invoke($this->manager, $this->provider, $action, $actionresponse); // The user list for course1context should return user1. $userlist = new \core_privacy\local\request\userlist($course1context, 'core_ai'); provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $this->assertTrue(in_array($user1->id, $userlist->get_userids())); $this->assertFalse(in_array($user2->id, $userlist->get_userids())); // The user list for course2context should return user2. $userlist = new \core_privacy\local\request\userlist($course2context, 'core_ai'); provider::get_users_in_context($userlist); $this->assertCount(1, $userlist); $this->assertTrue(in_array($user2->id, $userlist->get_userids())); $this->assertFalse(in_array($user1->id, $userlist->get_userids())); } /** * Test delete_data_for_users() for policy. */ public function test_delete_data_for_users_for_policy(): void { global $DB; $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $user3 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); // Set the user policy. 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); $approvedlist = new approved_userlist($course1context, 'core_ai', $userlist->get_userids()); provider::delete_data_for_users($approvedlist); // Verify all policy data for user1 and user2 have been deleted. $this->assertFalse($DB->record_exists('ai_policy_register', ['userid' => $user1->id])); $this->assertFalse($DB->record_exists('ai_policy_register', ['userid' => $user2->id])); // Verify policy data for user3 is still present. $this->assertTrue($DB->record_exists('ai_policy_register', ['userid' => $user3->id])); } /** * Test delete_data_for_users() for generate text. */ public function test_delete_data_for_users_for_generate_text(): void { global $DB; $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $action = new generate_text( contextid: $course1context->id, userid: $user1->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult1 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new generate_text( contextid: $course2context->id, userid: $user2->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_generate_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult2 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $userlist = new \core_privacy\local\request\userlist($course1context, 'core_ai'); provider::get_users_in_context($userlist); $approvedlist = new approved_userlist($course1context, 'core_ai', $userlist->get_userids()); provider::delete_data_for_users($approvedlist); $actionid1 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult1]); $actionid2 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult2]); $record = $DB->get_record('ai_action_generate_text', ['id' => $actionid1]); $this->assertEquals('', $record->prompt); $this->assertEquals('', $record->responseid); $this->assertEquals('', $record->fingerprint); $this->assertEquals('', $record->generatedcontent); $record = $DB->get_record('ai_action_generate_text', ['id' => $actionid2]); $this->assertNotEquals('', $record->prompt); $this->assertNotEquals('', $record->responseid); $this->assertNotEquals('', $record->fingerprint); $this->assertNotEquals('', $record->generatedcontent); } /** * Test delete_data_for_users() for generate image. */ public function test_delete_data_for_users_for_generate_image(): void { global $DB; $user1 = $this->getDataGenerator()->create_user(); $user2 = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $action = new generate_image( contextid: $course1context->id, userid: $user1->id, prompttext: 'This is a test prompt 1', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image1.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult1 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new generate_image( contextid: $course2context->id, userid: $user2->id, prompttext: 'This is a test prompt 2', quality: 'hd', aspectratio: 'square', numimages: 1, style: 'vivid', ); $body = [ 'revisedprompt' => 'This is a revised prompt', 'sourceurl' => 'https://example.com/image2.png', 'model' => 'dall-e-3', ]; $actionresponse = new response_generate_image( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult2 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $userlist = new \core_privacy\local\request\userlist($course1context, 'core_ai'); provider::get_users_in_context($userlist); $approvedlist = new approved_userlist($course1context, 'core_ai', $userlist->get_userids()); provider::delete_data_for_users($approvedlist); $actionid1 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult1]); $actionid2 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult2]); $record = $DB->get_record('ai_action_generate_image', ['id' => $actionid1]); $this->assertEquals('', $record->prompt); $this->assertEquals('', $record->sourceurl); $this->assertEquals('', $record->revisedprompt); $record = $DB->get_record('ai_action_generate_image', ['id' => $actionid2]); $this->assertNotEquals('', $record->prompt); $this->assertNotEquals('', $record->sourceurl); $this->assertNotEquals('', $record->revisedprompt); } /** * Test delete_data_for_users() for generate image. */ public function test_delete_data_for_users_for_summarise_text(): void { global $DB; $user = $this->getDataGenerator()->create_user(); $course1 = $this->getDataGenerator()->create_course(); $course2 = $this->getDataGenerator()->create_course(); $course1context = \context_course::instance($course1->id); $course2context = \context_course::instance($course2->id); $clock = $this->mock_clock_with_frozen(); $action = new summarise_text( contextid: $course1context->id, userid: $user->id, prompttext: 'This is a test prompt 1', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 1', 'finishreason' => 'stop', 'prompttokens' => 9, 'completiontokens' => 12, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult1 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $clock->bump(2); $action = new summarise_text( contextid: $course2context->id, userid: $user->id, prompttext: 'This is a test prompt 2', ); $body = [ 'id' => 'chatcmpl-123', 'fingerprint' => 'fp_44709d6fcb', 'generatedcontent' => 'This is the generated content 2', 'finishreason' => 'stop', 'prompttokens' => 10, 'completiontokens' => 15, 'model' => 'gpt-4o', ]; $actionresponse = new response_summarise_text( success: true, ); $actionresponse->set_response_data($body); $method = new \ReflectionMethod($this->manager, 'store_action_result'); $storeresult2 = $method->invoke($this->manager, $this->provider, $action, $actionresponse); $userlist = new \core_privacy\local\request\userlist($course1context, 'core_ai'); provider::get_users_in_context($userlist); $approvedlist = new approved_userlist($course1context, 'core_ai', $userlist->get_userids()); provider::delete_data_for_users($approvedlist); $actionid1 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult1]); $actionid2 = $DB->get_field('ai_action_register', 'actionid', ['id' => $storeresult2]); $record = $DB->get_record('ai_action_summarise_text', ['id' => $actionid1]); $this->assertEquals('', $record->prompt); $this->assertEquals('', $record->responseid); $this->assertEquals('', $record->fingerprint); $this->assertEquals('', $record->generatedcontent); $record = $DB->get_record('ai_action_summarise_text', ['id' => $actionid2]); $this->assertNotEquals('', $record->prompt); $this->assertNotEquals('', $record->responseid); $this->assertNotEquals('', $record->fingerprint); $this->assertNotEquals('', $record->generatedcontent); } /** * Test get_name. */ public function test_get_name(): void { $this->assertEquals('aiprovider_openai', $this->provider->get_name()); } /** * Test the is_request_allowed method of the provider abstract class. */ public function test_is_request_allowed(): void { // Create action. $action1 = new summarise_text( contextid: 1, userid: 1, prompttext: 'This is a test prompt 1', ); $action2 = new summarise_text( contextid: 1, userid: 2, prompttext: 'This is a test prompt 1', ); // Create the provider instance. $config = [ 'enableuserratelimit' => true, 'userratelimit' => 3, 'enableglobalratelimit' => true, 'globalratelimit' => 5, ]; $provider = $this->manager->create_provider_instance( classname: '\aiprovider_openai\provider', name: 'dummy', config: $config, ); // Make 3 requests for the first user, all should be allowed. for ($i = 0; $i < 3; $i++) { $this->assertTrue($provider->is_request_allowed($action1)); } // The 4th request should be denied. $this->assertFalse($provider->is_request_allowed($action1)['success']); // Make 2 requests for the second user, all should be allowed. for ($i = 0; $i < 2; $i++) { $this->assertTrue($provider->is_request_allowed($action2)); } // THe final request should be denied. $this->assertFalse($provider->is_request_allowed($action2)['success']); } } PKX\&&provider_test.phpnu[PKQg