From eb72ab58f9c67eb30b7fdc2421442ee13d594815 Mon Sep 17 00:00:00 2001 From: Clinton Blackburn Date: Fri, 13 Mar 2015 14:09:31 -0400 Subject: [PATCH] Validating event emission by the commerce app Added tests to ensure manual enrollments emit enrollment events. --- common/djangoapps/student/tests/tests.py | 91 +++++++++++++----------- lms/djangoapps/commerce/tests.py | 27 ++++--- 2 files changed, 67 insertions(+), 51 deletions(-) diff --git a/common/djangoapps/student/tests/tests.py b/common/djangoapps/student/tests/tests.py index 81363b87c0..65b2b7a61d 100644 --- a/common/djangoapps/student/tests/tests.py +++ b/common/djangoapps/student/tests/tests.py @@ -485,14 +485,60 @@ class DashboardTest(ModuleStoreTestCase): self.assertContains(response, expected_url) -class EnrollInCourseTest(TestCase): - """Tests enrolling and unenrolling in courses.""" +class EnrollmentEventTestMixin(object): + """ Mixin with assertions for validating enrollment events. """ def setUp(self): + super(EnrollmentEventTestMixin, self).setUp() patcher = patch('student.models.tracker') self.mock_tracker = patcher.start() self.addCleanup(patcher.stop) + def assert_no_events_were_emitted(self): + """Ensures no events were emitted since the last event related assertion""" + self.assertFalse(self.mock_tracker.emit.called) # pylint: disable=maybe-no-member + self.mock_tracker.reset_mock() + + def assert_enrollment_mode_change_event_was_emitted(self, user, course_key, mode): + """Ensures an enrollment mode change event was emitted""" + self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member + 'edx.course.enrollment.mode_changed', + { + 'course_id': course_key.to_deprecated_string(), + 'user_id': user.pk, + 'mode': mode + } + ) + self.mock_tracker.reset_mock() + + def assert_enrollment_event_was_emitted(self, user, course_key): + """Ensures an enrollment event was emitted since the last event related assertion""" + self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member + 'edx.course.enrollment.activated', + { + 'course_id': course_key.to_deprecated_string(), + 'user_id': user.pk, + 'mode': 'honor' + } + ) + self.mock_tracker.reset_mock() + + def assert_unenrollment_event_was_emitted(self, user, course_key): + """Ensures an unenrollment event was emitted since the last event related assertion""" + self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member + 'edx.course.enrollment.deactivated', + { + 'course_id': course_key.to_deprecated_string(), + 'user_id': user.pk, + 'mode': 'honor' + } + ) + self.mock_tracker.reset_mock() + + +class EnrollInCourseTest(EnrollmentEventTestMixin, TestCase): + """Tests enrolling and unenrolling in courses.""" + @unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms') def test_enrollment(self): user = User.objects.create_user("joe", "joe@joe.com", "password") @@ -540,47 +586,6 @@ class EnrollInCourseTest(TestCase): self.assertTrue(CourseEnrollment.is_enrolled(user, course_id)) self.assertEquals(enrollment.mode, "audit") - def assert_no_events_were_emitted(self): - """Ensures no events were emitted since the last event related assertion""" - self.assertFalse(self.mock_tracker.emit.called) # pylint: disable=maybe-no-member - self.mock_tracker.reset_mock() - - def assert_enrollment_mode_change_event_was_emitted(self, user, course_key, mode): - """Ensures an enrollment mode change event was emitted""" - self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member - 'edx.course.enrollment.mode_changed', - { - 'course_id': course_key.to_deprecated_string(), - 'user_id': user.pk, - 'mode': mode - } - ) - self.mock_tracker.reset_mock() - - def assert_enrollment_event_was_emitted(self, user, course_key): - """Ensures an enrollment event was emitted since the last event related assertion""" - self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member - 'edx.course.enrollment.activated', - { - 'course_id': course_key.to_deprecated_string(), - 'user_id': user.pk, - 'mode': 'honor' - } - ) - self.mock_tracker.reset_mock() - - def assert_unenrollment_event_was_emitted(self, user, course_key): - """Ensures an unenrollment event was emitted since the last event related assertion""" - self.mock_tracker.emit.assert_called_once_with( # pylint: disable=maybe-no-member - 'edx.course.enrollment.deactivated', - { - 'course_id': course_key.to_deprecated_string(), - 'user_id': user.pk, - 'mode': 'honor' - } - ) - self.mock_tracker.reset_mock() - def test_enrollment_non_existent_user(self): # Testing enrollment of newly unsaved user (i.e. no database entry) user = User(username="rusty", email="rusty@fake.edx.org") diff --git a/lms/djangoapps/commerce/tests.py b/lms/djangoapps/commerce/tests.py index c26e05a75f..80f9c98750 100644 --- a/lms/djangoapps/commerce/tests.py +++ b/lms/djangoapps/commerce/tests.py @@ -18,6 +18,7 @@ from course_modes.models import CourseMode from enrollment.api import add_enrollment from student.models import CourseEnrollment from student.tests.factories import UserFactory, CourseModeFactory +from student.tests.tests import EnrollmentEventTestMixin ECOMMERCE_API_URL = 'http://example.com/api' @@ -28,7 +29,7 @@ ECOMMERCE_API_SUCCESSFUL_BODY = json.dumps({'status': OrderStatus.COMPLETE, 'num @ddt @override_settings(ECOMMERCE_API_URL=ECOMMERCE_API_URL, ECOMMERCE_API_SIGNING_KEY=ECOMMERCE_API_SIGNING_KEY) -class OrdersViewTests(ModuleStoreTestCase): +class OrdersViewTests(EnrollmentEventTestMixin, ModuleStoreTestCase): """ Tests for the commerce orders view. """ @@ -71,6 +72,16 @@ class OrdersViewTests(ModuleStoreTestCase): self.assertEqual(response.status_code, 503) self.assertResponseMessage(response, 'Call to E-Commerce API failed. Order creation failed.') + def assertUserEnrolled(self): + """ Asserts that the user is enrolled in the course, and that an enrollment event was fired. """ + self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) + self.assert_enrollment_event_was_emitted(self.user, self.course.id) + + def assertUserNotEnrolled(self): + """ Asserts that the user is NOT enrolled in the course, and that an enrollment event was NOT fired. """ + self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id)) + self.assert_no_events_were_emitted() + def setUp(self): super(OrdersViewTests, self).setUp() self.url = reverse('commerce:orders') @@ -128,7 +139,7 @@ class OrdersViewTests(ModuleStoreTestCase): self._mock_ecommerce_api(status=status, body=json.dumps({'user_message': 'FAIL!'})) response = self._post_to_view() self.assertValidEcommerceApiErrorResponse(response) - self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id)) + self.assertUserNotEnrolled() @httpretty.activate def test_ecommerce_api_timeout(self): @@ -143,7 +154,7 @@ class OrdersViewTests(ModuleStoreTestCase): self._mock_ecommerce_api(body=request_callback) response = self._post_to_view() self.assertValidEcommerceApiErrorResponse(response) - self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id)) + self.assertUserNotEnrolled() @httpretty.activate def test_ecommerce_api_bad_data(self): @@ -153,7 +164,7 @@ class OrdersViewTests(ModuleStoreTestCase): self._mock_ecommerce_api(body='TOTALLY NOT JSON!') response = self._post_to_view() self.assertValidEcommerceApiErrorResponse(response) - self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id)) + self.assertUserNotEnrolled() @data(True, False) @httpretty.activate @@ -179,7 +190,7 @@ class OrdersViewTests(ModuleStoreTestCase): msg = Messages.ORDER_COMPLETED.format(order_number=ORDER_NUMBER) self.assertResponseMessage(response, msg) - self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) + self.assertUserEnrolled() # Verify the correct information was passed to the E-Commerce API request = httpretty.last_request() @@ -201,7 +212,7 @@ class OrdersViewTests(ModuleStoreTestCase): self.assertResponseMessage(response, msg) # TODO Eventually we should NOT be enrolling users directly from this view. - self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) + self.assertUserEnrolled() @httpretty.activate def test_course_without_sku(self): @@ -225,7 +236,7 @@ class OrdersViewTests(ModuleStoreTestCase): self.assertResponseMessage(response, msg) # The user should be enrolled, and no calls made to the E-Commerce API - self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) + self.assertUserEnrolled() self.assertIsInstance(httpretty.last_request(), HTTPrettyRequestEmpty) @httpretty.activate @@ -243,5 +254,5 @@ class OrdersViewTests(ModuleStoreTestCase): self.assertResponseMessage(response, msg) # Ensure that the user is not enrolled and that no calls were made to the E-Commerce API - self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id)) + self.assertUserEnrolled() self.assertIsInstance(httpretty.last_request(), HTTPrettyRequestEmpty)