|
84 | 84 | from courses.models import ( |
85 | 85 | CourseRunCertificate, |
86 | 86 | CourseRunEnrollment, |
| 87 | + CourseRunEnrollmentAudit, |
87 | 88 | EnrollmentMode, |
88 | 89 | PaidCourseRun, |
89 | 90 | ProgramCertificate, |
90 | 91 | ProgramEnrollment, |
| 92 | + ProgramEnrollmentAudit, |
91 | 93 | ProgramRequirement, |
92 | 94 | ProgramRequirementNodeType, |
93 | 95 | ) |
@@ -243,6 +245,26 @@ def test_create_local_enrollment_existing_enrollment( |
243 | 245 | assert enrollment.edx_enrolled is True |
244 | 246 |
|
245 | 247 |
|
| 248 | +def test_create_local_enrollment_writes_audit_on_creation(user): |
| 249 | + """ |
| 250 | + create_local_enrollment should write a CourseRunEnrollmentAudit row when a |
| 251 | + brand-new enrollment is created. Without this, the very first event in an |
| 252 | + enrollment's lifecycle would be missing from the audit table. |
| 253 | + """ |
| 254 | + run = CourseRunFactory.create() |
| 255 | + |
| 256 | + enrollment, created = create_local_enrollment(user, run) |
| 257 | + |
| 258 | + assert created is True |
| 259 | + audit_rows = CourseRunEnrollmentAudit.objects.filter(enrollment=enrollment) |
| 260 | + assert audit_rows.count() == 1 |
| 261 | + audit_row = audit_rows.first() |
| 262 | + assert audit_row.acting_user is None |
| 263 | + assert audit_row.data_after["id"] == enrollment.id |
| 264 | + assert audit_row.data_after["active"] is True |
| 265 | + assert audit_row.data_after["edx_enrolled"] is True |
| 266 | + |
| 267 | + |
246 | 268 | @pytest.mark.parametrize( |
247 | 269 | "enrollment_mode", [EDX_DEFAULT_ENROLLMENT_MODE, EDX_ENROLLMENT_VERIFIED_MODE] |
248 | 270 | ) |
@@ -295,6 +317,32 @@ def test_create_run_enrollments( |
295 | 317 | patched_send_enrollment_email.assert_any_call(enrollment) |
296 | 318 |
|
297 | 319 |
|
| 320 | +def test_create_run_enrollments_writes_audit_on_creation(mocker, user): |
| 321 | + """ |
| 322 | + create_run_enrollments should write a CourseRunEnrollmentAudit row for every |
| 323 | + newly-created enrollment. |
| 324 | + """ |
| 325 | + runs = CourseRunFactory.create_batch(2) |
| 326 | + mocker.patch("courses.api.enroll_in_edx_course_runs") |
| 327 | + mocker.patch("courses.api.mail_api.send_course_run_enrollment_email") |
| 328 | + mocker.patch("courses.tasks.subscribe_edx_course_emails.delay") |
| 329 | + |
| 330 | + successful_enrollments, edx_request_success = create_run_enrollments(user, runs) |
| 331 | + |
| 332 | + assert edx_request_success is True |
| 333 | + assert len(successful_enrollments) == len(runs) |
| 334 | + for enrollment in successful_enrollments: |
| 335 | + audit_rows = CourseRunEnrollmentAudit.objects.filter(enrollment=enrollment) |
| 336 | + assert audit_rows.count() == 1, ( |
| 337 | + f"expected exactly one audit row for the newly-created enrollment {enrollment.id}, " |
| 338 | + f"found {audit_rows.count()}" |
| 339 | + ) |
| 340 | + audit_row = audit_rows.first() |
| 341 | + assert audit_row.acting_user is None |
| 342 | + assert audit_row.data_after["id"] == enrollment.id |
| 343 | + assert audit_row.data_after["active"] is True |
| 344 | + |
| 345 | + |
298 | 346 | @pytest.mark.parametrize("is_active", [True, False]) |
299 | 347 | def test_create_run_enrollments_upgrade( |
300 | 348 | mocker, |
@@ -526,6 +574,28 @@ def test_create_program_enrollments_creation(user, mode): |
526 | 574 | assert enrollment.enrollment_mode == mode |
527 | 575 |
|
528 | 576 |
|
| 577 | +def test_create_program_enrollments_writes_audit_on_creation(user): |
| 578 | + """ |
| 579 | + create_program_enrollments should write a ProgramEnrollmentAudit row for |
| 580 | + every newly-created enrollment. |
| 581 | + """ |
| 582 | + programs = ProgramFactory.create_batch(2) |
| 583 | + |
| 584 | + successful_enrollments = create_program_enrollments(user, programs) |
| 585 | + |
| 586 | + assert len(successful_enrollments) == len(programs) |
| 587 | + for enrollment in successful_enrollments: |
| 588 | + audit_rows = ProgramEnrollmentAudit.objects.filter(enrollment=enrollment) |
| 589 | + assert audit_rows.count() == 1, ( |
| 590 | + f"expected exactly one audit row for the newly-created enrollment {enrollment.id}, " |
| 591 | + f"found {audit_rows.count()}" |
| 592 | + ) |
| 593 | + audit_row = audit_rows.first() |
| 594 | + assert audit_row.acting_user is None |
| 595 | + assert audit_row.data_after["id"] == enrollment.id |
| 596 | + assert audit_row.data_after["active"] is True |
| 597 | + |
| 598 | + |
529 | 599 | @pytest.mark.parametrize("active", [True, False]) |
530 | 600 | @pytest.mark.parametrize("change_status", [None, *ALL_ENROLL_CHANGE_STATUSES]) |
531 | 601 | def test_create_program_enrollments_reactivation(user, active, change_status): |
@@ -561,7 +631,11 @@ def test_create_program_enrollments_creation_fail(mocker, user): |
561 | 631 | creation of local enrollment records |
562 | 632 | """ |
563 | 633 | programs = ProgramFactory.create_batch(2) |
564 | | - enrollment = ProgramEnrollmentFactory.build(program=programs[1]) |
| 634 | + # Use .create() (not .build()) so the enrollment has a primary key. |
| 635 | + # `create_program_enrollments` now calls `enrollment.save_and_log(None)` |
| 636 | + # when get_or_create reports created=True, which writes an audit row |
| 637 | + # via a ForeignKey to the enrollment and therefore requires a saved PK. |
| 638 | + enrollment = ProgramEnrollmentFactory.create(user=user, program=programs[1]) |
565 | 639 | mocker.patch( |
566 | 640 | "courses.api.ProgramEnrollment.all_objects.get_or_create", |
567 | 641 | side_effect=[Exception(), (enrollment, True)], |
|
0 commit comments