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

47 statements  

« prev     ^ index     » next       coverage.py v7.11.3, created at 2025-11-15 00:14 +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 card_category: str = None, 

58 card_scheme: str = None, 

59): 

60 flow = session.flow(request) 

61 agency = session.agency(request) 

62 match (status): 

63 case Status.SUCCESS: 

64 agency = session.agency(request) 

65 expiry = session.enrollment_expiry(request) 

66 oauth_extra_claims = session.oauth_extra_claims(request) 

67 # EnrollmentEvent expects a string value for extra_claims 

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

69 str_extra_claims = ", ".join(oauth_extra_claims) 

70 else: 

71 str_extra_claims = "" 

72 event = models.EnrollmentEvent.objects.create( 

73 transit_agency=agency, 

74 enrollment_flow=flow, 

75 enrollment_method=enrollment_method, 

76 verified_by=verified_by, 

77 expiration_datetime=expiry, 

78 extra_claims=str_extra_claims, 

79 ) 

80 event.save() 

81 analytics.returned_success( 

82 request, 

83 enrollment_group=flow.group_id, 

84 transit_processor=agency.transit_processor, 

85 enrollment_method=enrollment_method, 

86 extra_claims=oauth_extra_claims, 

87 card_scheme=card_scheme, 

88 card_category=card_category, 

89 ) 

90 return redirect(route_success) 

91 

92 case Status.SYSTEM_ERROR: 

93 analytics.returned_error( 

94 request, 

95 str(exception), 

96 enrollment_group=flow.group_id, 

97 transit_processor=agency.transit_processor, 

98 enrollment_method=enrollment_method, 

99 ) 

100 sentry_sdk.capture_exception(exception) 

101 return redirect(route_system_error) 

102 

103 case Status.EXCEPTION: 

104 analytics.returned_error( 

105 request, 

106 str(exception), 

107 enrollment_group=flow.group_id, 

108 transit_processor=agency.transit_processor, 

109 enrollment_method=enrollment_method, 

110 ) 

111 raise exception 

112 

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

114 analytics.returned_error( 

115 request, 

116 "Re-enrollment error.", 

117 enrollment_group=flow.group_id, 

118 transit_processor=agency.transit_processor, 

119 enrollment_method=enrollment_method, 

120 ) 

121 return redirect(route_reenrollment_error)