Skip to content

Commit

Permalink
MDL-75947 ltiservice_gradebookservices: test sequential score posting
Browse files Browse the repository at this point in the history
This tests highlights a problem with the score handling logic and
will currently fail. On score post, the current time is used for
grade->timemodified instead of the score->timestamp provided by the
tool in the POST JSON.
  • Loading branch information
snake committed Aug 9, 2024
1 parent 0363e2c commit 17c4b84
Showing 1 changed file with 91 additions and 0 deletions.
91 changes: 91 additions & 0 deletions mod/lti/service/gradebookservices/tests/lineitem_test.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
namespace ltiservice_gradebookservices;

use ltiservice_gradebookservices\local\resources\lineitem;
use ltiservice_gradebookservices\local\resources\results;
use ltiservice_gradebookservices\local\resources\scores;
use ltiservice_gradebookservices\local\service\gradebookservices;

/**
Expand Down Expand Up @@ -174,6 +176,95 @@ public function test_execute_put_addsubreview(): void {
$this->assertFalse(isset($responseitem->submissionReview->custom));
}

/**
* Test running a series of score updates, highlighting problems with the score posting logic.
*
* @return void
*/
public function test_sequential_score_posts(): void {
global $CFG;
require_once($CFG->dirroot . '/mod/lti/locallib.php');
$this->resetAfterTest();
$resourceid = 'test-resource-id';
$tag = 'tag';
$course = $this->getDataGenerator()->create_course();
$typeid = $this->create_type();
$user = $this->getDataGenerator()->create_and_enrol($course);

// Create mod instance with line item - nothing pushed via services yet.
$gbservice = new gradebookservices();
$gbservice->set_type(lti_get_type($typeid));
$modinstance = $this->create_graded_lti($typeid, $course, $resourceid, $tag);
$gradeitems = $gbservice->get_lineitems($course->id, null, null, null, null, null, $typeid);
$this->assertEquals(1, $gradeitems[0]); // The 1st item in the array is the items count.

// Post a score so that there's at least one grade present at the time of lineitem update.
$score = new scores($gbservice);
$_SERVER['REQUEST_METHOD'] = \mod_lti\local\ltiservice\resource_base::HTTP_POST;
$_SERVER['PATH_INFO'] = "/$course->id/lineitems/{$gradeitems[1][0]->id}/lineitem/scores?type_id=$typeid";
$token = lti_new_access_token($typeid, ['https://purl.imsglobal.org/spec/lti-ags/scope/score']);
$_SERVER['HTTP_Authorization'] = 'Bearer '.$token->token;
$_GET['type_id'] = (string)$typeid;
$score->scoreGiven = "8.0";
$score->scoreMaximum = "10.0";
$score->activityProgress = "Completed";
$score->timestamp = "2024-08-07T18:54:36.736+00:00";
$score->gradingProgress = "FullyGraded";
$score->userId = $user->id;
$response = new \mod_lti\local\ltiservice\response();
$response->set_content_type('application/vnd.ims.lis.v1.score+json');
$response->set_request_data(json_encode($score));
$score->execute($response);

// The grade in Moodle should reflect the score->timestamp, not the time of the score post.
$grades = grade_get_grades($course->id, 'mod', 'lti', $modinstance->id, $user->id);
$studentgrade = array_shift($grades->items[0]->grades);
$this->assertEquals(strtotime($score->timestamp), $studentgrade->dategraded);

// Read the results via the service. This should also return the incorrect dategraded time (timemodified in LTI terms).
$_SERVER['REQUEST_METHOD'] = \mod_lti\local\ltiservice\resource_base::HTTP_GET;
$_SERVER['PATH_INFO'] = "/$course->id/lineitems/{$gradeitems[1][0]->id}/lineitem/results?type_id=$typeid";
$token = lti_new_access_token($typeid, ['https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly']);
$_SERVER['HTTP_Authorization'] = 'Bearer '.$token->token;
$_GET['type_id'] = (string)$typeid;
$result = new results($gbservice);
$response = new \mod_lti\local\ltiservice\response();
$response->set_content_type('application/vnd.ims.lis.v2.resultcontainer+json');
$result->execute($response);
$body = json_decode($response->get_body());
$result = array_shift($body);
$this->assertGreaterThanOrEqual(strtotime($score->timestamp), strtotime($result->timestamp));

// Now, try to post a newer score using a timestamp that is greater than the one originally sent, but less than the time at
// which the score was last posted. This should be valid since the timestamp is greater than the original posted score.
$_SERVER['REQUEST_METHOD'] = \mod_lti\local\ltiservice\resource_base::HTTP_POST;
$_SERVER['PATH_INFO'] = "/$course->id/lineitems/{$gradeitems[1][0]->id}/lineitem/scores?type_id=$typeid";
$token = lti_new_access_token($typeid, ['https://purl.imsglobal.org/spec/lti-ags/scope/score']);
$_SERVER['HTTP_Authorization'] = 'Bearer '.$token->token;
$_GET['type_id'] = (string)$typeid;
$score->scoreGiven = "14";
$score->timestamp = "2024-08-08T18:54:36.736+00:00";
$response = new \mod_lti\local\ltiservice\response();
$response->set_content_type('application/vnd.ims.lis.v1.score+json');
$response->set_request_data(json_encode($score));
$score->execute($response);
$this->assertEquals(200, json_decode($response->get_code()));

// Finally, post a score that's just been updated (i.e. score->timestamp = now)
$_SERVER['REQUEST_METHOD'] = \mod_lti\local\ltiservice\resource_base::HTTP_POST;
$_SERVER['PATH_INFO'] = "/$course->id/lineitems/{$gradeitems[1][0]->id}/lineitem/scores?type_id=$typeid";
$token = lti_new_access_token($typeid, ['https://purl.imsglobal.org/spec/lti-ags/scope/score']);
$_SERVER['HTTP_Authorization'] = 'Bearer '.$token->token;
$_GET['type_id'] = (string)$typeid;
$score->scoreGiven = "15";
$score->timestamp = date('c', time());
$response = new \mod_lti\local\ltiservice\response();
$response->set_content_type('application/vnd.ims.lis.v1.score+json');
$response->set_request_data(json_encode($score));
$score->execute($response);
$this->assertEquals(200, json_decode($response->get_code()));
}

/**
* Inserts a graded lti instance, which should create a grade_item and gradebookservices record.
*
Expand Down

0 comments on commit 17c4b84

Please sign in to comment.