From b2a92df8f9660827eb11de5d01b35e60e7f6e62b Mon Sep 17 00:00:00 2001 From: Marcos Prieto Date: Fri, 13 Dec 2024 16:27:38 +0100 Subject: [PATCH] Functional test for the DeepLinking fields API Just covering the LTI1.1 version on this commit. --- .../authentication/_bearer_token.py | 2 +- tests/conftest.py | 2 +- tests/factories/lti_user.py | 8 ++- .../functional/views/lti/deep_linking_test.py | 69 +++++++++++++++++++ 4 files changed, 78 insertions(+), 3 deletions(-) diff --git a/lms/validation/authentication/_bearer_token.py b/lms/validation/authentication/_bearer_token.py index 1a13a504f9..61d0aa5c18 100644 --- a/lms/validation/authentication/_bearer_token.py +++ b/lms/validation/authentication/_bearer_token.py @@ -41,7 +41,7 @@ class BearerTokenSchema(PyramidRequestSchema): def __init__(self, request): super().__init__(request) - self._jwt_service = request.find_service(iface=JWTService) + self._jwt_service: JWTService = request.find_service(iface=JWTService) self._lti_user_service = request.find_service(iface=LTIUserService) self._secret = request.registry.settings["jwt_secret"] diff --git a/tests/conftest.py b/tests/conftest.py index 9267c30029..afa72a862d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,7 +9,7 @@ "jwt_secret": "test_secret", "google_client_id": "fake_client_id", "google_developer_key": "fake_developer_key", - "lms_secret": "TEST_LMS_SECRET", + "lms_secret": "test_secret", "aes_secret": b"TSeQ7E3dzbHgu5ydX2xCrKJiXTmfJbOe", "jinja2.filters": { "static_path": "pyramid_jinja2.filters:static_path_filter", diff --git a/tests/factories/lti_user.py b/tests/factories/lti_user.py index 0630228509..f4a3905943 100644 --- a/tests/factories/lti_user.py +++ b/tests/factories/lti_user.py @@ -4,6 +4,12 @@ from tests.factories.application_instance import ApplicationInstance from tests.factories.attributes import TOOL_CONSUMER_INSTANCE_GUID, USER_ID +_LTI = make_factory( + LTI, + course_id=Faker("hexify", text="^" * 40), + product_family="UNKNOWN", +) + LTIUser = make_factory( LTIUser, user_id=USER_ID, @@ -13,6 +19,6 @@ effective_lti_roles=[], tool_consumer_instance_guid=TOOL_CONSUMER_INSTANCE_GUID, display_name=Faker("name"), - lti=LTI(course_id=Faker("hexify", text="^" * 40), product_family="UNKNOWN"), + lti=SubFactory(_LTI), application_instance=SubFactory(ApplicationInstance), ) diff --git a/tests/functional/views/lti/deep_linking_test.py b/tests/functional/views/lti/deep_linking_test.py index 4c6ca1eb0b..e9937c7d3e 100644 --- a/tests/functional/views/lti/deep_linking_test.py +++ b/tests/functional/views/lti/deep_linking_test.py @@ -1,4 +1,6 @@ +import json import time +from datetime import timedelta import oauthlib.common import oauthlib.oauth1 @@ -6,6 +8,10 @@ from h_matchers import Any from lms.resources._js_config import JSConfig +from lms.services.jwt import JWTService +from lms.services.lti_user import LTIUserService +from tests import factories +from tests.conftest import TEST_SETTINGS class TestDeepLinkingLaunch: @@ -68,6 +74,69 @@ def test_basic_lti_launch_canvas_deep_linking_url( } +class TestDeepLinkingFieldsViews: + def test_file_picker_to_form_fields_v11( + self, app, authorization_param, application_instance + ): + response = app.post_json( + "/lti/1.1/deep_linking/form_fields", + params={ + "content_item_return_url": "https://apps.imsglobal.org/lti/cert/tp/tp_return.php/basic-lti-launch-request", + "content": {"type": "url", "url": "https://example.com"}, + }, + headers={"Authorization": f"Bearer {authorization_param}"}, + status=200, + ) + + response_json = response.json + content_items = response_json.pop( + "content_items" + ) # We'll assert this separately + assert response_json == { + "oauth_version": "1.0", + "oauth_nonce": Any.string(), + "oauth_timestamp": Any.string(), + "oauth_consumer_key": application_instance.consumer_key, + "oauth_signature_method": "HMAC-SHA1", + "lti_message_type": "ContentItemSelection", + "lti_version": "LTI-1p0", + "oauth_signature": Any.string(), + } + assert json.loads(content_items) == { + "@context": "http://purl.imsglobal.org/ctx/lti/v1/ContentItem", + "@graph": [ + { + "@type": "LtiLinkItem", + "mediaType": "application/vnd.ims.lti.v1.ltilink", + "url": "http://localhost/lti_launches", + "custom": { + "deep_linking_uuid": Any.string(), + "url": "https://example.com", + }, + } + ], + } + + @pytest.fixture + def lti_user(self, application_instance, lti_params): + return factories.LTIUser( + application_instance_id=application_instance.id, + application_instance=application_instance, + user_id=lti_params["user_id"], + roles=lti_params["roles"], + ) + + @pytest.fixture + def authorization_param(self, lti_user): + return JWTService.encode_with_secret( + LTIUserService( + lti_role_service=None, application_instance_service=None + ).serialize(lti_user), + secret=TEST_SETTINGS["lms_secret"], + lifetime=timedelta(days=1), + ) + + @pytest.fixture def lti_params(application_instance, sign_lti_params): params = {