-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow event-routing-backends to be used when transforming non-openedx…
… events (#431) * fix: catch ImportErrors so that event-routing-backends can be dependency for non-edx-platform repos like openedx-completion-aggregator. * feat: adds utils.settings.event_tracking_backends_config() * Pulls the EVENT_TRACKING_BACKENDS configuration template into a utility method so it can be used by other apps. * Pulls allowed xAPI and Caliper events into settings to preserve changes * fix: event_tracking_backends_config must use the provided plugin settings instead of global django.conf.settings * refactor: creates fixture test mixins that can be used outside of ERB Refactors the transformer test classes to split the test functionality into two: * TransformersFixturesTestMixin -- for running the event fixture tests * TransformersTestMixin -- for running the ERB-specific event tests The fixtures file path constants have been moved to property methods so they can be overrideen when used outside of ERB. * fix: ensure plugin loading order doesn't matter for event routing settings Initialize EVENT_TRACKING_BACKENDS_ALLOWED_XAPI_EVENTS and EVENT_TRACKING_BACKENDS_ALLOWED_CALIPER_EVENTS only if they aren't already initialized, and append our events to them. This allows other plugins to modify these settings too.
- Loading branch information
1 parent
980b2aa
commit 9ae9c4f
Showing
10 changed files
with
331 additions
and
147 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,4 @@ | |
Various backends for receiving edX LMS events.. | ||
""" | ||
|
||
__version__ = '9.2.1' | ||
__version__ = '9.3.0' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
Mixin for testing transformers for all of the currently supported events | ||
""" | ||
import json | ||
import logging | ||
import os | ||
from abc import abstractmethod | ||
from unittest.mock import patch | ||
|
@@ -16,101 +17,89 @@ | |
from event_routing_backends.processors.mixins.base_transformer import BaseTransformerMixin | ||
from event_routing_backends.tests.factories import UserFactory | ||
|
||
logger = logging.getLogger(__name__) | ||
User = get_user_model() | ||
|
||
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/current/' | ||
) if event_file_name.endswith(".json") | ||
] | ||
try: | ||
EVENT_FIXTURE_FILENAMES = [ | ||
event_file_name for event_file_name in os.listdir( | ||
f'{TEST_DIR_PATH}/fixtures/current/' | ||
) if event_file_name.endswith(".json") | ||
] | ||
|
||
except FileNotFoundError as exc: # pragma: no cover | ||
# This exception may happen when these test mixins are used outside of the ERB package. | ||
logger.exception(exc) | ||
EVENT_FIXTURE_FILENAMES = [] | ||
|
||
|
||
class DummyTransformer(BaseTransformerMixin): | ||
required_fields = ('does_not_exist',) | ||
|
||
|
||
@ddt.ddt | ||
class TransformersTestMixin: | ||
class TransformersFixturesTestMixin: | ||
""" | ||
Test that supported events are transformed correctly. | ||
Mixin to help test event transforms using "raw" and "expected" fixture data. | ||
""" | ||
# no limit to diff in the output of tests | ||
maxDiff = None | ||
|
||
registry = None | ||
EXCEPTED_EVENTS_FIXTURES_PATH = None | ||
|
||
def setUp(self): | ||
super().setUp() | ||
UserFactory.create(username='edx', email='[email protected]') | ||
|
||
@property | ||
def raw_events_fixture_path(self): | ||
""" | ||
Return the path to the raw events fixture files. | ||
""" | ||
return f"{TEST_DIR_PATH}/fixtures/current" | ||
|
||
@property | ||
def expected_events_fixture_path(self): | ||
""" | ||
Return the path to the expected transformed events fixture files. | ||
""" | ||
raise NotImplementedError | ||
|
||
def get_raw_event(self, event_filename): | ||
""" | ||
Return raw event json parsed from current fixtures | ||
""" | ||
base_event_filename = os.path.basename(event_filename) | ||
|
||
input_event_file_path = '{test_dir}/fixtures/current/{event_filename}'.format( | ||
test_dir=TEST_DIR_PATH, event_filename=event_filename | ||
input_event_file_path = '{test_dir}/{event_filename}'.format( | ||
test_dir=self.raw_events_fixture_path, event_filename=base_event_filename | ||
) | ||
with open(input_event_file_path, encoding='utf-8') as current: | ||
data = json.loads(current.read()) | ||
return data | ||
|
||
@override_settings(RUNNING_WITH_TEST_SETTINGS=True) | ||
def test_transformer_version_with_test_settings(self): | ||
self.registry.register('test_event')(DummyTransformer) | ||
raw_event = self.get_raw_event('edx.course.enrollment.activated.json') | ||
transformed_event = self.registry.get_transformer(raw_event).transform() | ||
self.assert_correct_transformer_version(transformed_event, '[email protected]') | ||
|
||
@override_settings(RUNNING_WITH_TEST_SETTINGS=False) | ||
def test_transformer_version(self): | ||
self.registry.register('test_event')(DummyTransformer) | ||
raw_event = self.get_raw_event('edx.course.enrollment.activated.json') | ||
transformed_event = self.registry.get_transformer(raw_event).transform() | ||
self.assert_correct_transformer_version(transformed_event, 'event-routing-backends@{}'.format(__version__)) | ||
|
||
def test_with_no_field_transformer(self): | ||
self.registry.register('test_event')(DummyTransformer) | ||
with self.assertRaises(ValueError): | ||
self.registry.get_transformer({ | ||
'name': 'test_event' | ||
}).transform() | ||
|
||
def test_required_field_transformer(self): | ||
self.registry.register('test_event')(DummyTransformer) | ||
with self.assertRaises(ValueError): | ||
self.registry.get_transformer({ | ||
"name": "edx.course.enrollment.activated" | ||
}).transform() | ||
|
||
@abstractmethod | ||
def compare_events(self, transformed_event, expected_event): | ||
""" | ||
Every transformer's test case will implement its own logic to test | ||
events transformation | ||
""" | ||
@patch('event_routing_backends.helpers.uuid.uuid4') | ||
@ddt.data(*EVENT_FIXTURE_FILENAMES) | ||
def test_event_transformer(self, event_filename, mocked_uuid4): | ||
# Used to generate the anonymized actor.name, | ||
# which in turn is used to generate the event UUID. | ||
mocked_uuid4.return_value = UUID('32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb') | ||
raise NotImplementedError | ||
|
||
# if an event's expected fixture doesn't exist, the test shouldn't fail. | ||
# evaluate transformation of only supported event fixtures. | ||
expected_event_file_path = '{expected_events_fixtures_path}/{event_filename}'.format( | ||
expected_events_fixtures_path=self.EXCEPTED_EVENTS_FIXTURES_PATH, event_filename=event_filename | ||
) | ||
def check_event_transformer(self, raw_event_file, expected_event_file): | ||
""" | ||
Test that given event is transformed correctly. | ||
if not os.path.isfile(expected_event_file_path): | ||
return | ||
Transforms the contents of `raw_event_file` and compare it against the contents of `expected_event_file`. | ||
original_event = self.get_raw_event(event_filename) | ||
with open(expected_event_file_path, encoding='utf-8') as expected: | ||
Writes errors to test_out/ for analysis. | ||
""" | ||
original_event = self.get_raw_event(raw_event_file) | ||
with open(expected_event_file, encoding='utf-8') as expected: | ||
expected_event = json.loads(expected.read()) | ||
|
||
event_filename = os.path.basename(raw_event_file) | ||
if "anonymous" in event_filename: | ||
with pytest.raises(ValueError): | ||
self.registry.get_transformer(original_event).transform() | ||
|
@@ -132,7 +121,61 @@ def test_event_transformer(self, event_filename, mocked_uuid4): | |
actual_transformed_event_file.write(",".join(out_events)) | ||
actual_transformed_event_file.write("]") | ||
|
||
with open(f"test_output/expected.{event_filename}.json", "w") as expected_event_file: | ||
json.dump(expected_event, expected_event_file, indent=4) | ||
with open(f"test_output/expected.{raw_event_file}.json", "w") as test_output_file: | ||
json.dump(expected_event, test_output_file, indent=4) | ||
|
||
raise e | ||
|
||
|
||
@ddt.ddt | ||
class TransformersTestMixin: | ||
""" | ||
Tests that supported events are transformed correctly. | ||
""" | ||
def test_with_no_field_transformer(self): | ||
self.registry.register('test_event')(DummyTransformer) | ||
with self.assertRaises(ValueError): | ||
self.registry.get_transformer({ | ||
'name': 'test_event' | ||
}).transform() | ||
|
||
def test_required_field_transformer(self): | ||
self.registry.register('test_event')(DummyTransformer) | ||
with self.assertRaises(ValueError): | ||
self.registry.get_transformer({ | ||
"name": "edx.course.enrollment.activated" | ||
}).transform() | ||
|
||
@override_settings(RUNNING_WITH_TEST_SETTINGS=True) | ||
def test_transformer_version_with_test_settings(self): | ||
self.registry.register('test_event')(DummyTransformer) | ||
raw_event = self.get_raw_event('edx.course.enrollment.activated.json') | ||
transformed_event = self.registry.get_transformer(raw_event).transform() | ||
self.assert_correct_transformer_version(transformed_event, '[email protected]') | ||
|
||
@override_settings(RUNNING_WITH_TEST_SETTINGS=False) | ||
def test_transformer_version(self): | ||
self.registry.register('test_event')(DummyTransformer) | ||
raw_event = self.get_raw_event('edx.course.enrollment.activated.json') | ||
transformed_event = self.registry.get_transformer(raw_event).transform() | ||
self.assert_correct_transformer_version(transformed_event, 'event-routing-backends@{}'.format(__version__)) | ||
|
||
@patch('event_routing_backends.helpers.uuid.uuid4') | ||
@ddt.data(*EVENT_FIXTURE_FILENAMES) | ||
def test_event_transformer(self, raw_event_file_path, mocked_uuid4): | ||
# Used to generate the anonymized actor.name, | ||
# which in turn is used to generate the event UUID. | ||
mocked_uuid4.return_value = UUID('32e08e30-f8ae-4ce2-94a8-c2bfe38a70cb') | ||
|
||
# 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 = '{expected_events_fixture_path}/{event_filename}'.format( | ||
expected_events_fixture_path=self.expected_events_fixture_path, event_filename=base_event_filename | ||
) | ||
|
||
if not os.path.isfile(expected_event_file_path): | ||
return | ||
|
||
self.check_event_transformer(raw_event_file_path, expected_event_file_path) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.