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

47 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-13 19:35 +0000

1from datetime import datetime, timedelta 

2from enum import Enum 

3 

4import sentry_sdk 

5from django.shortcuts import redirect 

6from django.utils import timezone 

7 

8from benefits.core import models, session 

9from benefits.routes import routes 

10 

11from . import analytics 

12 

13 

14class Status(Enum): 

15 # SUCCESS means the enrollment went through successfully 

16 SUCCESS = 1 

17 

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

19 SYSTEM_ERROR = 2 

20 

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

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

23 EXCEPTION = 3 

24 

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

26 REENROLLMENT_ERROR = 4 

27 

28 

29def _is_expired(expiry_date: datetime): 

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

31 return expiry_date <= timezone.now() 

32 

33 

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

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

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

37 

38 

39def _calculate_expiry(expiration_days: int): 

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

41 where N is expiration_days.""" 

42 default_time_zone = timezone.get_default_timezone() 

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

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

45 

46 return expiry_datetime 

47 

48 

49def handle_enrollment_results( 

50 request, 

51 status: Status, 

52 verified_by: str, 

53 exception: Exception = None, 

54 enrollment_method: str = models.EnrollmentMethods.DIGITAL, 

55 route_reenrollment_error=routes.ENROLLMENT_REENROLLMENT_ERROR, 

56 route_success=routes.ENROLLMENT_SUCCESS, 

57 route_system_error=routes.ENROLLMENT_SYSTEM_ERROR, 

58 card_category: str = None, 

59 card_scheme: str = None, 

60): 

61 flow = session.flow(request) 

62 agency = session.agency(request) 

63 match (status): 

64 case Status.SUCCESS: 

65 agency = session.agency(request) 

66 expiry = session.enrollment_expiry(request) 

67 oauth_extra_claims = session.oauth_extra_claims(request) 

68 # EnrollmentEvent expects a string value for extra_claims 

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

70 str_extra_claims = ", ".join(oauth_extra_claims) 

71 else: 

72 str_extra_claims = "" 

73 event = models.EnrollmentEvent.objects.create( 

74 transit_agency=agency, 

75 enrollment_flow=flow, 

76 enrollment_method=enrollment_method, 

77 verified_by=verified_by, 

78 expiration_datetime=expiry, 

79 extra_claims=str_extra_claims, 

80 ) 

81 event.save() 

82 analytics.returned_success( 

83 request, 

84 enrollment_group=flow.group_id, 

85 transit_processor=agency.transit_processor, 

86 enrollment_method=enrollment_method, 

87 extra_claims=oauth_extra_claims, 

88 card_scheme=card_scheme, 

89 card_category=card_category, 

90 ) 

91 return redirect(route_success) 

92 

93 case Status.SYSTEM_ERROR: 

94 analytics.returned_error( 

95 request, 

96 str(exception), 

97 enrollment_group=flow.group_id, 

98 transit_processor=agency.transit_processor, 

99 enrollment_method=enrollment_method, 

100 ) 

101 sentry_sdk.capture_exception(exception) 

102 return redirect(route_system_error) 

103 

104 case Status.EXCEPTION: 

105 analytics.returned_error( 

106 request, 

107 str(exception), 

108 enrollment_group=flow.group_id, 

109 transit_processor=agency.transit_processor, 

110 enrollment_method=enrollment_method, 

111 ) 

112 raise exception 

113 

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

115 analytics.returned_error( 

116 request, 

117 "Re-enrollment error.", 

118 enrollment_group=flow.group_id, 

119 transit_processor=agency.transit_processor, 

120 enrollment_method=enrollment_method, 

121 ) 

122 return redirect(route_reenrollment_error)