cuenca-validations 2.1.41.dev0__tar.gz → 2.1.41.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/PKG-INFO +1 -1
  2. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/__init__.py +22 -0
  3. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/enums.py +10 -0
  4. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/queries.py +4 -0
  5. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/requests.py +145 -0
  6. cuenca_validations-2.1.41.dev2/cuenca_validations/version.py +1 -0
  7. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations.egg-info/PKG-INFO +1 -1
  8. cuenca_validations-2.1.41.dev2/tests/test_requests.py +314 -0
  9. cuenca_validations-2.1.41.dev0/cuenca_validations/version.py +0 -1
  10. cuenca_validations-2.1.41.dev0/tests/test_requests.py +0 -130
  11. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/LICENSE +0 -0
  12. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/README.md +0 -0
  13. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/__init__.py +0 -0
  14. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/card_bins.py +0 -0
  15. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/errors.py +0 -0
  16. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/py.typed +0 -0
  17. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/card.py +0 -0
  18. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/files.py +0 -0
  19. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/general.py +0 -0
  20. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/helpers.py +0 -0
  21. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/identities.py +0 -0
  22. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/types/morals.py +0 -0
  23. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/typing.py +0 -0
  24. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations/validators.py +0 -0
  25. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations.egg-info/SOURCES.txt +0 -0
  26. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations.egg-info/dependency_links.txt +0 -0
  27. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations.egg-info/requires.txt +0 -0
  28. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/cuenca_validations.egg-info/top_level.txt +0 -0
  29. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/setup.cfg +0 -0
  30. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/setup.py +0 -0
  31. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/tests/__init__.py +0 -0
  32. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/tests/test_card.py +0 -0
  33. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/tests/test_errors.py +0 -0
  34. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/tests/test_helpers.py +0 -0
  35. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/tests/test_statement.py +0 -0
  36. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/tests/test_types.py +0 -0
  37. {cuenca_validations-2.1.41.dev0 → cuenca_validations-2.1.41.dev2}/tests/test_validators.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuenca_validations
