Coverage for benefits/enrollment/enrollment.py: 95%

46 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-24 15:18 +0000

1from datetime import datetime, timedelta 

2from enum import Enum 

3 

4from django.shortcuts import redirect 

5from django.utils import timezone 

6import sentry_sdk 

7 

8from benefits.routes import routes 

9from benefits.core import models, session 

10from . import analytics 

11 

12 

13class Status(Enum): 

14 # SUCCESS means the enrollment went through successfully 

15 SUCCESS = 1 

16 

17 # SYSTEM_ERROR means the enrollment system encountered an internal error (returned a 500 HTTP status) 

18 SYSTEM_ERROR = 2 

19 

20 # EXCEPTION means the enrollment system is working, but something unexpected happened 

21 # because of a misconfiguration or invalid request from our side 

22 EXCEPTION = 3 

23 

24 # REENROLLMENT_ERROR means that the user tried to re-enroll but is not within the reenrollment window 

25 REENROLLMENT_ERROR = 4 

26 

27 

28def _is_expired(expiry_date: datetime): 

29 """Returns whether the passed in datetime is expired or not.""" 

30 return expiry_date <= timezone.now() 

31 

32 

33def _is_within_reenrollment_window(expiry_date: datetime, enrollment_reenrollment_date: datetime): 

34 """Returns if we are currently within the reenrollment window.""" 

35 return enrollment_reenrollment_date <= timezone.now() < expiry_date 

36 

37 

38def _calculate_expiry(expiration_days: int): 

39 """Returns the expiry datetime, which should be midnight in our configured timezone of the (N + 1)th day from now, 

40 where N is expiration_days.""" 

41 default_time_zone = timezone.get_default_timezone() 

42 expiry_date = timezone.localtime(timezone=default_time_zone) + timedelta(days=expiration_days + 1) 

43 expiry_datetime = expiry_date.replace(hour=0, minute=0, second=0, microsecond=0) 

44 

45 return expiry_datetime 

46 

47 

48def handle_enrollment_results( 

49 request, 

50 status: Status, 

51 verified_by: str, 

52 exception: Exception = None, 

53 enrollment_method: str = models.EnrollmentMethods.DIGITAL, 

54 route_reenrollment_error=routes.ENROLLMENT_REENROLLMENT_ERROR, 

55 route_success=routes.ENROLLMENT_SUCCESS, 

56 route_system_error=routes.ENROLLMENT_SYSTEM_ERROR, 

57): 

58 match (status): 

59 case Status.SUCCESS: 

60 agency = session.agency(request) 

61 flow = session.flow(request) 

62 expiry = session.enrollment_expiry(request) 

63 oauth_extra_claims = session.oauth_extra_claims(request) 

64 # EnrollmentEvent expects a string value for extra_claims 

65 if oauth_extra_claims: 65 ↛ 68line 65 didn't jump to line 68 because the condition on line 65 was always true

66 str_extra_claims = ", ".join(oauth_extra_claims) 

67 else: 

68 str_extra_claims = "" 

69 event = models.EnrollmentEvent.objects.create( 

70 transit_agency=agency, 

71 enrollment_flow=flow, 

72 enrollment_method=enrollment_method, 

73 verified_by=verified_by, 

74 expiration_datetime=expiry, 

75 extra_claims=str_extra_claims, 

76 ) 

77 event.save() 

78 analytics.returned_success( 

79 request, enrollment_group=flow.group_id, enrollment_method=enrollment_method, extra_claims=oauth_extra_claims 

80 ) 

81 return redirect(route_success) 

82 

83 case Status.SYSTEM_ERROR: 

84 analytics.returned_error(request, str(exception), enrollment_method=enrollment_method) 

85 sentry_sdk.capture_exception(exception) 

86 return redirect(route_system_error) 

87 

88 case Status.EXCEPTION: 

89 analytics.returned_error(request, str(exception), enrollment_method=enrollment_method) 

90 raise exception 

91 

92 case Status.REENROLLMENT_ERROR: 92 ↛ exitline 92 didn't return from function 'handle_enrollment_results' because the pattern on line 92 always matched

93 analytics.returned_error(request, "Re-enrollment error.", enrollment_method=enrollment_method) 

94 return redirect(route_reenrollment_error)