Coverage for src/lexigram/notification/exceptions.py: 70%

43 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-26 07:17 +0800

1"""lexigram-notification leaf exceptions.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6 

7from lexigram.contracts.exceptions.domain import DomainError 

8from lexigram.contracts.mailer.errors import MailerError 

9from lexigram.contracts.notification.errors import NotificationError 

10 

11 

12class TwilioNotificationError(NotificationError): 

13 """Twilio SMS delivery failure.""" 

14 

15 _code = "LEX_ERR_NOTIF_003" 

16 

17 def __init__( 

18 self, 

19 message: str = "Twilio SMS error", 

20 *, 

21 twilio_code: int | None = None, 

22 **kwargs: Any, 

23 ) -> None: 

24 super().__init__(message, channel="sms", backend="twilio", **kwargs) 

25 self.twilio_code = twilio_code 

26 

27 

28class FCMNotificationError(NotificationError): 

29 """FCM push notification delivery failure.""" 

30 

31 _code = "LEX_ERR_NOTIF_004" 

32 

33 def __init__( 

34 self, 

35 message: str = "FCM push error", 

36 *, 

37 fcm_error: str | None = None, 

38 **kwargs: Any, 

39 ) -> None: 

40 super().__init__(message, channel="push", backend="fcm", **kwargs) 

41 self.fcm_error = fcm_error 

42 

43 

44class APNsNotificationError(NotificationError): 

45 """APNs push notification delivery failure.""" 

46 

47 _code = "LEX_ERR_NOTIF_005" 

48 

49 def __init__( 

50 self, 

51 message: str = "APNs push error", 

52 *, 

53 apns_reason: str | None = None, 

54 **kwargs: Any, 

55 ) -> None: 

56 super().__init__(message, channel="push", backend="apns", **kwargs) 

57 self.apns_reason = apns_reason 

58 

59 

60class WebPushNotificationError(NotificationError): 

61 """Web Push notification delivery failure.""" 

62 

63 _code = "LEX_ERR_NOTIF_011" 

64 

65 def __init__( 

66 self, 

67 message: str = "Web Push error", 

68 *, 

69 status_code: int = 0, 

70 reason: str | None = None, 

71 **kwargs: Any, 

72 ) -> None: 

73 super().__init__(message, channel="push", backend="web_push", **kwargs) 

74 self.status_code = status_code 

75 self.reason = reason 

76 

77 

78# === Mail Exceptions (merged from mail/exceptions.py) === 

79 

80 

81class SMTPMailerError(MailerError): 

82 """SMTP-specific delivery failure (rejection, auth error, etc.).""" 

83 

84 _code = "LEX_ERR_NOTIF_006" 

85 

86 def __init__( 

87 self, 

88 message: str = "SMTP delivery error", 

89 *, 

90 smtp_code: int | None = None, 

91 **kwargs: Any, 

92 ) -> None: 

93 super().__init__(message, backend="smtp", **kwargs) 

94 self.smtp_code = smtp_code 

95 

96 

97class SendGridMailerError(MailerError): 

98 """SendGrid API delivery failure.""" 

99 

100 _code = "LEX_ERR_NOTIF_007" 

101 

102 def __init__( 

103 self, 

104 message: str = "SendGrid delivery error", 

105 *, 

106 status_code: int | None = None, 

107 **kwargs: Any, 

108 ) -> None: 

109 super().__init__(message, backend="sendgrid", **kwargs) 

110 self.status_code = status_code 

111 

112 

113# === Inbox Exceptions (merged from inbox/exceptions.py) === 

114 

115 

116class InboxError(DomainError): 

117 """Base exception for all inbox submodule errors.""" 

118 

119 _code: str = "LEX_ERR_NOTIF_008" 

120 

121 

122class InboxMessageNotFoundError(InboxError): 

123 """Raised when a requested inbox message does not exist.""" 

124 

125 _code: str = "LEX_ERR_NOTIF_009" 

126 

127 

128class InboxPermissionError(InboxError): 

129 """Raised when a user attempts to access another user's inbox messages.""" 

130 

131 _code: str = "LEX_ERR_NOTIF_010" 

132 

133 

134__all__ = [ 

135 "APNsNotificationError", 

136 "FCMNotificationError", 

137 "InboxError", 

138 "InboxMessageNotFoundError", 

139 "InboxPermissionError", 

140 "SMTPMailerError", 

141 "SendGridMailerError", 

142 "TwilioNotificationError", 

143 "WebPushNotificationError", 

144]