Skip to content

Commit a3f415b

Browse files
feanilclaude
andauthored
fix: restore all events in OpenEdxEventsTestMixin.tearDownClass (#559)
* fix: restore all events in OpenEdxEventsTestMixin.tearDownClass OpenEdxEventsTestMixin.setUpClass() disables all OpenEdX events then enables only those listed in ENABLED_OPENEDX_EVENTS, but had no corresponding tearDownClass to restore event state. This caused any events left disabled to remain disabled for subsequent test classes running in the same process. Add tearDownClass to re-enable all events so that test classes using this mixin do not leak disabled event state into other tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test: add tests for OpenEdxEventsTestMixin.tearDownClass event restoration Verify that tearDownClass re-enables all events so subsequent test classes running in the same process are not affected by the mixin's event isolation — the regression that motivated the tearDownClass fix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor: move test utilities to openedx_events.testing openedx_events/tests/utils.py was excluded from the installed package by the exclude=["*tests"] in setup.py (introduced in #557), breaking downstream consumers that import from openedx_events.tests.utils. Move the public test utilities to openedx_events/testing.py so they are included in the installed package. New import paths: from openedx_events.testing import FreezeSignalCacheMixin from openedx_events.testing import EventsIsolationMixin from openedx_events.testing import OpenEdxEventsTestMixin Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b0531c2 commit a3f415b

9 files changed

Lines changed: 107 additions & 9 deletions

File tree

CHANGELOG.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ Unreleased
1717
__________
1818

1919

20+
[11.1.1] - 2026-04-06
21+
---------------------
22+
23+
Fixed
24+
~~~~~
25+
26+
* ``OpenEdxEventsTestMixin`` now re-enables all events in ``tearDownClass`` to
27+
prevent disabled event state from leaking into subsequent test classes running
28+
in the same process.
29+
2030
[11.1.0] - 2026-03-19
2131
---------------------
2232

openedx_events/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
more information about the project.
66
"""
77

8-
__version__ = "11.1.0"
8+
__version__ = "11.1.1"

openedx_events/event_bus/avro/tests/test_avro.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
SubTestData1,
2929
create_simple_signal,
3030
)
31-
from openedx_events.tests.utils import FreezeSignalCacheMixin
31+
from openedx_events.testing import FreezeSignalCacheMixin
3232
from openedx_events.tooling import KNOWN_UNSERIALIZABLE_SIGNALS, OpenEdxPublicSignal, load_all_signals
3333

3434

openedx_events/event_bus/avro/tests/test_deserializer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
SubTestData1,
2424
create_simple_signal,
2525
)
26-
from openedx_events.tests.utils import FreezeSignalCacheMixin
26+
from openedx_events.testing import FreezeSignalCacheMixin
2727

2828

2929
@ddt.ddt

openedx_events/event_bus/avro/tests/test_serializer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
SubTestData1,
2424
create_simple_signal,
2525
)
26-
from openedx_events.tests.utils import FreezeSignalCacheMixin
26+
from openedx_events.testing import FreezeSignalCacheMixin
2727

2828

2929
class TestAvroSignalSerializerCache(FreezeSignalCacheMixin, TestCase):
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""
2-
Utils used by Open edX event tests.
2+
Test utilities for use by consumers of openedx-events.
3+
4+
Provides mixins that help isolate Open edX event state between test classes.
35
"""
46

57
from openedx_events.tooling import OpenEdxPublicSignal
@@ -100,11 +102,11 @@ class OpenEdxEventsTestMixin(EventsIsolationMixin):
100102
101103
Example usage:
102104
103-
class MyTestCase(TestCase, OpenEdxEventsTestCase):
105+
class MyTestCase(TestCase, OpenEdxEventsTestMixin):
104106
105107
ENABLED_OPENEDX_EVENTS = ['org.openedx.learning.student.registration.completed.v1']
106108
107-
This class assumes that's it's being used in conjunction TestCase or TestCase subclasses.
109+
This class assumes that it's being used in conjunction with TestCase or TestCase subclasses.
108110
"""
109111

110112
ENABLED_OPENEDX_EVENTS = []
@@ -117,6 +119,17 @@ def setUpClass(cls):
117119
super().setUpClass()
118120
cls().start_events_isolation()
119121

122+
@classmethod
123+
def tearDownClass(cls):
124+
"""
125+
Re-enable all events after class teardown.
126+
127+
Restores event state so subsequent test classes running in the same
128+
process are not affected by this class's event isolation.
129+
"""
130+
cls().enable_all_events()
131+
super().tearDownClass()
132+
120133
@classmethod
121134
def start_events_isolation(cls):
122135
"""

openedx_events/tests/test_generate_avro_schemas.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from openedx_events.event_bus.avro.tests.test_utilities import create_simple_signal
1010
from openedx_events.management.commands.generate_avro_schemas import Command
11-
from openedx_events.tests.utils import FreezeSignalCacheMixin
11+
from openedx_events.testing import FreezeSignalCacheMixin
1212
from openedx_events.tooling import KNOWN_UNSERIALIZABLE_SIGNALS, OpenEdxPublicSignal, load_all_signals
1313

1414

openedx_events/tests/test_tooling.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from openedx_events.data import EventsMetadata
1919
from openedx_events.exceptions import SenderValidationError
20-
from openedx_events.tests.utils import FreezeSignalCacheMixin
20+
from openedx_events.testing import FreezeSignalCacheMixin
2121
from openedx_events.tooling import OpenEdxPublicSignal, _process_all_signals_modules, load_all_signals
2222

2323

openedx_events/tests/test_utils.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
Tests for openedx_events/testing.py.
3+
"""
4+
from django.test import TestCase
5+
6+
from openedx_events.testing import FreezeSignalCacheMixin, OpenEdxEventsTestMixin
7+
from openedx_events.tooling import OpenEdxPublicSignal, load_all_signals
8+
9+
10+
class OpenEdxEventsTestMixinIsolationTest(FreezeSignalCacheMixin, TestCase):
11+
"""
12+
Tests that OpenEdxEventsTestMixin does not leak disabled event state.
13+
14+
The key regression: setUpClass disables all events, but before the fix
15+
there was no tearDownClass to re-enable them. Any test class that ran
16+
*after* a mixin class with ENABLED_OPENEDX_EVENTS=[] would find every
17+
event still disabled.
18+
"""
19+
20+
def setUp(self):
21+
super().setUp()
22+
load_all_signals()
23+
24+
def _all_events_enabled(self):
25+
return all(e._allow_events for e in OpenEdxPublicSignal.all_events()) # pylint: disable=protected-access
26+
27+
def _all_events_disabled(self):
28+
return all(not e._allow_events for e in OpenEdxPublicSignal.all_events()) # pylint: disable=protected-access
29+
30+
def test_teardown_re_enables_all_events(self):
31+
"""
32+
After tearDownClass runs, every event should be enabled.
33+
34+
Simulate the lifecycle of a mixin class with no enabled events:
35+
setUpClass disables everything, tearDownClass must restore it.
36+
"""
37+
class EmptyEnabledEvents(OpenEdxEventsTestMixin, TestCase):
38+
ENABLED_OPENEDX_EVENTS = []
39+
40+
# Before the class runs all events should be enabled.
41+
self.assertTrue(self._all_events_enabled())
42+
43+
EmptyEnabledEvents.setUpClass()
44+
# After setUpClass, all events are disabled.
45+
self.assertTrue(self._all_events_disabled())
46+
47+
EmptyEnabledEvents.tearDownClass()
48+
# After tearDownClass, all events must be re-enabled.
49+
self.assertTrue(
50+
self._all_events_enabled(),
51+
"tearDownClass should re-enable all events so subsequent test "
52+
"classes are not affected by this class's event isolation.",
53+
)
54+
55+
def test_teardown_re_enables_events_after_subset_enabled(self):
56+
"""
57+
The tearDownClass re-enables all events even when only a subset was enabled.
58+
"""
59+
all_event_types = [e.event_type for e in OpenEdxPublicSignal.all_events()]
60+
one_event_type = all_event_types[0]
61+
62+
class OneEnabledEvent(OpenEdxEventsTestMixin, TestCase):
63+
ENABLED_OPENEDX_EVENTS = [one_event_type]
64+
65+
OneEnabledEvent.setUpClass()
66+
# Only one event should be enabled at this point.
67+
enabled = [e for e in OpenEdxPublicSignal.all_events() if e._allow_events] # pylint: disable=protected-access
68+
self.assertEqual(len(enabled), 1)
69+
self.assertEqual(enabled[0].event_type, one_event_type)
70+
71+
OneEnabledEvent.tearDownClass()
72+
self.assertTrue(
73+
self._all_events_enabled(),
74+
"tearDownClass should re-enable all events regardless of how many were enabled.",
75+
)

0 commit comments

Comments
 (0)