Skip to content

Commit effffbb

Browse files
feat: add necessary tooling for Open edX events
- Add signal class used to create events - Add metadata generator to class - Add argument validator to class - Override signal methods to recommend using the custom send_event
1 parent 9842f9d commit effffbb

File tree

13 files changed

+537
-99
lines changed

13 files changed

+537
-99
lines changed

openedx_events/exceptions.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"""
2+
Custom exceptions thrown by Open edX events tooling.
3+
"""
4+
5+
6+
class HookEventException(Exception):
7+
"""
8+
Base class for Open edX Events exceptions.
9+
"""
10+
11+
def __init__(self, message=""):
12+
"""
13+
Init method for HookEventException base class.
14+
15+
Arguments:
16+
message (str): message describing why the exception was raised.
17+
"""
18+
super().__init__()
19+
self.message = message
20+
21+
def __str__(self):
22+
"""
23+
Show string representation of HookEventException using its message.
24+
"""
25+
return self.message
26+
27+
28+
class InstantiationError(HookEventException):
29+
"""
30+
Describes errors that occur while instantiating events.
31+
32+
This exception is raised when there's an error instantiating an Open edX
33+
event, it can be that a required argument for the event definition is
34+
missing.
35+
"""
36+
37+
def __init__(self, event_type="", message=""):
38+
"""
39+
Init method for InstantiationError custom exception class.
40+
41+
Arguments:
42+
event_type (str): name of the event raising the exception.
43+
message (str): message describing why the exception was raised.
44+
"""
45+
super().__init__(
46+
message="InstantiationError {event_type}: {message}".format(
47+
event_type=event_type, message=message
48+
)
49+
)
50+
51+
52+
class SenderValidationError(HookEventException):
53+
"""
54+
Describes errors that occur while validating arguments of send methods.
55+
"""
56+
57+
def __init__(self, event_type="", message=""):
58+
"""
59+
Init method for SenderValidationError custom exception class.
60+
61+
Arguments:
62+
event_type (str): name of the event raising the exception.
63+
message (str): message describing why the exception was raised.
64+
"""
65+
super().__init__(
66+
message="SenderValidationError {event_type}: {message}".format(
67+
event_type=event_type, message=message
68+
)
69+
)
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
"""This file contains all test for the tooling.py file.
2+
3+
Classes:
4+
EventsToolingTest: Test events tooling.
5+
"""
6+
from unittest.mock import Mock, patch
7+
8+
import ddt
9+
from django.test import TestCase, override_settings
10+
11+
from openedx_events.exceptions import InstantiationError, SenderValidationError
12+
from openedx_events.tooling import OpenEdxPublicSignal
13+
14+
15+
@ddt.ddt
16+
class OpenEdxPublicSignalTest(TestCase):
17+
"""
18+
Test cases for Open edX events base class.
19+
"""
20+
21+
def setUp(self):
22+
"""
23+
Setup common conditions for every test case.
24+
"""
25+
super().setUp()
26+
self.event_type = "org.openedx.learning.session.login.completed.v1"
27+
self.data_attr = {
28+
"user": Mock,
29+
}
30+
self.public_signal = OpenEdxPublicSignal(
31+
event_type=self.event_type, data=self.data_attr,
32+
)
33+
34+
def test_string_representation(self):
35+
"""
36+
This methods checks the string representation for events base class.
37+
38+
Expected behavior:
39+
The representation contains the event_type.
40+
"""
41+
self.assertIn(self.event_type, str(self.public_signal))
42+
43+
@override_settings(SERVICE_VARIANT="lms")
44+
@patch("crum.get_current_request")
45+
def test_get_signal_metadata(self, get_request_mock):
46+
"""
47+
This methods tests getting the generated metadata for an event.
48+
49+
Expected behavior:
50+
Returns the metadata containing information about the event.
51+
"""
52+
get_request_mock.return_value.get_host.return_value = "edx.devstack.lms"
53+
expected_metadata = {
54+
"event_type": self.event_type,
55+
"minorversion": "0.0",
56+
"source": "openedx/lms/web",
57+
"sourcehost": "edx.devstack.lms",
58+
}
59+
60+
metadata = self.public_signal.get_signal_metadata()
61+
62+
self.assertDictContainsSubset(expected_metadata, metadata)
63+
64+
@ddt.data(
65+
("", {"user": Mock()}, "event_type"),
66+
("org.openedx.learning.session.login.completed.v1", None, "data"),
67+
)
68+
@ddt.unpack
69+
def test_event_instantiation_exception(
70+
self, event_type, event_data, missing_argument
71+
):
72+
"""
73+
This method tests when an event is instantiated without event_type or
74+
event data.
75+
76+
Expected behavior:
77+
An InstantiationError exception is raised.
78+
"""
79+
exception_message = "InstantiationError {event_type}: Missing required argument '{missing_argument}'".format(
80+
event_type=event_type,
81+
missing_argument=missing_argument
82+
)
83+
84+
with self.assertRaisesMessage(InstantiationError, exception_message):
85+
OpenEdxPublicSignal(event_type=event_type, data=event_data)
86+
87+
@patch("openedx_events.tooling.Signal.send")
88+
def test_send_event_successfully(self, send_mock):
89+
"""
90+
This method tests the process of sending an event.
91+
92+
Expected behavior:
93+
The event is sent as a django signal.
94+
"""
95+
self.public_signal.send_event(send_robust=False, user=Mock())
96+
97+
send_mock.assert_called()
98+
99+
@patch("openedx_events.tooling.Signal.send_robust")
100+
def test_send_robust_event_successfully(self, send_robust_mock):
101+
"""
102+
This method tests the process of sending an event.
103+
104+
Expected behavior:
105+
The event is sent as a django signal.
106+
"""
107+
self.public_signal.send_event(user=Mock())
108+
109+
send_robust_mock.assert_called()
110+
111+
@ddt.data(
112+
(
113+
{"student": Mock()},
114+
"SenderValidationError org.openedx.learning.session.login.completed.v1: "
115+
"Missing required argument 'user'",
116+
),
117+
(
118+
{"user": {"student": Mock()}},
119+
"SenderValidationError org.openedx.learning.session.login.completed.v1: "
120+
"The argument 'user' is not instance of the Class Attribute 'type'",
121+
),
122+
(
123+
{"student": Mock(), "user": Mock()},
124+
"SenderValidationError org.openedx.learning.session.login.completed.v1: "
125+
"There's a mismatch between initialization data and send_event arguments",
126+
),
127+
)
128+
@ddt.unpack
129+
def test_invalid_sender(self, send_arguments, exception_message):
130+
"""
131+
This method tests sending an event with invalid setup on the sender
132+
side.
133+
134+
Expected behavior:
135+
A SenderValidationError exception is raised.
136+
"""
137+
with self.assertRaisesMessage(SenderValidationError, exception_message):
138+
self.public_signal.send_event(**send_arguments)
139+
140+
def test_send_event_with_django(self):
141+
"""
142+
This method tests sending an event using the `send` built-in Django
143+
method.
144+
145+
Expected behavior:
146+
A warning is showed advicing to use Open edX events custom
147+
send_signal method.
148+
"""
149+
message = "Please, use 'send_event' when triggering an Open edX event."
150+
151+
with self.assertWarns(Warning, msg=message):
152+
self.public_signal.send(sender=Mock())
153+
154+
def test_send_robust_event_with_django(self):
155+
"""
156+
This method tests sending an event using the `send` built-in Django
157+
method.
158+
159+
Expected behavior:
160+
A warning is showed advicing to use Open edX events custom
161+
send_signal method.
162+
"""
163+
message = "Please, use 'send_event' with send_robust equals to True when triggering an Open edX event."
164+
165+
with self.assertWarns(Warning, msg=message):
166+
self.public_signal.send_robust(sender=Mock())

0 commit comments

Comments
 (0)