From 4542458d5c029be853478edf4e04fca9ac6365c6 Mon Sep 17 00:00:00 2001 From: Jillian Vogel Date: Thu, 13 Jun 2024 21:34:57 +0930 Subject: [PATCH] test: adds transformer tests Uses transforms fixture test utilities from ERB to ensure that raw aggregator events are transformed to xAPI as expected. * test_output/ -- used by ERB to write failed event transforms for debugging * adds 'factory' as a test requirement because ERB tests needs it. --- .gitignore | 1 + completion_aggregator/transformers.py | 2 +- requirements/dev.txt | 7 ++ requirements/doc.txt | 7 ++ requirements/quality.txt | 7 ++ requirements/test.in | 1 + requirements/test.txt | 5 ++ test_output/.gitkeep | 1 + test_settings.py | 3 +- ...pletion_aggregator.completion.chapter.json | 45 +++++++++++ ...mpletion_aggregator.completion.course.json | 45 +++++++++++ ...tion_aggregator.completion.sequential.json | 45 +++++++++++ ...letion_aggregator.completion.vertical.json | 45 +++++++++++ ...ompletion_aggregator.progress.chapter.json | 51 +++++++++++++ ...completion_aggregator.progress.course.json | 51 +++++++++++++ ...letion_aggregator.progress.sequential.json | 51 +++++++++++++ ...mpletion_aggregator.progress.vertical.json | 51 +++++++++++++ ...pletion_aggregator.completion.chapter.json | 30 ++++++++ ...mpletion_aggregator.completion.course.json | 34 +++++++++ ...tion_aggregator.completion.sequential.json | 30 ++++++++ ...letion_aggregator.completion.vertical.json | 30 ++++++++ ...ompletion_aggregator.progress.chapter.json | 30 ++++++++ ...completion_aggregator.progress.course.json | 34 +++++++++ ...letion_aggregator.progress.sequential.json | 30 ++++++++ ...mpletion_aggregator.progress.vertical.json | 30 ++++++++ tests/test_transformers.py | 75 +++++++++++++++++++ 26 files changed, 739 insertions(+), 2 deletions(-) create mode 100644 test_output/.gitkeep create mode 100644 tests/fixtures/expected/openedx.completion_aggregator.completion.chapter.json create mode 100644 tests/fixtures/expected/openedx.completion_aggregator.completion.course.json create mode 100644 tests/fixtures/expected/openedx.completion_aggregator.completion.sequential.json create mode 100644 tests/fixtures/expected/openedx.completion_aggregator.completion.vertical.json create mode 100644 tests/fixtures/expected/openedx.completion_aggregator.progress.chapter.json create mode 100644 tests/fixtures/expected/openedx.completion_aggregator.progress.course.json create mode 100644 tests/fixtures/expected/openedx.completion_aggregator.progress.sequential.json create mode 100644 tests/fixtures/expected/openedx.completion_aggregator.progress.vertical.json create mode 100644 tests/fixtures/raw/openedx.completion_aggregator.completion.chapter.json create mode 100644 tests/fixtures/raw/openedx.completion_aggregator.completion.course.json create mode 100644 tests/fixtures/raw/openedx.completion_aggregator.completion.sequential.json create mode 100644 tests/fixtures/raw/openedx.completion_aggregator.completion.vertical.json create mode 100644 tests/fixtures/raw/openedx.completion_aggregator.progress.chapter.json create mode 100644 tests/fixtures/raw/openedx.completion_aggregator.progress.course.json create mode 100644 tests/fixtures/raw/openedx.completion_aggregator.progress.sequential.json create mode 100644 tests/fixtures/raw/openedx.completion_aggregator.progress.vertical.json create mode 100644 tests/test_transformers.py diff --git a/.gitignore b/.gitignore index b7b99cc3..7b5132ee 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ pip-log.txt .tox coverage.xml htmlcov/ +test_output/*.json # Translations *.mo diff --git a/completion_aggregator/transformers.py b/completion_aggregator/transformers.py index 3dcadc52..7d51f9c6 100644 --- a/completion_aggregator/transformers.py +++ b/completion_aggregator/transformers.py @@ -98,7 +98,7 @@ def get_object(self) -> Activity: Get object for xAPI transformed event. """ if not self.object_type: - raise NotImplementedError() + raise NotImplementedError() # pragma: no cover return Activity( id=self.get_object_iri("xblock", self.get_data("data.block_id")), diff --git a/requirements/dev.txt b/requirements/dev.txt index e18e60a1..7fcd4df0 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -241,6 +241,12 @@ event-tracking==2.4.0 # -r requirements/quality.txt # edx-completion # edx-event-routing-backends +factory-boy==3.3.0 + # via -r requirements/quality.txt +faker==25.8.0 + # via + # -r requirements/quality.txt + # factory-boy fastavro==1.9.4 # via # -r requirements/quality.txt @@ -468,6 +474,7 @@ python-dateutil==2.8.2 # botocore # celery # edx-event-routing-backends + # faker # freezegun # xblock python-slugify==8.0.4 diff --git a/requirements/doc.txt b/requirements/doc.txt index 8382dc9d..e7d494f9 100644 --- a/requirements/doc.txt +++ b/requirements/doc.txt @@ -264,6 +264,12 @@ event-tracking==2.4.0 # -r requirements/test.txt # edx-completion # edx-event-routing-backends +factory-boy==3.3.0 + # via -r requirements/test.txt +faker==25.8.0 + # via + # -r requirements/test.txt + # factory-boy fastavro==1.9.4 # via # -r requirements/base.txt @@ -465,6 +471,7 @@ python-dateutil==2.8.2 # botocore # celery # edx-event-routing-backends + # faker # freezegun # xblock python-slugify==8.0.4 diff --git a/requirements/quality.txt b/requirements/quality.txt index 6dcff4a4..92723dc9 100644 --- a/requirements/quality.txt +++ b/requirements/quality.txt @@ -226,6 +226,12 @@ event-tracking==2.4.0 # -r requirements/test.txt # edx-completion # edx-event-routing-backends +factory-boy==3.3.0 + # via -r requirements/test.txt +faker==25.8.0 + # via + # -r requirements/test.txt + # factory-boy fastavro==1.9.4 # via # -r requirements/test.txt @@ -416,6 +422,7 @@ python-dateutil==2.8.2 # botocore # celery # edx-event-routing-backends + # faker # freezegun # xblock python-slugify==8.0.4 diff --git a/requirements/test.in b/requirements/test.in index 6371b860..2bc7a2d2 100644 --- a/requirements/test.in +++ b/requirements/test.in @@ -16,3 +16,4 @@ django-oauth-toolkit mysqlclient # For connecting to MySQL edx-i18n-tools # For i18n_tool dummy +factory-boy # For event transformers diff --git a/requirements/test.txt b/requirements/test.txt index fd026e3a..62148a60 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -211,6 +211,10 @@ event-tracking==2.4.0 # -r requirements/base.txt # edx-completion # edx-event-routing-backends +factory-boy==3.3.0 + # via -r requirements/test.in +faker==25.8.0 + # via factory-boy fastavro==1.9.4 # via # -r requirements/base.txt @@ -362,6 +366,7 @@ python-dateutil==2.8.2 # botocore # celery # edx-event-routing-backends + # faker # freezegun # xblock python-slugify==8.0.4 diff --git a/test_output/.gitkeep b/test_output/.gitkeep new file mode 100644 index 00000000..91cf7fb0 --- /dev/null +++ b/test_output/.gitkeep @@ -0,0 +1 @@ +# Output dir for failed transformer tests diff --git a/test_settings.py b/test_settings.py index 0e60b81e..63d75345 100644 --- a/test_settings.py +++ b/test_settings.py @@ -101,7 +101,8 @@ def root(*args): EVENT_TRACKING_BACKENDS = {} EVENT_TRACKING_BACKENDS_ALLOWED_XAPI_EVENTS = [] EVENT_TRACKING_BACKENDS_ALLOWED_CALIPER_EVENTS = [] -LMS_ROOT_URL = "http://lms.url" +LMS_ROOT_URL = "http://localhost:18000" +RUNNING_WITH_TEST_SETTINGS = True # pylint: disable=unused-import,wrong-import-position from test_utils.test_app import celery # isort:skip diff --git a/tests/fixtures/expected/openedx.completion_aggregator.completion.chapter.json b/tests/fixtures/expected/openedx.completion_aggregator.completion.chapter.json new file mode 100644 index 00000000..eeb4de61 --- /dev/null +++ b/tests/fixtures/expected/openedx.completion_aggregator.completion.chapter.json @@ -0,0 +1,45 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"484fe8d7-7a5b-52ff-a0ab-3d3d8c1a8b27", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@chapter+block@b443e0d6bc4d43c1bed991dbd8a10d42", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/completed", + "display":{ + "en":"completed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/tests/fixtures/expected/openedx.completion_aggregator.completion.course.json b/tests/fixtures/expected/openedx.completion_aggregator.completion.course.json new file mode 100644 index 00000000..a6931c7a --- /dev/null +++ b/tests/fixtures/expected/openedx.completion_aggregator.completion.course.json @@ -0,0 +1,45 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"484fe8d7-7a5b-52ff-a0ab-3d3d8c1a8b27", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@course+block@course", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/course" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/completed", + "display":{ + "en":"completed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/tests/fixtures/expected/openedx.completion_aggregator.completion.sequential.json b/tests/fixtures/expected/openedx.completion_aggregator.completion.sequential.json new file mode 100644 index 00000000..d54194b9 --- /dev/null +++ b/tests/fixtures/expected/openedx.completion_aggregator.completion.sequential.json @@ -0,0 +1,45 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"484fe8d7-7a5b-52ff-a0ab-3d3d8c1a8b27", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@sequential+block@bf1eac10ebb649e3aaf9cc07325f8e04", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/completed", + "display":{ + "en":"completed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/tests/fixtures/expected/openedx.completion_aggregator.completion.vertical.json b/tests/fixtures/expected/openedx.completion_aggregator.completion.vertical.json new file mode 100644 index 00000000..97cfccde --- /dev/null +++ b/tests/fixtures/expected/openedx.completion_aggregator.completion.vertical.json @@ -0,0 +1,45 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"484fe8d7-7a5b-52ff-a0ab-3d3d8c1a8b27", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@vertical+block@e1fabd9fa55f441caa75580f258ffbc3", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/completed", + "display":{ + "en":"completed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/tests/fixtures/expected/openedx.completion_aggregator.progress.chapter.json b/tests/fixtures/expected/openedx.completion_aggregator.progress.chapter.json new file mode 100644 index 00000000..4a575518 --- /dev/null +++ b/tests/fixtures/expected/openedx.completion_aggregator.progress.chapter.json @@ -0,0 +1,51 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"146d5372-1d64-54b1-8c60-b4acaad3c976", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@chapter+block@b443e0d6bc4d43c1bed991dbd8a10d42", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/progressed", + "display":{ + "en":"progressed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "result":{ + "score":{ + "scaled":0.5 + }, + "completion":false + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/tests/fixtures/expected/openedx.completion_aggregator.progress.course.json b/tests/fixtures/expected/openedx.completion_aggregator.progress.course.json new file mode 100644 index 00000000..e2f58dfe --- /dev/null +++ b/tests/fixtures/expected/openedx.completion_aggregator.progress.course.json @@ -0,0 +1,51 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"146d5372-1d64-54b1-8c60-b4acaad3c976", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@course+block@course", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/course" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/progressed", + "display":{ + "en":"progressed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "result":{ + "score":{ + "scaled":0.8 + }, + "completion":false + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/tests/fixtures/expected/openedx.completion_aggregator.progress.sequential.json b/tests/fixtures/expected/openedx.completion_aggregator.progress.sequential.json new file mode 100644 index 00000000..fbe78f6b --- /dev/null +++ b/tests/fixtures/expected/openedx.completion_aggregator.progress.sequential.json @@ -0,0 +1,51 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"146d5372-1d64-54b1-8c60-b4acaad3c976", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@sequential+block@bf1eac10ebb649e3aaf9cc07325f8e04", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/progressed", + "display":{ + "en":"progressed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "result":{ + "score":{ + "scaled":0.6 + }, + "completion":false + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/tests/fixtures/expected/openedx.completion_aggregator.progress.vertical.json b/tests/fixtures/expected/openedx.completion_aggregator.progress.vertical.json new file mode 100644 index 00000000..c44a4d7c --- /dev/null +++ b/tests/fixtures/expected/openedx.completion_aggregator.progress.vertical.json @@ -0,0 +1,51 @@ +{ + "actor": { + "objectType": "Agent", + "account": { + "name": "32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb", + "homePage": "http://localhost:18000" + } + }, + "id":"146d5372-1d64-54b1-8c60-b4acaad3c976", + "object":{ + "id":"http://localhost:18000/xblock/block-v1:edX+DemoX+Demo_Course+type@vertical+block@e1fabd9fa55f441caa75580f258ffbc3", + "definition":{ + "type":"http://adlnet.gov/expapi/activities/module" + }, + "objectType":"Activity" + }, + "verb":{ + "id":"http://adlnet.gov/expapi/verbs/progressed", + "display":{ + "en":"progressed" + } + }, + "version":"1.0.3", + "context":{ + "contextActivities":{ + "parent":[ + { + "id":"http://localhost:18000/course/course-v1:edX+DemoX+Demo_Course", + "objectType":"Activity", + "definition":{ + "name":{ + "en-US":"Demonstration Course" + }, + "type":"http://adlnet.gov/expapi/activities/course" + } + } + ] + }, + "extensions":{ + "https://w3id.org/xapi/openedx/extension/transformer-version":"event-routing-backends@1.1.1", + "https://w3id.org/xapi/openedx/extensions/session-id":"056aca2a1c6b76742b283e73d3424453" + } + }, + "result":{ + "score":{ + "scaled":1.0 + }, + "completion":true + }, + "timestamp":"2023-12-05T21:34:52.909063+00:00" +} diff --git a/tests/fixtures/raw/openedx.completion_aggregator.completion.chapter.json b/tests/fixtures/raw/openedx.completion_aggregator.completion.chapter.json new file mode 100644 index 00000000..16a0b214 --- /dev/null +++ b/tests/fixtures/raw/openedx.completion_aggregator.completion.chapter.json @@ -0,0 +1,30 @@ +{ + "name": "openedx.completion_aggregator.completion.chapter", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@chapter+block@b443e0d6bc4d43c1bed991dbd8a10d42", + "block_type": "course", + "percent": 0.5, + "earned": 5, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "edx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/tests/fixtures/raw/openedx.completion_aggregator.completion.course.json b/tests/fixtures/raw/openedx.completion_aggregator.completion.course.json new file mode 100644 index 00000000..9be7737f --- /dev/null +++ b/tests/fixtures/raw/openedx.completion_aggregator.completion.course.json @@ -0,0 +1,34 @@ +{ + "name": "openedx.completion_aggregator.completion.course", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@course+block@course", + "block_type": "course", + "percent": 0.8, + "earned": 8, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "openedx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "", + "module": { + "display_name": "Checkboxes", + "usage_key": "block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175" + } + } +} diff --git a/tests/fixtures/raw/openedx.completion_aggregator.completion.sequential.json b/tests/fixtures/raw/openedx.completion_aggregator.completion.sequential.json new file mode 100644 index 00000000..ba4ec543 --- /dev/null +++ b/tests/fixtures/raw/openedx.completion_aggregator.completion.sequential.json @@ -0,0 +1,30 @@ +{ + "name": "openedx.completion_aggregator.completion.sequential", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@bf1eac10ebb649e3aaf9cc07325f8e04", + "block_type": "course", + "percent": 0.6, + "earned": 6, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "openedx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/tests/fixtures/raw/openedx.completion_aggregator.completion.vertical.json b/tests/fixtures/raw/openedx.completion_aggregator.completion.vertical.json new file mode 100644 index 00000000..e179db5e --- /dev/null +++ b/tests/fixtures/raw/openedx.completion_aggregator.completion.vertical.json @@ -0,0 +1,30 @@ +{ + "name": "openedx.completion_aggregator.completion.vertical", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@e1fabd9fa55f441caa75580f258ffbc3", + "block_type": "course", + "percent": 1, + "earned": 10, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "openedx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/tests/fixtures/raw/openedx.completion_aggregator.progress.chapter.json b/tests/fixtures/raw/openedx.completion_aggregator.progress.chapter.json new file mode 100644 index 00000000..007e09a7 --- /dev/null +++ b/tests/fixtures/raw/openedx.completion_aggregator.progress.chapter.json @@ -0,0 +1,30 @@ +{ + "name": "openedx.completion_aggregator.progress.chapter", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@chapter+block@b443e0d6bc4d43c1bed991dbd8a10d42", + "block_type": "course", + "percent": 0.5, + "earned": 5, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "openedx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/tests/fixtures/raw/openedx.completion_aggregator.progress.course.json b/tests/fixtures/raw/openedx.completion_aggregator.progress.course.json new file mode 100644 index 00000000..dbd5ece4 --- /dev/null +++ b/tests/fixtures/raw/openedx.completion_aggregator.progress.course.json @@ -0,0 +1,34 @@ +{ + "name": "openedx.completion_aggregator.progress.course", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@course+block@course", + "block_type": "course", + "percent": 0.8, + "earned": 8, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "openedx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "", + "module": { + "display_name": "Checkboxes", + "usage_key": "block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175" + } + } +} diff --git a/tests/fixtures/raw/openedx.completion_aggregator.progress.sequential.json b/tests/fixtures/raw/openedx.completion_aggregator.progress.sequential.json new file mode 100644 index 00000000..949f44aa --- /dev/null +++ b/tests/fixtures/raw/openedx.completion_aggregator.progress.sequential.json @@ -0,0 +1,30 @@ +{ + "name": "openedx.completion_aggregator.progress.sequential", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@bf1eac10ebb649e3aaf9cc07325f8e04", + "block_type": "course", + "percent": 0.6, + "earned": 6, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "openedx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/tests/fixtures/raw/openedx.completion_aggregator.progress.vertical.json b/tests/fixtures/raw/openedx.completion_aggregator.progress.vertical.json new file mode 100644 index 00000000..3d8d8a49 --- /dev/null +++ b/tests/fixtures/raw/openedx.completion_aggregator.progress.vertical.json @@ -0,0 +1,30 @@ +{ + "name": "openedx.completion_aggregator.progress.vertical", + "timestamp": "2023-12-05T21:34:52.909063+00:00", + "data": { + "user_id": 4, + "course_id": "course-v1:edX+DemoX+Demo_Course", + "context_key": "course-v1:edX+DemoX+Demo_Course", + "block_id": "block-v1:edX+DemoX+Demo_Course+type@vertical+block@e1fabd9fa55f441caa75580f258ffbc3", + "block_type": "course", + "percent": 1, + "earned": 10, + "possible": 10 + }, + "context": { + "course_id": "course-v1:edX+DemoX+Demo_Course", + "course_user_tags": {}, + "session": "056aca2a1c6b76742b283e73d3424453", + "user_id": 3, + "username": "openedx", + "ip": "172.18.0.1", + "host": "localhost:18000", + "agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36", + "path": "/courses/course-v1:edX+DemoX+Demo_Course/xblock/block-v1:edX+DemoX+Demo_Course+type@problem+block@7c54b16c8ed34f9f8772015178c7a175/handler/xmodule_handler/problem_check", + "referer": "http://localhost:18000/courses/course-v1:edX+DemoX+Demo_Course+type@vertical+block@dd8110c941b94d929b56841195213797?show_title=0&show_bookmark_button=0&recheck_access=1&view=student_view", + "accept_language": "en-US,en;q=0.9,es;q=0.8", + "client_id": null, + "org_id": "edX", + "enterprise_uuid": "" + } +} diff --git a/tests/test_transformers.py b/tests/test_transformers.py new file mode 100644 index 00000000..7a425baf --- /dev/null +++ b/tests/test_transformers.py @@ -0,0 +1,75 @@ +""" +Test the completion aggregator transformers. +""" +import os +from unittest.mock import patch +from uuid import UUID + +import ddt +from event_routing_backends.processors.xapi.tests.test_transformers import XApiTransformersFixturesTestMixin +from event_routing_backends.settings import common as erb_settings + +from django.conf import settings +from django.test import TestCase + +from completion_aggregator.settings import common as common_settings + + +@ddt.ddt +class TestXApiTransformers(XApiTransformersFixturesTestMixin, TestCase): + """ + Test xApi event transforms and settings. + """ + TEST_DIR_PATH = os.path.dirname(os.path.abspath(__file__)) + + EVENT_FIXTURE_FILENAMES = [ + event_file_name for event_file_name in os.listdir( + f'{TEST_DIR_PATH}/fixtures/raw/' + ) if event_file_name.endswith(".json") + ] + + @property + def raw_events_fixture_path(self): + """ + Return the path to the expected transformed events fixture files. + """ + return f'{self.TEST_DIR_PATH}/fixtures/raw' + + @property + def expected_events_fixture_path(self): + """ + Return the path to the expected transformed events fixture files. + """ + return f'{self.TEST_DIR_PATH}/fixtures/expected' + + def setUp(self): + """ + Initialize the plugin settings. + """ + erb_settings.plugin_settings(settings) + common_settings.plugin_settings(settings) + + super().setUp() + + @patch('event_routing_backends.processors.xapi.transformer.get_anonymous_user_id') + @patch('event_routing_backends.processors.xapi.transformer.get_course_from_id') + @ddt.data(*EVENT_FIXTURE_FILENAMES) + def test_event_transformer(self, raw_event_file_path, mock_get_course_from_id, mock_get_anonymous_user_id): + # Generates the anonymized actor.name, + mock_get_anonymous_user_id.return_value = UUID('32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb') + + # Generates the contextActivities + mock_get_course_from_id.return_value = { + "display_name": "Demonstration Course", + "id": "course-v1:edX+DemoX+Demo_Course", + } + + # if an event's expected fixture doesn't exist, the test shouldn't fail. + # evaluate transformation of only supported event fixtures. + base_event_filename = os.path.basename(raw_event_file_path) + + expected_event_file_path = f'{self.expected_events_fixture_path}/{base_event_filename}' + + assert os.path.isfile(expected_event_file_path) + + self.check_event_transformer(raw_event_file_path, expected_event_file_path)