feat: relinting
Trying with new autoformat settings. FIXES: APER-3146
This commit is contained in:
@@ -128,9 +128,7 @@ def award_program_certificate(client, user, program_uuid, visible_date):
|
||||
"username": user.username,
|
||||
"lms_user_id": user.id,
|
||||
"credential": {"type": PROGRAM_CERTIFICATE, "program_uuid": program_uuid},
|
||||
"attributes": [
|
||||
{"name": "visible_date", "value": visible_date.strftime(DATE_FORMAT)}
|
||||
],
|
||||
"attributes": [{"name": "visible_date", "value": visible_date.strftime(DATE_FORMAT)}],
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
@@ -138,9 +136,7 @@ def award_program_certificate(client, user, program_uuid, visible_date):
|
||||
|
||||
@shared_task(bind=True, ignore_result=True)
|
||||
@set_code_owner_attribute
|
||||
def award_program_certificates(
|
||||
self, username
|
||||
): # lint-amnesty, pylint: disable=too-many-statements
|
||||
def award_program_certificates(self, username): # lint-amnesty, pylint: disable=too-many-statements
|
||||
"""
|
||||
This task is designed to be called whenever a student's completion status
|
||||
changes with respect to one or more courses (primarily, when a course
|
||||
@@ -172,9 +168,7 @@ def award_program_certificates(
|
||||
return self.retry(exc=exception, countdown=countdown, max_retries=MAX_RETRIES)
|
||||
|
||||
LOGGER.info(f"Running task award_program_certificates for username {username}")
|
||||
programs_without_certificates = configuration_helpers.get_value(
|
||||
"programs_without_certificates", []
|
||||
)
|
||||
programs_without_certificates = configuration_helpers.get_value("programs_without_certificates", [])
|
||||
if programs_without_certificates:
|
||||
if str(programs_without_certificates[0]).lower() == "all":
|
||||
# this check will prevent unnecessary logging for partners without program certificates
|
||||
@@ -187,19 +181,17 @@ def award_program_certificates(
|
||||
# mark this task for retry instead of failing it altogether.
|
||||
|
||||
if not CredentialsApiConfig.current().is_learner_issuance_enabled:
|
||||
error_msg = "Task award_program_certificates cannot be executed when credentials issuance is disabled in API config"
|
||||
LOGGER.warning(error_msg)
|
||||
raise _retry_with_custom_exception(
|
||||
username=username, reason=error_msg, countdown=countdown
|
||||
error_msg = (
|
||||
"Task award_program_certificates cannot be executed when credentials issuance is disabled in API config"
|
||||
)
|
||||
LOGGER.warning(error_msg)
|
||||
raise _retry_with_custom_exception(username=username, reason=error_msg, countdown=countdown)
|
||||
|
||||
try:
|
||||
try:
|
||||
student = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
LOGGER.exception(
|
||||
f"Task award_program_certificates was called with invalid username {username}"
|
||||
)
|
||||
LOGGER.exception(f"Task award_program_certificates was called with invalid username {username}")
|
||||
# Don't retry for this case - just conclude the task.
|
||||
return
|
||||
completed_programs = {}
|
||||
@@ -208,9 +200,7 @@ def award_program_certificates(
|
||||
if not completed_programs:
|
||||
# No reason to continue beyond this point unless/until this
|
||||
# task gets updated to support revocation of program certs.
|
||||
LOGGER.info(
|
||||
f"Task award_program_certificates was called for user {username} with no completed programs"
|
||||
)
|
||||
LOGGER.info(f"Task award_program_certificates was called for user {username} with no completed programs")
|
||||
return
|
||||
|
||||
# Determine which program certificates the user has already been awarded, if any.
|
||||
@@ -218,16 +208,12 @@ def award_program_certificates(
|
||||
|
||||
# we will skip all the programs which have already been awarded and we want to skip the programs
|
||||
# which are exit in site configuration in 'programs_without_certificates' list.
|
||||
awarded_and_skipped_program_uuids = list(
|
||||
set(existing_program_uuids + list(programs_without_certificates))
|
||||
)
|
||||
awarded_and_skipped_program_uuids = list(set(existing_program_uuids + list(programs_without_certificates)))
|
||||
|
||||
except Exception as exc:
|
||||
error_msg = f"Failed to determine program certificates to be awarded for user {username}. {exc}"
|
||||
LOGGER.exception(error_msg)
|
||||
raise _retry_with_custom_exception(
|
||||
username=username, reason=error_msg, countdown=countdown
|
||||
) from exc
|
||||
raise _retry_with_custom_exception(username=username, reason=error_msg, countdown=countdown) from exc
|
||||
|
||||
# For each completed program for which the student doesn't already have a
|
||||
# certificate, award one now.
|
||||
@@ -235,9 +221,7 @@ def award_program_certificates(
|
||||
# This logic is important, because we will retry the whole task if awarding any particular program cert fails.
|
||||
#
|
||||
# N.B. the list is sorted to facilitate deterministic ordering, e.g. for tests.
|
||||
new_program_uuids = sorted(
|
||||
list(set(completed_programs.keys()) - set(awarded_and_skipped_program_uuids))
|
||||
)
|
||||
new_program_uuids = sorted(list(set(completed_programs.keys()) - set(awarded_and_skipped_program_uuids)))
|
||||
if new_program_uuids:
|
||||
try:
|
||||
credentials_client = get_credentials_api_client(
|
||||
@@ -247,23 +231,15 @@ def award_program_certificates(
|
||||
error_msg = "Failed to create a credentials API client to award program certificates"
|
||||
LOGGER.exception(error_msg)
|
||||
# Retry because a misconfiguration could be fixed
|
||||
raise _retry_with_custom_exception(
|
||||
username=username, reason=error_msg, countdown=countdown
|
||||
) from exc
|
||||
raise _retry_with_custom_exception(username=username, reason=error_msg, countdown=countdown) from exc
|
||||
|
||||
failed_program_certificate_award_attempts = []
|
||||
for program_uuid in new_program_uuids:
|
||||
visible_date = completed_programs[program_uuid]
|
||||
try:
|
||||
LOGGER.info(
|
||||
f"Visible date for user {username} : program {program_uuid} is {visible_date}"
|
||||
)
|
||||
award_program_certificate(
|
||||
credentials_client, student, program_uuid, visible_date
|
||||
)
|
||||
LOGGER.info(
|
||||
f"Awarded certificate for program {program_uuid} to user {username}"
|
||||
)
|
||||
LOGGER.info(f"Visible date for user {username} : program {program_uuid} is {visible_date}")
|
||||
award_program_certificate(credentials_client, student, program_uuid, visible_date)
|
||||
LOGGER.info(f"Awarded certificate for program {program_uuid} to user {username}")
|
||||
except HTTPError as exc:
|
||||
if exc.response.status_code == 404:
|
||||
LOGGER.exception(
|
||||
@@ -290,16 +266,12 @@ def award_program_certificates(
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# keep trying to award other certs, but retry the whole task to fix any missing entries
|
||||
LOGGER.exception(
|
||||
f"Failed to award certificate for program {program_uuid} to user {username}."
|
||||
)
|
||||
LOGGER.exception(f"Failed to award certificate for program {program_uuid} to user {username}.")
|
||||
failed_program_certificate_award_attempts.append(program_uuid)
|
||||
|
||||
if failed_program_certificate_award_attempts:
|
||||
# N.B. This logic assumes that this task is idempotent
|
||||
LOGGER.info(
|
||||
f"Retrying task to award failed certificates to user {username}"
|
||||
)
|
||||
LOGGER.info(f"Retrying task to award failed certificates to user {username}")
|
||||
# The error message may change on each reattempt but will never be raised until
|
||||
# the max number of retries have been exceeded. It is unlikely that this list
|
||||
# will change by the time it reaches its maximimum number of attempts.
|
||||
@@ -307,20 +279,14 @@ def award_program_certificates(
|
||||
f"Failed to award certificate for user {username} "
|
||||
f"for programs {failed_program_certificate_award_attempts}"
|
||||
)
|
||||
raise _retry_with_custom_exception(
|
||||
username=username, reason=error_msg, countdown=countdown
|
||||
)
|
||||
raise _retry_with_custom_exception(username=username, reason=error_msg, countdown=countdown)
|
||||
else:
|
||||
LOGGER.info(f"User {username} is not eligible for any new program certificates")
|
||||
|
||||
LOGGER.info(
|
||||
f"Successfully completed the task award_program_certificates for username {username}"
|
||||
)
|
||||
LOGGER.info(f"Successfully completed the task award_program_certificates for username {username}")
|
||||
|
||||
|
||||
def post_course_certificate_configuration(
|
||||
client, cert_config, certificate_available_date=None
|
||||
):
|
||||
def post_course_certificate_configuration(client, cert_config, certificate_available_date=None):
|
||||
"""
|
||||
Make a POST request to the Credentials IDA's `course_certificates` endpoint (/api/v2/course_certificates/). This
|
||||
endpoint manages the course certificate configurations within the Credentials IDA.
|
||||
@@ -333,9 +299,7 @@ def post_course_certificate_configuration(
|
||||
in the form of an ISO 8601 DateTime String.
|
||||
"""
|
||||
credentials_api_base_url = get_credentials_api_base_url()
|
||||
credentials_api_url = urljoin(
|
||||
f"{credentials_api_base_url}/", "course_certificates/"
|
||||
)
|
||||
credentials_api_url = urljoin(f"{credentials_api_base_url}/", "course_certificates/")
|
||||
|
||||
response = client.post(
|
||||
credentials_api_url,
|
||||
@@ -360,9 +324,7 @@ def post_course_certificate_configuration(
|
||||
response.raise_for_status()
|
||||
|
||||
|
||||
def post_course_certificate(
|
||||
client, username, certificate, visible_date, date_override=None, org=None
|
||||
):
|
||||
def post_course_certificate(client, username, certificate, visible_date, date_override=None, org=None):
|
||||
"""
|
||||
POST a certificate that has been updated to Credentials
|
||||
"""
|
||||
@@ -373,20 +335,14 @@ def post_course_certificate(
|
||||
api_url,
|
||||
json={
|
||||
"username": username,
|
||||
"status": "awarded"
|
||||
if certificate.is_valid()
|
||||
else "revoked", # Only need the two options at this time
|
||||
"status": "awarded" if certificate.is_valid() else "revoked", # Only need the two options at this time
|
||||
"credential": {
|
||||
"course_run_key": str(certificate.course_id),
|
||||
"mode": certificate.mode,
|
||||
"type": COURSE_CERTIFICATE,
|
||||
},
|
||||
"date_override": {"date": date_override.strftime(DATE_FORMAT)}
|
||||
if date_override
|
||||
else None,
|
||||
"attributes": [
|
||||
{"name": "visible_date", "value": visible_date.strftime(DATE_FORMAT)}
|
||||
],
|
||||
"date_override": {"date": date_override.strftime(DATE_FORMAT)} if date_override else None,
|
||||
"attributes": [{"name": "visible_date", "value": visible_date.strftime(DATE_FORMAT)}],
|
||||
},
|
||||
)
|
||||
response.raise_for_status()
|
||||
@@ -417,15 +373,9 @@ def update_credentials_course_certificate_configuration_available_date(
|
||||
course_key = str(course_key)
|
||||
course_modes = CourseMode.objects.filter(course_id=course_key)
|
||||
# There should only ever be one certificate relevant mode per course run
|
||||
modes = [
|
||||
mode.slug
|
||||
for mode in course_modes
|
||||
if mode.slug in CourseMode.CERTIFICATE_RELEVANT_MODES
|
||||
]
|
||||
modes = [mode.slug for mode in course_modes if mode.slug in CourseMode.CERTIFICATE_RELEVANT_MODES]
|
||||
if len(modes) != 1:
|
||||
LOGGER.exception(
|
||||
f"Either course {course_key} has no certificate mode or multiple modes. Task failed."
|
||||
)
|
||||
LOGGER.exception(f"Either course {course_key} has no certificate mode or multiple modes. Task failed.")
|
||||
return
|
||||
|
||||
credentials_client = get_credentials_api_client(
|
||||
@@ -474,7 +424,9 @@ def award_course_certificate(self, username, course_run_key):
|
||||
# has been temporarily disabled. Since this is a recoverable situation,
|
||||
# mark this task for retry instead of failing it altogether.
|
||||
if not CredentialsApiConfig.current().is_learner_issuance_enabled:
|
||||
error_msg = "Task award_course_certificate cannot be executed when credentials issuance is disabled in API config"
|
||||
error_msg = (
|
||||
"Task award_course_certificate cannot be executed when credentials issuance is disabled in API " "config"
|
||||
)
|
||||
LOGGER.warning(error_msg)
|
||||
raise _retry_with_custom_exception(
|
||||
username=username,
|
||||
@@ -487,9 +439,7 @@ def award_course_certificate(self, username, course_run_key):
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
LOGGER.exception(
|
||||
f"Task award_course_certificate was called with invalid username {username}"
|
||||
)
|
||||
LOGGER.exception(f"Task award_course_certificate was called with invalid username {username}")
|
||||
# Don't retry for this case - just conclude the task.
|
||||
return
|
||||
# Get the cert for the course key and username if it's both passing and available in professional/verified
|
||||
@@ -546,9 +496,7 @@ def award_course_certificate(self, username, course_run_key):
|
||||
org=course_key.org,
|
||||
)
|
||||
|
||||
LOGGER.info(
|
||||
f"Awarded certificate for course {course_key} to user {username}"
|
||||
)
|
||||
LOGGER.info(f"Awarded certificate for course {course_key} to user {username}")
|
||||
except Exception as exc:
|
||||
error_msg = f"Failed to determine course certificates to be awarded for user {username}."
|
||||
LOGGER.exception(error_msg)
|
||||
@@ -560,9 +508,7 @@ def award_course_certificate(self, username, course_run_key):
|
||||
) from exc
|
||||
|
||||
|
||||
def get_revokable_program_uuids(
|
||||
course_specific_programs: List[Dict], student: User
|
||||
) -> List[str]:
|
||||
def get_revokable_program_uuids(course_specific_programs: List[Dict], student: User) -> List[str]:
|
||||
"""
|
||||
Get program uuids for which certificate to be revoked.
|
||||
|
||||
@@ -618,9 +564,7 @@ def revoke_program_certificate(client, username, program_uuid):
|
||||
|
||||
@shared_task(bind=True, ignore_result=True)
|
||||
@set_code_owner_attribute
|
||||
def revoke_program_certificates(
|
||||
self, username, course_key
|
||||
): # lint-amnesty, pylint: disable=too-many-statements
|
||||
def revoke_program_certificates(self, username, course_key): # lint-amnesty, pylint: disable=too-many-statements
|
||||
"""
|
||||
This task is designed to be called whenever a student's course certificate is
|
||||
revoked.
|
||||
@@ -654,7 +598,9 @@ def revoke_program_certificates(
|
||||
# mark this task for retry instead of failing it altogether.
|
||||
|
||||
if not CredentialsApiConfig.current().is_learner_issuance_enabled:
|
||||
error_msg = "Task revoke_program_certificates cannot be executed when credentials issuance is disabled in API config"
|
||||
error_msg = (
|
||||
"Task revoke_program_certificates cannot be executed when credentials issuance is disabled in API config"
|
||||
)
|
||||
LOGGER.warning(error_msg)
|
||||
raise _retry_with_custom_exception(
|
||||
username=username,
|
||||
@@ -685,13 +631,10 @@ def revoke_program_certificates(
|
||||
return
|
||||
|
||||
# Determine which program certificates the user has already been awarded, if any.
|
||||
program_uuids_to_revoke = get_revokable_program_uuids(
|
||||
course_specific_programs, student
|
||||
)
|
||||
program_uuids_to_revoke = get_revokable_program_uuids(course_specific_programs, student)
|
||||
except Exception as exc:
|
||||
error_msg = (
|
||||
f"Failed to determine program certificates to be revoked for user {username} "
|
||||
f"with course {course_key}"
|
||||
f"Failed to determine program certificates to be revoked for user {username} " f"with course {course_key}"
|
||||
)
|
||||
LOGGER.exception(error_msg)
|
||||
raise _retry_with_custom_exception(
|
||||
@@ -710,17 +653,13 @@ def revoke_program_certificates(
|
||||
error_msg = "Failed to create a credentials API client to revoke program certificates"
|
||||
LOGGER.exception(error_msg)
|
||||
# Retry because a misconfiguration could be fixed
|
||||
raise _retry_with_custom_exception(
|
||||
username, course_key, reason=exc, countdown=countdown
|
||||
) from exc
|
||||
raise _retry_with_custom_exception(username, course_key, reason=exc, countdown=countdown) from exc
|
||||
|
||||
failed_program_certificate_revoke_attempts = []
|
||||
for program_uuid in program_uuids_to_revoke:
|
||||
try:
|
||||
revoke_program_certificate(credentials_client, username, program_uuid)
|
||||
LOGGER.info(
|
||||
f"Revoked certificate for program {program_uuid} for user {username}"
|
||||
)
|
||||
LOGGER.info(f"Revoked certificate for program {program_uuid} for user {username}")
|
||||
except HTTPError as exc:
|
||||
if exc.response.status_code == 404:
|
||||
LOGGER.exception(
|
||||
@@ -742,21 +681,15 @@ def revoke_program_certificates(
|
||||
countdown=rate_limit_countdown,
|
||||
) from exc
|
||||
else:
|
||||
LOGGER.exception(
|
||||
f"Unable to revoke certificate for user {username} for program {program_uuid}."
|
||||
)
|
||||
LOGGER.exception(f"Unable to revoke certificate for user {username} for program {program_uuid}.")
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# keep trying to revoke other certs, but retry the whole task to fix any missing entries
|
||||
LOGGER.warning(
|
||||
f"Failed to revoke certificate for program {program_uuid} of user {username}."
|
||||
)
|
||||
LOGGER.warning(f"Failed to revoke certificate for program {program_uuid} of user {username}.")
|
||||
failed_program_certificate_revoke_attempts.append(program_uuid)
|
||||
|
||||
if failed_program_certificate_revoke_attempts:
|
||||
# N.B. This logic assumes that this task is idempotent
|
||||
LOGGER.info(
|
||||
f"Retrying task to revoke failed certificates to user {username}"
|
||||
)
|
||||
LOGGER.info(f"Retrying task to revoke failed certificates to user {username}")
|
||||
# The error message may change on each reattempt but will never be raised until
|
||||
# the max number of retries have been exceeded. It is unlikely that this list
|
||||
# will change by the time it reaches its maximimum number of attempts.
|
||||
@@ -764,15 +697,11 @@ def revoke_program_certificates(
|
||||
f"Failed to revoke certificate for user {username} "
|
||||
f"for programs {failed_program_certificate_revoke_attempts}"
|
||||
)
|
||||
raise _retry_with_custom_exception(
|
||||
username, course_key, reason=error_msg, countdown=countdown
|
||||
)
|
||||
raise _retry_with_custom_exception(username, course_key, reason=error_msg, countdown=countdown)
|
||||
|
||||
else:
|
||||
LOGGER.info(f"There is no program certificates for user {username} to revoke")
|
||||
LOGGER.info(
|
||||
f"Successfully completed the task revoke_program_certificates for username {username}"
|
||||
)
|
||||
LOGGER.info(f"Successfully completed the task revoke_program_certificates for username {username}")
|
||||
|
||||
|
||||
@shared_task(bind=True, ignore_result=True)
|
||||
@@ -810,11 +739,9 @@ def update_certificate_visible_date_on_course_update(self, course_key):
|
||||
|
||||
# Retrieve a list of all usernames of learners who have a certificate record in this course-run. The
|
||||
# Credentials IDA REST API still requires a username as the main identifier for the learner.
|
||||
users_with_certificates_in_course = (
|
||||
GeneratedCertificate.eligible_available_certificates.filter(
|
||||
course_id=course_key
|
||||
).values_list("user__username", flat=True)
|
||||
)
|
||||
users_with_certificates_in_course = GeneratedCertificate.eligible_available_certificates.filter(
|
||||
course_id=course_key
|
||||
).values_list("user__username", flat=True)
|
||||
|
||||
LOGGER.info(
|
||||
f"Resending course certificates for learners in course {course_key} to the Credentials service. Queueing "
|
||||
@@ -862,8 +789,7 @@ def update_certificate_available_date_on_course_update(self, course_key):
|
||||
if (
|
||||
course_overview
|
||||
and course_overview.self_paced is False
|
||||
and course_overview.certificates_display_behavior
|
||||
== CertificatesDisplayBehaviors.END_WITH_DATE
|
||||
and course_overview.certificates_display_behavior == CertificatesDisplayBehaviors.END_WITH_DATE
|
||||
):
|
||||
LOGGER.info(
|
||||
f"Queueing task to update the `certificate_available_date` of course-run {course_key} to "
|
||||
@@ -880,18 +806,12 @@ def update_certificate_available_date_on_course_update(self, course_key):
|
||||
# associated with a `certificate_available_date`. This ends up causing learners' certificate to be incorrectly
|
||||
# hidden. This is due to the Credentials IDA not understanding the concept of course pacing. Thus, we need a way
|
||||
# to remove this value from self-paced courses in Credentials.
|
||||
elif (
|
||||
course_overview
|
||||
and course_overview.self_paced is True
|
||||
and course_overview.certificate_available_date is None
|
||||
):
|
||||
elif course_overview and course_overview.self_paced is True and course_overview.certificate_available_date is None:
|
||||
LOGGER.info(
|
||||
"Queueing task to remove the `certificate_available_date` in the Credentials service for course-run "
|
||||
f"{course_key}"
|
||||
)
|
||||
update_credentials_course_certificate_configuration_available_date.delay(
|
||||
str(course_key), None
|
||||
)
|
||||
update_credentials_course_certificate_configuration_available_date.delay(str(course_key), None)
|
||||
# ELSE, we don't meet the criteria to update the course cert config in the Credentials IDA
|
||||
else:
|
||||
LOGGER.warning(
|
||||
|
||||
@@ -77,16 +77,12 @@ class GetAwardedCertificateProgramsTestCase(TestCase):
|
||||
"""
|
||||
student = UserFactory(username="test-username")
|
||||
mock_get_credentials.return_value = [
|
||||
self.make_credential_result(
|
||||
status="awarded", credential={"program_uuid": 1}
|
||||
),
|
||||
self.make_credential_result(status="awarded", credential={"program_uuid": 1}),
|
||||
]
|
||||
|
||||
result = tasks.get_certified_programs(student)
|
||||
assert mock_get_credentials.call_args[0] == (student,)
|
||||
assert (
|
||||
mock_get_credentials.call_args[1].get("credential_type", None) == "program"
|
||||
)
|
||||
assert mock_get_credentials.call_args[1].get("credential_type", None) == "program"
|
||||
assert result == [1]
|
||||
|
||||
|
||||
@@ -113,9 +109,7 @@ class AwardProgramCertificateTestCase(TestCase):
|
||||
"http://test-server/credentials/",
|
||||
)
|
||||
|
||||
tasks.award_program_certificate(
|
||||
test_client, student, 123, datetime(2010, 5, 30)
|
||||
)
|
||||
tasks.award_program_certificate(test_client, student, 123, datetime(2010, 5, 30))
|
||||
|
||||
expected_body = {
|
||||
"username": student.username,
|
||||
@@ -141,9 +135,7 @@ class AwardProgramCertificateTestCase(TestCase):
|
||||
@mock.patch(TASKS_MODULE + ".get_certified_programs")
|
||||
@mock.patch(TASKS_MODULE + ".get_completed_programs")
|
||||
@override_settings(CREDENTIALS_SERVICE_USERNAME="test-service-username")
|
||||
class AwardProgramCertificatesTestCase(
|
||||
CatalogIntegrationMixin, CredentialsApiConfigMixin, TestCase
|
||||
):
|
||||
class AwardProgramCertificatesTestCase(CatalogIntegrationMixin, CredentialsApiConfigMixin, TestCase):
|
||||
"""
|
||||
Tests for the 'award_program_certificates' celery task.
|
||||
"""
|
||||
@@ -194,20 +186,14 @@ class AwardProgramCertificatesTestCase(
|
||||
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
|
||||
actual_program_uuids = [
|
||||
call[0][2] for call in mock_award_program_certificate.call_args_list
|
||||
]
|
||||
actual_program_uuids = [call[0][2] for call in mock_award_program_certificate.call_args_list]
|
||||
assert actual_program_uuids == expected_awarded_program_uuids
|
||||
|
||||
actual_visible_dates = [
|
||||
call[0][3] for call in mock_award_program_certificate.call_args_list
|
||||
]
|
||||
actual_visible_dates = [call[0][3] for call in mock_award_program_certificate.call_args_list]
|
||||
assert actual_visible_dates == expected_awarded_program_uuids
|
||||
# program uuids are same as mock dates
|
||||
|
||||
@mock.patch(
|
||||
"openedx.core.djangoapps.site_configuration.helpers.get_current_site_configuration"
|
||||
)
|
||||
@mock.patch("openedx.core.djangoapps.site_configuration.helpers.get_current_site_configuration")
|
||||
def test_awarding_certs_with_skip_program_certificate(
|
||||
self,
|
||||
mocked_get_current_site_configuration,
|
||||
@@ -236,13 +222,9 @@ class AwardProgramCertificatesTestCase(
|
||||
expected_awarded_program_uuids = [3, 4]
|
||||
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
actual_program_uuids = [
|
||||
call[0][2] for call in mock_award_program_certificate.call_args_list
|
||||
]
|
||||
actual_program_uuids = [call[0][2] for call in mock_award_program_certificate.call_args_list]
|
||||
assert actual_program_uuids == expected_awarded_program_uuids
|
||||
actual_visible_dates = [
|
||||
call[0][3] for call in mock_award_program_certificate.call_args_list
|
||||
]
|
||||
actual_visible_dates = [call[0][3] for call in mock_award_program_certificate.call_args_list]
|
||||
assert actual_visible_dates == expected_awarded_program_uuids
|
||||
# program uuids are same as mock dates
|
||||
|
||||
@@ -250,16 +232,12 @@ class AwardProgramCertificatesTestCase(
|
||||
("credentials", "enable_learner_issuance"),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_retry_if_config_disabled(
|
||||
self, disabled_config_type, disabled_config_attribute, *mock_helpers
|
||||
):
|
||||
def test_retry_if_config_disabled(self, disabled_config_type, disabled_config_attribute, *mock_helpers):
|
||||
"""
|
||||
Checks that the task is aborted if any relevant api configs are
|
||||
disabled.
|
||||
"""
|
||||
getattr(self, f"create_{disabled_config_type}_config")(
|
||||
**{disabled_config_attribute: False}
|
||||
)
|
||||
getattr(self, f"create_{disabled_config_type}_config")(**{disabled_config_attribute: False})
|
||||
with mock.patch(TASKS_MODULE + ".LOGGER.warning") as mock_warning:
|
||||
with pytest.raises(MaxRetriesExceededError):
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
@@ -371,9 +349,7 @@ class AwardProgramCertificatesTestCase(
|
||||
"""
|
||||
mock_get_completed_programs.return_value = {1: 1, 2: 2}
|
||||
mock_get_certified_programs.side_effect = [[], [2]]
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect(
|
||||
[Exception("boom"), None]
|
||||
)
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect([Exception("boom"), None])
|
||||
|
||||
with mock.patch(TASKS_MODULE + ".LOGGER.info") as mock_info, mock.patch(
|
||||
TASKS_MODULE + ".LOGGER.exception"
|
||||
@@ -386,25 +362,17 @@ class AwardProgramCertificatesTestCase(
|
||||
uuid=1, username=self.student.username
|
||||
)
|
||||
)
|
||||
mock_info.assert_any_call(
|
||||
f"Awarded certificate for program {1} to user {self.student.username}"
|
||||
)
|
||||
mock_info.assert_any_call(
|
||||
f"Awarded certificate for program {2} to user {self.student.username}"
|
||||
)
|
||||
mock_info.assert_any_call(f"Awarded certificate for program {1} to user {self.student.username}")
|
||||
mock_info.assert_any_call(f"Awarded certificate for program {2} to user {self.student.username}")
|
||||
|
||||
def test_retry_on_programs_api_errors(
|
||||
self, mock_get_completed_programs, *_mock_helpers
|
||||
):
|
||||
def test_retry_on_programs_api_errors(self, mock_get_completed_programs, *_mock_helpers):
|
||||
"""
|
||||
Ensures that any otherwise-unhandled errors that arise while trying
|
||||
to get completed programs (e.g. network issues or other
|
||||
transient API errors) will cause the task to be failed and queued for
|
||||
retry.
|
||||
"""
|
||||
mock_get_completed_programs.side_effect = self._make_side_effect(
|
||||
[Exception("boom"), None]
|
||||
)
|
||||
mock_get_completed_programs.side_effect = self._make_side_effect([Exception("boom"), None])
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
assert mock_get_completed_programs.call_count == 3
|
||||
|
||||
@@ -422,9 +390,7 @@ class AwardProgramCertificatesTestCase(
|
||||
"""
|
||||
mock_get_completed_programs.return_value = {1: 1, 2: 2}
|
||||
mock_get_certified_programs.return_value = [1]
|
||||
mock_get_certified_programs.side_effect = self._make_side_effect(
|
||||
[Exception("boom"), None]
|
||||
)
|
||||
mock_get_certified_programs.side_effect = self._make_side_effect([Exception("boom"), None])
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
assert mock_get_certified_programs.call_count == 2
|
||||
assert mock_award_program_certificate.call_count == 1
|
||||
@@ -441,9 +407,7 @@ class AwardProgramCertificatesTestCase(
|
||||
exception = HTTPError()
|
||||
exception.response = mock.Mock(status_code=429)
|
||||
mock_get_completed_programs.return_value = {1: 1, 2: 2}
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect(
|
||||
[exception, None]
|
||||
)
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect([exception, None])
|
||||
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
|
||||
@@ -461,9 +425,7 @@ class AwardProgramCertificatesTestCase(
|
||||
exception = HTTPError()
|
||||
exception.response = mock.Mock(status_code=404)
|
||||
mock_get_completed_programs.return_value = {1: 1, 2: 2}
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect(
|
||||
[exception, None]
|
||||
)
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect([exception, None])
|
||||
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
|
||||
@@ -481,9 +443,7 @@ class AwardProgramCertificatesTestCase(
|
||||
exception = HTTPError()
|
||||
exception.response = mock.Mock(status_code=418)
|
||||
mock_get_completed_programs.return_value = {1: 1, 2: 2}
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect(
|
||||
[exception, None]
|
||||
)
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect([exception, None])
|
||||
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
|
||||
@@ -525,9 +485,7 @@ class PostCourseCertificateTestCase(TestCase):
|
||||
|
||||
visible_date = datetime.now()
|
||||
|
||||
tasks.post_course_certificate(
|
||||
test_client, self.student.username, self.certificate, visible_date
|
||||
)
|
||||
tasks.post_course_certificate(test_client, self.student.username, self.certificate, visible_date)
|
||||
|
||||
expected_body = {
|
||||
"username": self.student.username,
|
||||
@@ -541,9 +499,7 @@ class PostCourseCertificateTestCase(TestCase):
|
||||
"attributes": [
|
||||
{
|
||||
"name": "visible_date",
|
||||
"value": visible_date.strftime(
|
||||
"%Y-%m-%dT%H:%M:%SZ"
|
||||
), # text representation of date
|
||||
"value": visible_date.strftime("%Y-%m-%dT%H:%M:%SZ"), # text representation of date
|
||||
}
|
||||
],
|
||||
}
|
||||
@@ -607,107 +563,79 @@ class AwardCourseCertificatesTestCase(CredentialsApiConfigMixin, TestCase):
|
||||
"""
|
||||
self.certificate.mode = mode
|
||||
self.certificate.save()
|
||||
tasks.award_course_certificate.delay(
|
||||
self.student.username, str(self.course.id)
|
||||
).get()
|
||||
tasks.award_course_certificate.delay(self.student.username, str(self.course.id)).get()
|
||||
call_args, _ = mock_post_course_certificate.call_args
|
||||
assert call_args[1] == self.student.username
|
||||
assert call_args[2] == self.certificate
|
||||
assert call_args[3] == self.certificate.modified_date
|
||||
|
||||
def test_award_course_certificates_available_date(
|
||||
self, mock_post_course_certificate
|
||||
):
|
||||
def test_award_course_certificates_available_date(self, mock_post_course_certificate):
|
||||
"""
|
||||
Tests the API POST method is called with available date when the course is not self paced
|
||||
"""
|
||||
self.course.self_paced = False
|
||||
self.course.save()
|
||||
tasks.award_course_certificate.delay(
|
||||
self.student.username, str(self.course.id)
|
||||
).get()
|
||||
tasks.award_course_certificate.delay(self.student.username, str(self.course.id)).get()
|
||||
call_args, _ = mock_post_course_certificate.call_args
|
||||
assert call_args[1] == self.student.username
|
||||
assert call_args[2] == self.certificate
|
||||
assert call_args[3] == self.available_date
|
||||
|
||||
def test_award_course_certificates_override_date(
|
||||
self, mock_post_course_certificate
|
||||
):
|
||||
def test_award_course_certificates_override_date(self, mock_post_course_certificate):
|
||||
"""
|
||||
Tests the API POST method is called with date override when present
|
||||
"""
|
||||
self._add_certificate_date_override()
|
||||
tasks.award_course_certificate.delay(
|
||||
self.student.username, str(self.course.id)
|
||||
).get()
|
||||
tasks.award_course_certificate.delay(self.student.username, str(self.course.id)).get()
|
||||
call_args, _ = mock_post_course_certificate.call_args
|
||||
assert call_args[1] == self.student.username
|
||||
assert call_args[2] == self.certificate
|
||||
assert call_args[3] == self.certificate.modified_date
|
||||
assert call_args[4] == self.certificate.date_override.date
|
||||
|
||||
def test_award_course_cert_not_called_if_disabled(
|
||||
self, mock_post_course_certificate
|
||||
):
|
||||
def test_award_course_cert_not_called_if_disabled(self, mock_post_course_certificate):
|
||||
"""
|
||||
Test that the post method is never called if the config is disabled
|
||||
"""
|
||||
self.create_credentials_config(enabled=False)
|
||||
with mock.patch(TASKS_MODULE + ".LOGGER.warning") as mock_warning:
|
||||
with pytest.raises(MaxRetriesExceededError):
|
||||
tasks.award_course_certificate.delay(
|
||||
self.student.username, str(self.course.id)
|
||||
).get()
|
||||
tasks.award_course_certificate.delay(self.student.username, str(self.course.id)).get()
|
||||
assert mock_warning.called
|
||||
assert not mock_post_course_certificate.called
|
||||
|
||||
def test_award_course_cert_not_called_if_user_not_found(
|
||||
self, mock_post_course_certificate
|
||||
):
|
||||
def test_award_course_cert_not_called_if_user_not_found(self, mock_post_course_certificate):
|
||||
"""
|
||||
Test that the post method is never called if the user isn't found by username
|
||||
"""
|
||||
with mock.patch(TASKS_MODULE + ".LOGGER.exception") as mock_exception:
|
||||
# Use a random username here since this user won't be found in the DB
|
||||
tasks.award_course_certificate.delay(
|
||||
"random_username", str(self.course.id)
|
||||
).get()
|
||||
tasks.award_course_certificate.delay("random_username", str(self.course.id)).get()
|
||||
assert mock_exception.called
|
||||
assert not mock_post_course_certificate.called
|
||||
|
||||
def test_award_course_cert_not_called_if_certificate_not_found(
|
||||
self, mock_post_course_certificate
|
||||
):
|
||||
def test_award_course_cert_not_called_if_certificate_not_found(self, mock_post_course_certificate):
|
||||
"""
|
||||
Test that the post method is never called if the certificate doesn't exist for the user and course
|
||||
"""
|
||||
self.certificate.delete()
|
||||
with mock.patch(TASKS_MODULE + ".LOGGER.exception") as mock_exception:
|
||||
tasks.award_course_certificate.delay(
|
||||
self.student.username, str(self.course.id)
|
||||
).get()
|
||||
tasks.award_course_certificate.delay(self.student.username, str(self.course.id)).get()
|
||||
assert mock_exception.called
|
||||
assert not mock_post_course_certificate.called
|
||||
|
||||
def test_award_course_cert_not_called_if_course_overview_not_found(
|
||||
self, mock_post_course_certificate
|
||||
):
|
||||
def test_award_course_cert_not_called_if_course_overview_not_found(self, mock_post_course_certificate):
|
||||
"""
|
||||
Test that the post method is never called if the CourseOverview isn't found
|
||||
"""
|
||||
self.course.delete()
|
||||
with mock.patch(TASKS_MODULE + ".LOGGER.exception") as mock_exception:
|
||||
# Use the certificate course id here since the course will be deleted
|
||||
tasks.award_course_certificate.delay(
|
||||
self.student.username, str(self.certificate.course_id)
|
||||
).get()
|
||||
tasks.award_course_certificate.delay(self.student.username, str(self.certificate.course_id)).get()
|
||||
assert mock_exception.called
|
||||
assert not mock_post_course_certificate.called
|
||||
|
||||
def test_award_course_cert_not_called_if_certificated_not_verified_mode(
|
||||
self, mock_post_course_certificate
|
||||
):
|
||||
def test_award_course_cert_not_called_if_certificated_not_verified_mode(self, mock_post_course_certificate):
|
||||
"""
|
||||
Test that the post method is never called if the GeneratedCertificate is an 'audit' cert
|
||||
"""
|
||||
@@ -717,9 +645,7 @@ class AwardCourseCertificatesTestCase(CredentialsApiConfigMixin, TestCase):
|
||||
self.certificate.save()
|
||||
self.create_credentials_config()
|
||||
|
||||
tasks.award_course_certificate.delay(
|
||||
self.student.username, str(self.certificate.course_id)
|
||||
).get()
|
||||
tasks.award_course_certificate.delay(self.student.username, str(self.certificate.course_id)).get()
|
||||
assert not mock_post_course_certificate.called
|
||||
|
||||
|
||||
@@ -765,9 +691,7 @@ class RevokeProgramCertificateTestCase(TestCase):
|
||||
@mock.patch(TASKS_MODULE + ".get_certified_programs")
|
||||
@mock.patch(TASKS_MODULE + ".get_inverted_programs")
|
||||
@override_settings(CREDENTIALS_SERVICE_USERNAME="test-service-username")
|
||||
class RevokeProgramCertificatesTestCase(
|
||||
CatalogIntegrationMixin, CredentialsApiConfigMixin, TestCase
|
||||
):
|
||||
class RevokeProgramCertificatesTestCase(CatalogIntegrationMixin, CredentialsApiConfigMixin, TestCase):
|
||||
"""
|
||||
Tests for the 'revoke_program_certificates' celery task.
|
||||
"""
|
||||
@@ -811,9 +735,7 @@ class RevokeProgramCertificatesTestCase(
|
||||
Checks that the Programs API is used correctly to determine completed
|
||||
programs.
|
||||
"""
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
mock_get_inverted_programs.assert_any_call(self.student)
|
||||
|
||||
def test_revoke_program_certificate(
|
||||
@@ -827,14 +749,10 @@ class RevokeProgramCertificatesTestCase(
|
||||
the proper programs.
|
||||
"""
|
||||
expected_program_uuid = 1
|
||||
mock_get_inverted_programs.return_value = {
|
||||
self.course_key: [{"uuid": expected_program_uuid}]
|
||||
}
|
||||
mock_get_inverted_programs.return_value = {self.course_key: [{"uuid": expected_program_uuid}]}
|
||||
mock_get_certified_programs.return_value = [expected_program_uuid]
|
||||
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
|
||||
call_args, _ = mock_revoke_program_certificate.call_args
|
||||
assert call_args[1] == self.student.username
|
||||
@@ -844,21 +762,15 @@ class RevokeProgramCertificatesTestCase(
|
||||
("credentials", "enable_learner_issuance"),
|
||||
)
|
||||
@ddt.unpack
|
||||
def test_retry_if_config_disabled(
|
||||
self, disabled_config_type, disabled_config_attribute, *mock_helpers
|
||||
):
|
||||
def test_retry_if_config_disabled(self, disabled_config_type, disabled_config_attribute, *mock_helpers):
|
||||
"""
|
||||
Checks that the task is aborted if any relevant api configs are
|
||||
disabled.
|
||||
"""
|
||||
getattr(self, f"create_{disabled_config_type}_config")(
|
||||
**{disabled_config_attribute: False}
|
||||
)
|
||||
getattr(self, f"create_{disabled_config_type}_config")(**{disabled_config_attribute: False})
|
||||
with mock.patch(TASKS_MODULE + ".LOGGER.warning") as mock_warning:
|
||||
with pytest.raises(MaxRetriesExceededError):
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
assert mock_warning.called
|
||||
for mock_helper in mock_helpers:
|
||||
assert not mock_helper.called
|
||||
@@ -869,9 +781,7 @@ class RevokeProgramCertificatesTestCase(
|
||||
passed was not found, and that an exception is logged.
|
||||
"""
|
||||
with mock.patch(TASKS_MODULE + ".LOGGER.exception") as mock_exception:
|
||||
tasks.revoke_program_certificates.delay(
|
||||
"nonexistent-username", self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay("nonexistent-username", self.course_key).get()
|
||||
assert mock_exception.called
|
||||
for mock_helper in mock_helpers:
|
||||
assert not mock_helper.called
|
||||
@@ -887,9 +797,7 @@ class RevokeProgramCertificatesTestCase(
|
||||
not part of any program.
|
||||
"""
|
||||
mock_get_inverted_programs.return_value = {}
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
assert mock_get_inverted_programs.called
|
||||
assert not mock_get_certified_programs.called
|
||||
assert not mock_revoke_program_certificate.called
|
||||
@@ -908,16 +816,12 @@ class RevokeProgramCertificatesTestCase(
|
||||
"""
|
||||
mock_get_inverted_programs.return_value = self.inverted_programs
|
||||
mock_get_certified_programs.side_effect = [[1], [1, 2]]
|
||||
mock_revoke_program_certificate.side_effect = self._make_side_effect(
|
||||
[Exception("boom"), None]
|
||||
)
|
||||
mock_revoke_program_certificate.side_effect = self._make_side_effect([Exception("boom"), None])
|
||||
|
||||
with mock.patch(TASKS_MODULE + ".LOGGER.info") as mock_info, mock.patch(
|
||||
TASKS_MODULE + ".LOGGER.warning"
|
||||
) as mock_warning:
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
|
||||
assert mock_revoke_program_certificate.call_count == 3
|
||||
mock_warning.assert_called_once_with(
|
||||
@@ -925,12 +829,8 @@ class RevokeProgramCertificatesTestCase(
|
||||
uuid=1, username=self.student.username
|
||||
)
|
||||
)
|
||||
mock_info.assert_any_call(
|
||||
f"Revoked certificate for program {1} for user {self.student.username}"
|
||||
)
|
||||
mock_info.assert_any_call(
|
||||
f"Revoked certificate for program {2} for user {self.student.username}"
|
||||
)
|
||||
mock_info.assert_any_call(f"Revoked certificate for program {1} for user {self.student.username}")
|
||||
mock_info.assert_any_call(f"Revoked certificate for program {2} for user {self.student.username}")
|
||||
|
||||
def test_retry_on_credentials_api_errors(
|
||||
self,
|
||||
@@ -946,12 +846,8 @@ class RevokeProgramCertificatesTestCase(
|
||||
"""
|
||||
mock_get_inverted_programs.return_value = self.inverted_programs
|
||||
mock_get_certified_programs.return_value = [1]
|
||||
mock_get_certified_programs.side_effect = self._make_side_effect(
|
||||
[Exception("boom"), None]
|
||||
)
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
mock_get_certified_programs.side_effect = self._make_side_effect([Exception("boom"), None])
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
assert mock_get_certified_programs.call_count == 2
|
||||
assert mock_revoke_program_certificate.call_count == 1
|
||||
|
||||
@@ -968,13 +864,9 @@ class RevokeProgramCertificatesTestCase(
|
||||
exception.response = mock.Mock(status_code=429)
|
||||
mock_get_inverted_programs.return_value = self.inverted_programs
|
||||
mock_get_certified_programs.return_value = [1, 2]
|
||||
mock_revoke_program_certificate.side_effect = self._make_side_effect(
|
||||
[exception, None]
|
||||
)
|
||||
mock_revoke_program_certificate.side_effect = self._make_side_effect([exception, None])
|
||||
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
|
||||
assert mock_revoke_program_certificate.call_count == 3
|
||||
|
||||
@@ -991,13 +883,9 @@ class RevokeProgramCertificatesTestCase(
|
||||
exception.response = mock.Mock(status_code=404)
|
||||
mock_get_inverted_programs.return_value = self.inverted_programs
|
||||
mock_get_certified_programs.return_value = [1, 2]
|
||||
mock_revoke_program_certificate.side_effect = self._make_side_effect(
|
||||
[exception, None]
|
||||
)
|
||||
mock_revoke_program_certificate.side_effect = self._make_side_effect([exception, None])
|
||||
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
|
||||
assert mock_revoke_program_certificate.call_count == 2
|
||||
|
||||
@@ -1014,13 +902,9 @@ class RevokeProgramCertificatesTestCase(
|
||||
exception.response = mock.Mock(status_code=418)
|
||||
mock_get_inverted_programs.return_value = self.inverted_programs
|
||||
mock_get_certified_programs.return_value = [1, 2]
|
||||
mock_revoke_program_certificate.side_effect = self._make_side_effect(
|
||||
[exception, None]
|
||||
)
|
||||
mock_revoke_program_certificate.side_effect = self._make_side_effect([exception, None])
|
||||
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
|
||||
assert mock_revoke_program_certificate.call_count == 2
|
||||
|
||||
@@ -1036,16 +920,12 @@ class RevokeProgramCertificatesTestCase(
|
||||
mock_get_inverted_programs.return_value = self.inverted_programs
|
||||
mock_get_certified_programs.return_value = [1, 2]
|
||||
|
||||
with mock.patch(
|
||||
TASKS_MODULE + ".get_credentials_api_client"
|
||||
) as mock_get_api_client, mock.patch(
|
||||
with mock.patch(TASKS_MODULE + ".get_credentials_api_client") as mock_get_api_client, mock.patch(
|
||||
TASKS_MODULE + ".LOGGER.exception"
|
||||
) as mock_exception:
|
||||
mock_get_api_client.side_effect = Exception("boom")
|
||||
with pytest.raises(MaxRetriesExceededError):
|
||||
tasks.revoke_program_certificates.delay(
|
||||
self.student.username, self.course_key
|
||||
).get()
|
||||
tasks.revoke_program_certificates.delay(self.student.username, self.course_key).get()
|
||||
assert mock_exception.called
|
||||
assert mock_get_api_client.call_count == (tasks.MAX_RETRIES + 1)
|
||||
assert not mock_revoke_program_certificate.called
|
||||
@@ -1070,9 +950,7 @@ class UpdateCredentialsCourseCertificateConfigurationAvailableDateTestCase(TestC
|
||||
self.credentials_worker = UserFactory(username="test-service-username")
|
||||
|
||||
def test_update_course_cert_available_date(self):
|
||||
with mock.patch(
|
||||
TASKS_MODULE + ".post_course_certificate_configuration"
|
||||
) as update_posted:
|
||||
with mock.patch(TASKS_MODULE + ".post_course_certificate_configuration") as update_posted:
|
||||
tasks.update_credentials_course_certificate_configuration_available_date(
|
||||
self.course_id, self.available_date
|
||||
)
|
||||
@@ -1080,9 +958,7 @@ class UpdateCredentialsCourseCertificateConfigurationAvailableDateTestCase(TestC
|
||||
|
||||
def test_course_with_two_paid_modes(self):
|
||||
CourseModeFactory.create(course_id=self.course.id, mode_slug="professional")
|
||||
with mock.patch(
|
||||
TASKS_MODULE + ".post_course_certificate_configuration"
|
||||
) as update_posted:
|
||||
with mock.patch(TASKS_MODULE + ".post_course_certificate_configuration") as update_posted:
|
||||
tasks.update_credentials_course_certificate_configuration_available_date(
|
||||
self.course_id, self.available_date
|
||||
)
|
||||
@@ -1119,9 +995,7 @@ class PostCourseCertificateConfigurationTestCase(TestCase):
|
||||
|
||||
available_date = datetime.now().strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
tasks.post_course_certificate_configuration(
|
||||
test_client, self.certificate, available_date
|
||||
)
|
||||
tasks.post_course_certificate_configuration(test_client, self.certificate, available_date)
|
||||
|
||||
expected_body = {
|
||||
"course_id": "testCourse",
|
||||
@@ -1134,9 +1008,7 @@ class PostCourseCertificateConfigurationTestCase(TestCase):
|
||||
|
||||
|
||||
@skip_unless_lms
|
||||
class UpdateCertificateVisibleDatesOnCourseUpdateTestCase(
|
||||
CredentialsApiConfigMixin, TestCase
|
||||
):
|
||||
class UpdateCertificateVisibleDatesOnCourseUpdateTestCase(CredentialsApiConfigMixin, TestCase):
|
||||
"""
|
||||
Tests for the `update_certificate_visible_date_on_course_update` task.
|
||||
"""
|
||||
@@ -1183,9 +1055,7 @@ class UpdateCertificateVisibleDatesOnCourseUpdateTestCase(
|
||||
exception when the max number of retries has reached.
|
||||
"""
|
||||
with pytest.raises(MaxRetriesExceededError):
|
||||
tasks.update_certificate_visible_date_on_course_update(
|
||||
self.course.id
|
||||
) # pylint: disable=no-value-for-parameter
|
||||
tasks.update_certificate_visible_date_on_course_update(self.course.id) # pylint: disable=no-value-for-parameter
|
||||
|
||||
def test_update_visible_dates(self):
|
||||
"""
|
||||
@@ -1199,20 +1069,14 @@ class UpdateCertificateVisibleDatesOnCourseUpdateTestCase(
|
||||
self.credentials_api_config.enabled = True
|
||||
self.credentials_api_config.enable_learner_issuance = True
|
||||
|
||||
with mock.patch(
|
||||
f"{TASKS_MODULE}.award_course_certificate.delay"
|
||||
) as award_course_cert:
|
||||
tasks.update_certificate_visible_date_on_course_update(
|
||||
self.course.id
|
||||
) # pylint: disable=no-value-for-parameter
|
||||
with mock.patch(f"{TASKS_MODULE}.award_course_certificate.delay") as award_course_cert:
|
||||
tasks.update_certificate_visible_date_on_course_update(self.course.id) # pylint: disable=no-value-for-parameter
|
||||
|
||||
assert award_course_cert.call_count == 3
|
||||
|
||||
|
||||
@skip_unless_lms
|
||||
class UpdateCertificateAvailableDateOnCourseUpdateTestCase(
|
||||
CredentialsApiConfigMixin, TestCase
|
||||
):
|
||||
class UpdateCertificateAvailableDateOnCourseUpdateTestCase(CredentialsApiConfigMixin, TestCase):
|
||||
"""
|
||||
Tests for the `update_certificate_available_date_on_course_update` task.
|
||||
"""
|
||||
@@ -1236,9 +1100,7 @@ class UpdateCertificateAvailableDateOnCourseUpdateTestCase(
|
||||
course = CourseOverviewFactory.create()
|
||||
|
||||
with pytest.raises(MaxRetriesExceededError):
|
||||
tasks.update_certificate_available_date_on_course_update(
|
||||
course.id
|
||||
) # pylint: disable=no-value-for-parameter
|
||||
tasks.update_certificate_available_date_on_course_update(course.id) # pylint: disable=no-value-for-parameter
|
||||
|
||||
def test_update_certificate_available_date_with_self_paced_course(self):
|
||||
"""
|
||||
@@ -1251,20 +1113,14 @@ class UpdateCertificateAvailableDateOnCourseUpdateTestCase(
|
||||
self.credentials_api_config.enabled = True
|
||||
self.credentials_api_config.enable_learner_issuance = True
|
||||
|
||||
course = CourseOverviewFactory.create(
|
||||
self_paced=True, certificate_available_date=None
|
||||
)
|
||||
course = CourseOverviewFactory.create(self_paced=True, certificate_available_date=None)
|
||||
|
||||
with mock.patch(
|
||||
f"{TASKS_MODULE}.update_credentials_course_certificate_configuration_available_date.delay"
|
||||
) as update_credentials_course_cert_config:
|
||||
tasks.update_certificate_available_date_on_course_update(
|
||||
course.id
|
||||
) # pylint: disable=no-value-for-parameter
|
||||
tasks.update_certificate_available_date_on_course_update(course.id) # pylint: disable=no-value-for-parameter
|
||||
|
||||
update_credentials_course_cert_config.assert_called_once_with(
|
||||
str(course.id), None
|
||||
)
|
||||
update_credentials_course_cert_config.assert_called_once_with(str(course.id), None)
|
||||
|
||||
def test_update_certificate_available_date_with_instructor_paced_course(self):
|
||||
"""
|
||||
@@ -1288,13 +1144,9 @@ class UpdateCertificateAvailableDateOnCourseUpdateTestCase(
|
||||
with mock.patch(
|
||||
f"{TASKS_MODULE}.update_credentials_course_certificate_configuration_available_date.delay"
|
||||
) as update_credentials_course_cert_config:
|
||||
tasks.update_certificate_available_date_on_course_update(
|
||||
course.id
|
||||
) # pylint: disable=no-value-for-parameter
|
||||
tasks.update_certificate_available_date_on_course_update(course.id) # pylint: disable=no-value-for-parameter
|
||||
|
||||
update_credentials_course_cert_config.assert_called_once_with(
|
||||
str(course.id), str(available_date)
|
||||
)
|
||||
update_credentials_course_cert_config.assert_called_once_with(str(course.id), str(available_date))
|
||||
|
||||
def test_update_certificate_available_date_with_expect_no_update(self):
|
||||
"""
|
||||
@@ -1318,9 +1170,7 @@ class UpdateCertificateAvailableDateOnCourseUpdateTestCase(
|
||||
)
|
||||
|
||||
with LogCapture(level=logging.WARNING) as log_capture:
|
||||
tasks.update_certificate_available_date_on_course_update(
|
||||
course.id
|
||||
) # pylint: disable=no-value-for-parameter
|
||||
tasks.update_certificate_available_date_on_course_update(course.id) # pylint: disable=no-value-for-parameter
|
||||
|
||||
assert len(log_capture.records) == 1
|
||||
assert log_capture.records[0].getMessage() == expected_message
|
||||
|
||||
Reference in New Issue
Block a user