3
- Version: 2.1.41.dev0
3
+ Version: 2.1.41.dev2
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -60,11 +60,20 @@ __all__ = [
60
60
  'KYCValidationRequest',
61
61
  'KYCValidationSource',
62
62
  'Language',
63
+ 'LegalPersonRequest',
64
+ 'LegalPersonUpdateRequest',
63
65
  'LimitedWalletRequest',
64
66
  'MonthlyMovementsType',
65
67
  'MonthlySpendingType',
66
68
  'OperationalEventAction',
67
69
  'OperationalEventQuery',
70
+ 'OperatorLoginRequest',
71
+ 'OperatorLoginResponse',
72
+ 'OperatorQuery',
73
+ 'OperatorRequest',
74
+ 'OperatorRole',
75
+ 'OperatorStatus',
76
+ 'OperatorUpdateRequest',
68
77
  'PartnerRequest',
69
78
  'PartnerUpdateRequest',
70
79
  'PasswordResetRequest',
@@ -82,8 +91,10 @@ __all__ = [
82
91
  'SavingRequest',
83
92
  'SavingUpdateRequest',
84
93
  'ServiceProviderCategory',
94
+ 'SessionMetadata',
85
95
  'SessionQuery',
86
96
  'SessionRequest',
97
+ 'SessionResponse',
87
98
  'SessionType',
88
99
  'State',
89
100
  'StatementQuery',
@@ -162,6 +173,8 @@ from .enums import (
162
173
  MonthlyMovementsType,
163
174
  MonthlySpendingType,
164
175
  OperationalEventAction,
176
+ OperatorRole,
177
+ OperatorStatus,
165
178
  PlatformType,
166
179
  PosCapability,
167
180
  Profession,
@@ -221,6 +234,7 @@ from .queries import (
221
234
  FileQuery,
222
235
  IdentityQuery,
223
236
  OperationalEventQuery,
237
+ OperatorQuery,
224
238
  PostalCodeQuery,
225
239
  QueryParams,
226
240
  SessionQuery,
@@ -248,7 +262,13 @@ from .requests import (
248
262
  FileUploadRequest,
249
263
  FraudFundsTransferRequest,
250
264
  KYCValidationRequest,
265
+ LegalPersonRequest,
266
+ LegalPersonUpdateRequest,
251
267
  LimitedWalletRequest,
268
+ OperatorLoginRequest,
269
+ OperatorLoginResponse,
270
+ OperatorRequest,
271
+ OperatorUpdateRequest,
252
272
  PartnerRequest,
253
273
  PartnerUpdateRequest,
254
274
  PasswordResetRequest,
@@ -256,7 +276,9 @@ from .requests import (
256
276
  QuestionnairesRequest,
257
277
  SavingRequest,
258
278
  SavingUpdateRequest,
279
+ SessionMetadata,
259
280
  SessionRequest,
281
+ SessionResponse,
260
282
  StrictTransferRequest,
261
283
  TOSRequest,
262
284
  TransferRequest,
@@ -756,3 +756,13 @@ class OperationalEventAction(str, Enum):
756
756
  account_updated = 'account_updated'
757
757
  transfer_created = 'transfer_created'
758
758
  statement_downloaded = 'statement_downloaded'
759
+
760
+
761
+ class OperatorRole(str, Enum):
762
+ operator = 'operator'
763
+ authorizer = 'authorizer'
764
+
765
+
766
+ class OperatorStatus(str, Enum):
767
+ active = 'active'
768
+ disabled = 'disabled'
@@ -159,6 +159,10 @@ class OperationalEventQuery(QueryParams):
159
159
  action: Optional[OperationalEventAction] = None
160
160
 
161
161
 
162
+ class OperatorQuery(QueryParams):
163
+ email: Optional[EmailStr] = None
164
+
165
+
162
166
  class BalanceEntryQuery(QueryParams):
163
167
  funding_instrument_uri: Optional[str] = None
164
168
  wallet_id: str = 'default'
@@ -37,6 +37,8 @@ from ..types.enums import (
37
37
  KYCValidationSource,
38
38
  MonthlyMovementsType,
39
39
  MonthlySpendingType,
40
+ OperatorRole,
41
+ OperatorStatus,
40
42
  PlatformType,
41
43
  PosCapability,
42
44
  Profession,
@@ -630,11 +632,29 @@ class UserLoginRequest(BaseRequest):
630
632
  )
631
633
 
632
634
 
635
+ class SessionMetadata(BaseModel):
636
+ operator_id: Optional[str] = None
637
+ model_config = ConfigDict(extra='forbid')
638
+
639
+
640
+ class SessionResponse(BaseModel):
641
+ id: str
642
+ created_at: dt.datetime
643
+ user_id: str
644
+ platform_id: str
645
+ expires_at: dt.datetime
646
+ type: SessionType
647
+ success_url: Optional[SerializableAnyUrl] = None
648
+ failure_url: Optional[SerializableAnyUrl] = None
649
+ metadata: Optional[SessionMetadata] = None
650
+
651
+
633
652
  class SessionRequest(BaseRequest):
634
653
  user_id: str
635
654
  type: SessionType
636
655
  success_url: Optional[SerializableAnyUrl] = None
637
656
  failure_url: Optional[SerializableAnyUrl] = None
657
+ metadata: Optional[SessionMetadata] = None
638
658
  model_config = ConfigDict(
639
659
  json_schema_extra={
640
660
  'example': {
@@ -897,5 +917,130 @@ class PartnerUpdateRequest(BaseRequest):
897
917
  shareholders: Optional[list[Shareholder]] = None
898
918
 
899
919
 
920
+ class LegalPersonRequest(BaseRequest):
921
+ legal_name: str
922
+ rfc: Rfc
923
+ legal_representatives: list[LegalRepresentative]
924
+
925
+ model_config = ConfigDict(
926
+ json_schema_extra={
927
+ 'example': {
928
+ 'legal_name': 'Aceros del Norte SA de CV',
929
+ 'rfc': 'ADN850101ABC',
930
+ 'legal_representatives': [
931
+ {
932
+ 'names': 'Juan',
933
+ 'first_surname': 'Perez',
934
+ 'job': 'Director General',
935
+ 'phone_number': '+525512345678',
936
+ 'email_address': 'juan.perez@aceros.com',
937
+ 'address': AddressRequest.model_json_schema().get(
938
+ 'example'
939
+ ),
940
+ }
941
+ ],
942
+ }
943
+ },
944
+ )
945
+
946
+ @field_validator('rfc')
947
+ @classmethod
948
+ def validate_legal_rfc(cls, rfc: Rfc) -> Rfc:
949
+ if len(rfc) != 12:
950
+ raise ValueError('RFC must be 12 characters for legal persons')
951
+ return rfc
952
+
953
+
954
+ class LegalPersonUpdateRequest(BaseRequest):
955
+ legal_name: Optional[str] = None
956
+ rfc: Optional[Rfc] = None
957
+ legal_representatives: Optional[list[LegalRepresentative]] = None
958
+
959
+ @model_validator(mode="before")
960
+ @classmethod
961
+ def check_at_least_one_param(cls, values: DictStrAny) -> DictStrAny:
962
+ if not values:
963
+ raise ValueError('At least one parameter must be provided')
964
+ return values
965
+
966
+ @field_validator('rfc')
967
+ @classmethod
968
+ def validate_legal_rfc(cls, rfc: Optional[Rfc]) -> Optional[Rfc]:
969
+ if rfc is not None and len(rfc) != 12:
970
+ raise ValueError('RFC must be 12 characters for legal persons')
971
+ return rfc
972
+
973
+
974
+ class OperatorRequest(BaseRequest):
975
+ name: str
976
+ email: EmailStr
977
+ phone: PhoneNumber
978
+ company_user_id: str
979
+ role: OperatorRole
980
+ status: OperatorStatus = OperatorStatus.active
981
+
982
+ model_config = ConfigDict(
983
+ json_schema_extra={
984
+ 'example': {
985
+ 'name': 'Maria Lopez',
986
+ 'email': 'maria.lopez@aceros.com',
987
+ 'phone': '+525512345678',
988
+ 'company_user_id': 'USWqY5cvkISJOxHyEKjAKf8w',
989
+ 'role': 'operator',
990
+ }
991
+ },
992
+ )
993
+
994
+ @field_validator('email', mode='before')
995
+ @classmethod
996
+ def validate_email(cls, email: str) -> str:
997
+ return normalize_email(email)
998
+
999
+
1000
+ class OperatorUpdateRequest(BaseRequest):
1001
+ name: Optional[str] = None
1002
+ phone: Optional[PhoneNumber] = None
1003
+ role: Optional[OperatorRole] = None
1004
+ status: Optional[OperatorStatus] = None
1005
+
1006
+ @model_validator(mode="before")
1007
+ @classmethod
1008
+ def check_at_least_one_param(cls, values: DictStrAny) -> DictStrAny:
1009
+ if not values:
1010
+ raise ValueError('At least one parameter must be provided')
1011
+ return values
1012
+
1013
+
1014
+ class OperatorLoginRequest(BaseRequest):
1015
+ email: EmailStr
1016
+ password: Annotated[Password, LogConfig(masked=True)]
1017
+
1018
+ model_config = ConfigDict(
1019
+ json_schema_extra={
1020
+ 'example': {
1021
+ 'email': 'maria.lopez@aceros.com',
1022
+ 'password': 'supersecret',
1023
+ }
1024
+ },
1025
+ )
1026
+
1027
+ @field_validator('email', mode='before')
1028
+ @classmethod
1029
+ def validate_email(cls, email: str) -> str:
1030
+ return normalize_email(email)
1031
+
1032
+
1033
+ class OperatorLoginResponse(BaseModel):
1034
+ session_token: str
1035
+
1036
+ model_config = ConfigDict(
1037
+ json_schema_extra={
1038
+ 'example': {
1039
+ 'session_token': 'SEWqY5cvkISJOxHyEKjAKf8w',
1040
+ }
1041
+ },
1042
+ )
1043
+
1044
+
900
1045
  class PhoneVerificationAssociationRequest(BaseRequest):
901
1046
  verification_id: str
@@ -0,0 +1 @@
1
+ __version__ = '2.1.41.dev2'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuenca_validations
3
- Version: 2.1.41.dev0
3
+ Version: 2.1.41.dev2
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -0,0 +1,314 @@
1
+ import pytest
2
+ from pydantic import ValidationError
3
+ from pydantic_extra_types.phone_numbers import PhoneNumber
4
+
5
+ from cuenca_validations.types.enums import (
6
+ Country,
7
+ OperatorRole,
8
+ OperatorStatus,
9
+ SessionType,
10
+ VerificationType,
11
+ )
12
+ from cuenca_validations.types.queries import OperatorQuery
13
+ from cuenca_validations.types.requests import (
14
+ LegalPersonRequest,
15
+ LegalPersonUpdateRequest,
16
+ OperatorLoginRequest,
17
+ OperatorLoginResponse,
18
+ OperatorRequest,
19
+ OperatorUpdateRequest,
20
+ PasswordResetRequest,
21
+ SessionMetadata,
22
+ SessionRequest,
23
+ UpdateTransferRequest,
24
+ UserTOSAgreementRequest,
25
+ UserUpdateRequest,
26
+ VerificationRequest,
27
+ )
28
+ from cuenca_validations.typing import DictStrAny
29
+
30
+ LEGAL_REPRESENTATIVE: DictStrAny = {
31
+ 'names': 'Juan',
32
+ 'first_surname': 'Perez',
33
+ 'job': 'Director General',
34
+ 'phone_number': '+525512345678',
35
+ 'email_address': 'juan.perez@aceros.com',
36
+ 'address': {
37
+ 'street': 'Reforma',
38
+ 'ext_number': '265',
39
+ 'postal_code_id': 'PC2ygq9j2bS9-9tsuVawzErA',
40
+ },
41
+ }
42
+
43
+ LEGAL_PERSON_REQUEST: DictStrAny = {
44
+ 'legal_name': 'Aceros del Norte SA de CV',
45
+ 'rfc': 'ADN850101ABC',
46
+ 'legal_representatives': [LEGAL_REPRESENTATIVE],
47
+ }
48
+
49
+
50
+ def test_legal_person_request_valid() -> None:
51
+ req = LegalPersonRequest.model_validate(LEGAL_PERSON_REQUEST)
52
+ assert req.legal_name == 'Aceros del Norte SA de CV'
53
+ assert req.rfc == 'ADN850101ABC'
54
+
55
+
56
+ def test_legal_person_request_rejects_physical_rfc() -> None:
57
+ with pytest.raises(ValidationError) as ex:
58
+ LegalPersonRequest.model_validate(
59
+ {**LEGAL_PERSON_REQUEST, 'rfc': 'GOCG650418TJ1'}
60
+ )
61
+ assert 'RFC must be 12 characters for legal persons' in str(ex.value)
62
+
63
+
64
+ def test_legal_person_request_forbids_extra() -> None:
65
+ with pytest.raises(ValidationError) as ex:
66
+ LegalPersonRequest.model_validate(
67
+ {**LEGAL_PERSON_REQUEST, 'foo': 'bar'}
68
+ )
69
+ assert 'Extra inputs are not permitted' in str(ex.value)
70
+
71
+
72
+ def test_legal_person_update_requires_at_least_one_param() -> None:
73
+ with pytest.raises(ValueError) as ex:
74
+ LegalPersonUpdateRequest()
75
+ assert 'At least one parameter must be provided' in str(ex.value)
76
+
77
+
78
+ def test_legal_person_update_valid() -> None:
79
+ req = LegalPersonUpdateRequest.model_validate({'legal_name': 'New name'})
80
+ assert req.legal_name == 'New name'
81
+
82
+
83
+ def test_legal_person_update_accepts_legal_rfc() -> None:
84
+ req = LegalPersonUpdateRequest.model_validate({'rfc': 'ADN850101ABC'})
85
+ assert req.rfc == 'ADN850101ABC'
86
+
87
+
88
+ def test_legal_person_update_rejects_physical_rfc() -> None:
89
+ with pytest.raises(ValidationError) as ex:
90
+ LegalPersonUpdateRequest.model_validate({'rfc': 'GOCG650418TJ1'})
91
+ assert 'RFC must be 12 characters for legal persons' in str(ex.value)
92
+
93
+
94
+ def test_operator_request_valid() -> None:
95
+ req = OperatorRequest(
96
+ name='Maria Lopez',
97
+ email='Maria+Tag@Aceros.com',
98
+ phone=PhoneNumber('+525512345678'),
99
+ company_user_id='USWqY5cvkISJOxHyEKjAKf8w',
100
+ role=OperatorRole.operator,
101
+ )
102
+ assert req.email == 'maria@aceros.com'
103
+ assert req.status == OperatorStatus.active
104
+
105
+
106
+ def test_operator_request_rejects_invalid_role() -> None:
107
+ with pytest.raises(ValidationError) as ex:
108
+ OperatorRequest.model_validate(
109
+ {
110
+ 'name': 'Maria Lopez',
111
+ 'email': 'maria@aceros.com',
112
+ 'phone': '+525512345678',
113
+ 'company_user_id': 'USWqY5cvkISJOxHyEKjAKf8w',
114
+ 'role': 'admin',
115
+ }
116
+ )
117
+ assert 'role' in str(ex.value)
118
+
119
+
120
+ def test_operator_update_requires_at_least_one_param() -> None:
121
+ with pytest.raises(ValueError) as ex:
122
+ OperatorUpdateRequest()
123
+ assert 'At least one parameter must be provided' in str(ex.value)
124
+
125
+
126
+ def test_operator_update_valid() -> None:
127
+ req = OperatorUpdateRequest.model_validate({'name': 'New name'})
128
+ assert req.name == 'New name'
129
+
130
+
131
+ def test_operator_login_request_valid() -> None:
132
+ req = OperatorLoginRequest.model_validate(
133
+ {
134
+ 'email': 'Operator+Tag@Aceros.com',
135
+ 'password': 'supersecret',
136
+ }
137
+ )
138
+ assert req.email == 'operator@aceros.com'
139
+
140
+
141
+ def test_operator_login_request_rejects_short_password() -> None:
142
+ with pytest.raises(ValidationError) as ex:
143
+ OperatorLoginRequest.model_validate(
144
+ {
145
+ 'email': 'operator@aceros.com',
146
+ 'password': 'short',
147
+ }
148
+ )
149
+ assert 'password' in str(ex.value)
150
+
151
+
152
+ def test_operator_login_request_forbids_extra() -> None:
153
+ with pytest.raises(ValidationError) as ex:
154
+ OperatorLoginRequest.model_validate(
155
+ {
156
+ 'email': 'operator@aceros.com',
157
+ 'password': 'supersecret',
158
+ 'foo': 'bar',
159
+ }
160
+ )
161
+ assert 'Extra inputs are not permitted' in str(ex.value)
162
+
163
+
164
+ def test_operator_login_response_valid() -> None:
165
+ resp = OperatorLoginResponse(session_token='SEWqY5cvkISJOxHyEKjAKf8w')
166
+ assert resp.session_token == 'SEWqY5cvkISJOxHyEKjAKf8w'
167
+
168
+
169
+ def test_operator_query_valid() -> None:
170
+ query = OperatorQuery.model_validate({'email': 'maria.lopez@aceros.com'})
171
+ assert str(query.email) == 'maria.lopez@aceros.com'
172
+
173
+
174
+ def test_operator_query_forbids_extra() -> None:
175
+ with pytest.raises(ValidationError) as ex:
176
+ OperatorQuery.model_validate(
177
+ {'email': 'maria@aceros.com', 'foo': 'bar'}
178
+ )
179
+ assert 'Extra inputs are not permitted' in str(ex.value)
180
+
181
+
182
+ def test_session_request_with_operator_metadata() -> None:
183
+ req = SessionRequest(
184
+ user_id='USWqY5cvkISJOxHyEKjAKf8w',
185
+ type=SessionType.registration,
186
+ metadata=SessionMetadata(operator_id='OPWqY5cvkISJOxHyEKjAKf8w'),
187
+ )
188
+ assert req.metadata is not None
189
+ assert req.metadata.operator_id == 'OPWqY5cvkISJOxHyEKjAKf8w'
190
+
191
+
192
+ def test_session_request_metadata_forbids_extra() -> None:
193
+ with pytest.raises(ValidationError) as ex:
194
+ SessionMetadata.model_validate(
195
+ {'operator_id': 'OPWqY5cvkISJOxHyEKjAKf8w', 'foo': 'bar'}
196
+ )
197
+ assert 'Extra inputs are not permitted' in str(ex.value)
198
+
199
+
200
+ @pytest.mark.parametrize('environment', ['api.stage', 'api.sandbox', 'api'])
201
+ def test_file_cuenca_url(environment: str) -> None:
202
+ request_data: DictStrAny = dict(
203
+ tos_id='TS67dcae8e74e81bba5a77bf47',
204
+ location=(19.432607, -99.133209),
205
+ signature_image_url=(
206
+ f'https://{environment}.cuenca.com/files/EFQL8_ohvoRp-PkOTYgvQYFA'
207
+ ),
208
+ )
209
+ utos = UserTOSAgreementRequest(**request_data)
210
+ assert utos.signature_image_url is not None
211
+ assert utos.signature_image_url.file_id == 'EFQL8_ohvoRp-PkOTYgvQYFA'
212
+
213
+
214
+ def test_file_cuenca_url_invalid() -> None:
215
+ request_data: DictStrAny = dict(
216
+ tos_id='TS67dcae8e74e81bba5a77bf47',
217
+ location=(19.432607, -99.133209),
218
+ signature_image_url=(
219
+ 'https://cuenca.com/files/EFQL87ohvoRp-PkOTYgvQYFA/invalid'
220
+ ),
221
+ )
222
+ with pytest.raises(ValidationError):
223
+ UserTOSAgreementRequest(**request_data)
224
+
225
+
226
+ def test_password_reset_request_serializes() -> None:
227
+ payload: DictStrAny = {'location': (19.432607, -99.133209)}
228
+ req = PasswordResetRequest.model_validate(payload)
229
+ assert req.model_dump() == {
230
+ 'location': {
231
+ 'latitude': 19.432607,
232
+ 'longitude': -99.133209,
233
+ },
234
+ }
235
+
236
+
237
+ def test_update_user_requires_at_least_one_param():
238
+ with pytest.raises(ValueError) as ex:
239
+ UserUpdateRequest()
240
+ assert 'At least one parameter must be provided' in str(ex.value)
241
+
242
+
243
+ def test_extra_params_are_not_allowed():
244
+ with pytest.raises(ValueError) as ex:
245
+ UserUpdateRequest(foo='bar')
246
+ assert 'Extra inputs are not permitted' in str(ex.value)
247
+
248
+
249
+ def test_update_user_update_govt() -> None:
250
+ govt_id: DictStrAny = {
251
+ "govt_id": {"type": "ine", "uri_front": "files/123"}
252
+ }
253
+ with pytest.raises(ValueError) as ex:
254
+ UserUpdateRequest(**govt_id)
255
+ assert 'uri_back must be provided for type ine' in str(ex.value)
256
+
257
+
258
+ def test_verification_request_normalizes_email() -> None:
259
+ req = VerificationRequest(
260
+ recipient='user+cuenca@Gmail.com',
261
+ type=VerificationType.email,
262
+ )
263
+ assert req.recipient == 'user@gmail.com'
264
+
265
+
266
+ def test_verification_request_normalizes_phone() -> None:
267
+ req = VerificationRequest(
268
+ recipient='+116504401222',
269
+ type=VerificationType.phone,
270
+ )
271
+ assert req.recipient == '+16504401222'
272
+
273
+
274
+ def test_user_update_request_normalizes_email() -> None:
275
+ req = UserUpdateRequest(email_address='user+tag@Gmail.com')
276
+ assert req.email_address == 'user@gmail.com'
277
+
278
+
279
+ def test_user_update_request_normalizes_phone() -> None:
280
+ req = UserUpdateRequest(phone_number=PhoneNumber('+116504401222'))
281
+ assert req.phone_number == '+16504401222'
282
+
283
+
284
+ def test_user_update_request_accepts_country_of_birth() -> None:
285
+ req = UserUpdateRequest(country_of_birth=Country.CA)
286
+ assert req.country_of_birth == Country.CA
287
+
288
+
289
+ @pytest.mark.parametrize('status', ['succeeded', 'failed'])
290
+ def test_update_transfer_request_valid_status(status: str) -> None:
291
+ req = UpdateTransferRequest.model_validate({'status': status})
292
+ assert req.status == status
293
+ assert req.model_dump() == {'status': status}
294
+
295
+
296
+ @pytest.mark.parametrize('status', ['created', 'submitted', 'in_review'])
297
+ def test_update_transfer_request_invalid_status(status: str) -> None:
298
+ with pytest.raises(ValidationError) as ex:
299
+ UpdateTransferRequest.model_validate({'status': status})
300
+ assert 'status' in str(ex.value)
301
+
302
+
303
+ def test_update_transfer_request_missing_status() -> None:
304
+ with pytest.raises(ValidationError) as ex:
305
+ UpdateTransferRequest.model_validate({})
306
+ assert 'status' in str(ex.value)
307
+
308
+
309
+ def test_update_transfer_request_forbids_extra() -> None:
310
+ with pytest.raises(ValidationError) as ex:
311
+ UpdateTransferRequest.model_validate(
312
+ {'status': 'succeeded', 'foo': 'bar'}
313
+ )
314
+ assert 'Extra inputs are not permitted' in str(ex.value)
@@ -1 +0,0 @@
1
- __version__ = '2.1.41.dev0'
@@ -1,130 +0,0 @@
1
- import pytest
2
- from pydantic import ValidationError
3
- from pydantic_extra_types.phone_numbers import PhoneNumber
4
-
5
- from cuenca_validations.types.enums import Country, VerificationType
6
- from cuenca_validations.types.requests import (
7
- PasswordResetRequest,
8
- UpdateTransferRequest,
9
- UserTOSAgreementRequest,
10
- UserUpdateRequest,
11
- VerificationRequest,
12
- )
13
- from cuenca_validations.typing import DictStrAny
14
-
15
-
16
- @pytest.mark.parametrize('environment', ['api.stage', 'api.sandbox', 'api'])
17
- def test_file_cuenca_url(environment: str) -> None:
18
- request_data: DictStrAny = dict(
19
- tos_id='TS67dcae8e74e81bba5a77bf47',
20
- location=(19.432607, -99.133209),
21
- signature_image_url=(
22
- f'https://{environment}.cuenca.com/files/EFQL8_ohvoRp-PkOTYgvQYFA'
23
- ),
24
- )
25
- utos = UserTOSAgreementRequest(**request_data)
26
- assert utos.signature_image_url is not None
27
- assert utos.signature_image_url.file_id == 'EFQL8_ohvoRp-PkOTYgvQYFA'
28
-
29
-
30
- def test_file_cuenca_url_invalid() -> None:
31
- request_data: DictStrAny = dict(
32
- tos_id='TS67dcae8e74e81bba5a77bf47',
33
- location=(19.432607, -99.133209),
34
- signature_image_url=(
35
- 'https://cuenca.com/files/EFQL87ohvoRp-PkOTYgvQYFA/invalid'
36
- ),
37
- )
38
- with pytest.raises(ValidationError):
39
- UserTOSAgreementRequest(**request_data)
40
-
41
-
42
- def test_password_reset_request_serializes() -> None:
43
- payload: DictStrAny = {'location': (19.432607, -99.133209)}
44
- req = PasswordResetRequest.model_validate(payload)
45
- assert req.model_dump() == {
46
- 'location': {
47
- 'latitude': 19.432607,
48
- 'longitude': -99.133209,
49
- },
50
- }
51
-
52
-
53
- def test_update_user_requires_at_least_one_param():
54
- with pytest.raises(ValueError) as ex:
55
- UserUpdateRequest()
56
- assert 'At least one parameter must be provided' in str(ex.value)
57
-
58
-
59
- def test_extra_params_are_not_allowed():
60
- with pytest.raises(ValueError) as ex:
61
- UserUpdateRequest(foo='bar')
62
- assert 'Extra inputs are not permitted' in str(ex.value)
63
-
64
-
65
- def test_update_user_update_govt() -> None:
66
- govt_id: DictStrAny = {
67
- "govt_id": {"type": "ine", "uri_front": "files/123"}
68
- }
69
- with pytest.raises(ValueError) as ex:
70
- UserUpdateRequest(**govt_id)
71
- assert 'uri_back must be provided for type ine' in str(ex.value)
72
-
73
-
74
- def test_verification_request_normalizes_email() -> None:
75
- req = VerificationRequest(
76
- recipient='user+cuenca@Gmail.com',
77
- type=VerificationType.email,
78
- )
79
- assert req.recipient == 'user@gmail.com'
80
-
81
-
82
- def test_verification_request_normalizes_phone() -> None:
83
- req = VerificationRequest(
84
- recipient='+116504401222',
85
- type=VerificationType.phone,
86
- )
87
- assert req.recipient == '+16504401222'
88
-
89
-
90
- def test_user_update_request_normalizes_email() -> None:
91
- req = UserUpdateRequest(email_address='user+tag@Gmail.com')
92
- assert req.email_address == 'user@gmail.com'
93
-
94
-
95
- def test_user_update_request_normalizes_phone() -> None:
96
- req = UserUpdateRequest(phone_number=PhoneNumber('+116504401222'))
97
- assert req.phone_number == '+16504401222'
98
-
99
-
100
- def test_user_update_request_accepts_country_of_birth() -> None:
101
- req = UserUpdateRequest(country_of_birth=Country.CA)
102
- assert req.country_of_birth == Country.CA
103
-
104
-
105
- @pytest.mark.parametrize('status', ['succeeded', 'failed'])
106
- def test_update_transfer_request_valid_status(status: str) -> None:
107
- req = UpdateTransferRequest.model_validate({'status': status})
108
- assert req.status == status
109
- assert req.model_dump() == {'status': status}
110
-
111
-
112
- @pytest.mark.parametrize('status', ['created', 'submitted', 'in_review'])
113
- def test_update_transfer_request_invalid_status(status: str) -> None:
114
- with pytest.raises(ValidationError) as ex:
115
- UpdateTransferRequest.model_validate({'status': status})
116
- assert 'status' in str(ex.value)
117
-
118
-
119
- def test_update_transfer_request_missing_status() -> None:
120
- with pytest.raises(ValidationError) as ex:
121
- UpdateTransferRequest.model_validate({})
122
- assert 'status' in str(ex.value)
123
-
124
-
125
- def test_update_transfer_request_forbids_extra() -> None:
126
- with pytest.raises(ValidationError) as ex:
127
- UpdateTransferRequest.model_validate(
128
- {'status': 'succeeded', 'foo': 'bar'}
129
- )
130
- assert 'Extra inputs are not permitted' in str(ex.value)