Skip to content

fix: test isolation fixes for certificates and openedx-events test mixins#38347

Open
feanil wants to merge 5 commits intomasterfrom
feanil/test-isolation-fixes
Open

fix: test isolation fixes for certificates and openedx-events test mixins#38347
feanil wants to merge 5 commits intomasterfrom
feanil/test-isolation-fixes

Conversation

@feanil
Copy link
Copy Markdown
Contributor

@feanil feanil commented Apr 10, 2026

Summary

These fixes address two pre-existing test isolation bugs that were exposed when rebalancing the CI test shards (#38287). They are independent of the rebalancing work and safe to merge now.

Bug 1: SharedModuleStoreTestCase misuse in certificate tests

Three certificate test classes called CourseFactory() in setUp() instead of setUpClass(). With SharedModuleStoreTestCase, the modulestore is shared across all tests in the class and connections are only closed at tearDownClass. Calling CourseFactory() in setUp() accumulated open MongoDB connections across every test method, eventually exhausting file descriptors when run in the same process as other tests.

Fixed in test_filters.py and test_models.py — moved CourseFactory() to setUpClass() in CertificateFiltersTest, CertificateInvalidationTest, and CertificateAllowlistTest. Safe because tests only read the course from MongoDB; per-test data (enrollments, certificates) is in the Django ORM and is rolled back between tests.

Bug 2: OpenEdxEventsTestMixin leaving events globally disabled

OpenEdxEventsTestMixin.setUpClass() called disable_all_events() then enabled only ENABLED_OPENEDX_EVENTS, but had no tearDownClass to restore event state. Certificate test classes with ENABLED_OPENEDX_EVENTS = [] left all OpenEdX events globally disabled for subsequent tests in the same process.

Fixed upstream in openedx/openedx-events#559 (released as 11.1.1). This PR updates to 11.1.1 and cleans up the interim workarounds (manual tearDownClass overrides and duplicate mixin includes) from the affected test classes.

feanil and others added 5 commits April 10, 2026 17:12
…ubclasses

Three test classes in the certificates app were calling CourseFactory() in
setUp() despite extending SharedModuleStoreTestCase. Unlike ModuleStoreTestCase,
SharedModuleStoreTestCase shares a single modulestore across all tests in the
class and only closes MongoDB connections at tearDownClass. Calling
CourseFactory() in setUp() created a new MongoDB course (and opened connections)
for every test method without releasing them, causing connection accumulation
across the full test run.

Affected classes:
- CertificateFiltersTest (test_filters.py)
- CertificateInvalidationTest (test_models.py)
- CertificateAllowlistTest (test_models.py)

In each case the course is only read by test methods (test data such as users,
enrollments and certificates is written via Django ORM and rolled back between
tests), so sharing a single course across the class is correct.

See: https://github.com/openedx/openedx-platform/blob/master/xmodule/modulestore/tests/django_utils.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…vents.testing

openedx_events/tests/utils.py was moved to openedx_events/testing.py in
openedx/openedx-events#559 so the test utilities are included in the
installed package (setup.py excludes the tests/ subpackage from the wheel).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When OpenEdxEventsTestMixin was listed after a TestCase subclass (e.g.
Foo(SharedModuleStoreTestCase, OpenEdxEventsTestMixin)), it landed after
unittest.case.TestCase in the MRO. Since unittest.case.TestCase.setUpClass
and tearDownClass do not call super(), the mixin's lifecycle methods never
ran. The workaround was to manually call cls.start_events_isolation() in
each class's setUpClass, but there was no corresponding tearDownClass to
restore event state, causing events disabled by one test class to leak into
subsequent classes in the same process.

Fix by placing OpenEdxEventsTestMixin first in the base class list so it
appears before unittest.case.TestCase in the MRO. This lets setUpClass and
tearDownClass run automatically through the cooperative super() chain,
removing the need for manual start_events_isolation() calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This mixin is already included via one of the other mixins on this test
class so including it again was messing with the MRO for the test
classes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant