cuenca-validations 2.1.41.dev3__tar.gz → 2.1.42__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.dev3 → cuenca_validations-2.1.42}/PKG-INFO +1 -1
  2. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/__init__.py +3 -30
  3. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/enums.py +0 -22
  4. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/queries.py +0 -10
  5. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/requests.py +3 -146
  6. cuenca_validations-2.1.42/cuenca_validations/version.py +1 -0
  7. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations.egg-info/PKG-INFO +1 -1
  8. cuenca_validations-2.1.42/tests/test_requests.py +130 -0
  9. cuenca_validations-2.1.41.dev3/cuenca_validations/version.py +0 -1
  10. cuenca_validations-2.1.41.dev3/tests/test_requests.py +0 -314
  11. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/LICENSE +0 -0
  12. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/README.md +0 -0
  13. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/__init__.py +0 -0
  14. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/card_bins.py +0 -0
  15. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/errors.py +0 -0
  16. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/py.typed +0 -0
  17. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/card.py +0 -0
  18. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/files.py +0 -0
  19. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/general.py +0 -0
  20. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/helpers.py +0 -0
  21. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/identities.py +0 -0
  22. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/types/morals.py +0 -0
  23. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/typing.py +0 -0
  24. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations/validators.py +0 -0
  25. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations.egg-info/SOURCES.txt +0 -0
  26. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations.egg-info/dependency_links.txt +0 -0
  27. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations.egg-info/requires.txt +0 -0
  28. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/cuenca_validations.egg-info/top_level.txt +0 -0
  29. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/setup.cfg +0 -0
  30. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/setup.py +0 -0
  31. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/tests/__init__.py +0 -0
  32. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/tests/test_card.py +0 -0
  33. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/tests/test_errors.py +0 -0
  34. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/tests/test_helpers.py +0 -0
  35. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/tests/test_statement.py +0 -0
  36. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/tests/test_types.py +0 -0
  37. {cuenca_validations-2.1.41.dev3 → cuenca_validations-2.1.42}/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.dev3
3
+ Version: 2.1.42
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -1,9 +1,6 @@
1
1
  __all__ = [
2
2
  'AccountUseType',
3
3
  'AccountQuery',
4
- 'AccountRequest',
5
- 'AccountUpdateRequest',
6
- 'AccountValidationStatus',
7
4
  'Address',
8
5
  'AgentQuery',
9
6
  'AgentRequest',
@@ -60,20 +57,11 @@ __all__ = [
60
57
  'KYCValidationRequest',
61
58
  'KYCValidationSource',
62
59
  'Language',
63
- 'LegalPersonRequest',
64
- 'LegalPersonUpdateRequest',
65
60
  'LimitedWalletRequest',
66
61
  'MonthlyMovementsType',
67
62
  'MonthlySpendingType',
68
- 'OperationalEventAction',
69
- 'OperationalEventQuery',
70
- 'OperatorLoginRequest',
71
- 'OperatorLoginResponse',
72
- 'OperatorQuery',
73
- 'OperatorRequest',
74
- 'OperatorRole',
75
- 'OperatorStatus',
76
- 'OperatorUpdateRequest',
63
+ 'LegalPersonRequest',
64
+ 'LegalPersonUpdateRequest',
77
65
  'PartnerRequest',
78
66
  'PartnerUpdateRequest',
79
67
  'PasswordResetRequest',
@@ -83,6 +71,7 @@ __all__ = [
83
71
  'PosCapability',
84
72
  'Profession',
85
73
  'QueryParams',
74
+ 'LegalRfc',
86
75
  'Rfc',
87
76
  'QuestionnairesRequest',
88
77
  'SantizedDict',
@@ -91,10 +80,8 @@ __all__ = [
91
80
  'SavingRequest',
92
81
  'SavingUpdateRequest',
93
82
  'ServiceProviderCategory',
94
- 'SessionMetadata',
95
83
  'SessionQuery',
96
84
  'SessionRequest',
97
- 'SessionResponse',
98
85
  'SessionType',
99
86
  'State',
100
87
  'StatementQuery',
@@ -145,7 +132,6 @@ __all__ = [
145
132
  from .card import StrictPaymentCardNumber
146
133
  from .enums import (
147
134
  AccountUseType,
148
- AccountValidationStatus,
149
135
  AuthorizerTransaction,
150
136
  BankAccountStatus,
151
137
  CardErrorType,
@@ -172,9 +158,6 @@ from .enums import (
172
158
  Language,
173
159
  MonthlyMovementsType,
174
160
  MonthlySpendingType,
175
- OperationalEventAction,
176
- OperatorRole,
177
- OperatorStatus,
178
161
  PlatformType,
179
162
  PosCapability,
180
163
  Profession,
@@ -233,8 +216,6 @@ from .queries import (
233
216
  EventQuery,
234
217
  FileQuery,
235
218
  IdentityQuery,
236
- OperationalEventQuery,
237
- OperatorQuery,
238
219
  PostalCodeQuery,
239
220
  QueryParams,
240
221
  SessionQuery,
@@ -248,8 +229,6 @@ from .queries import (
248
229
  WalletTransactionQuery,
249
230
  )
250
231
  from .requests import (
251
- AccountRequest,
252
- AccountUpdateRequest,
253
232
  AgentRequest,
254
233
  ApiKeyUpdateRequest,
255
234
  BankAccountValidationRequest,
@@ -265,10 +244,6 @@ from .requests import (
265
244
  LegalPersonRequest,
266
245
  LegalPersonUpdateRequest,
267
246
  LimitedWalletRequest,
268
- OperatorLoginRequest,
269
- OperatorLoginResponse,
270
- OperatorRequest,
271
- OperatorUpdateRequest,
272
247
  PartnerRequest,
273
248
  PartnerUpdateRequest,
274
249
  PasswordResetRequest,
@@ -276,9 +251,7 @@ from .requests import (
276
251
  QuestionnairesRequest,
277
252
  SavingRequest,
278
253
  SavingUpdateRequest,
279
- SessionMetadata,
280
254
  SessionRequest,
281
- SessionResponse,
282
255
  StrictTransferRequest,
283
256
  TOSRequest,
284
257
  TransferRequest,
@@ -745,25 +745,3 @@ class RequiredAction(str, Enum):
745
745
  level_up_required = 'level_up_required'
746
746
  level_up_invitation = 'level_up_invitation'
747
747
  fix_documents = 'fix_documents'
748
-
749
-
750
- class AccountValidationStatus(str, Enum):
751
- verified = 'verified'
752
- pending = 'pending'
753
-
754
-
755
- class OperationalEventAction(str, Enum):
756
- account_created = 'account_created'
757
- account_updated = 'account_updated'
758
- transfer_created = 'transfer_created'
759
- statement_downloaded = 'statement_downloaded'
760
-
761
-
762
- class OperatorRole(str, Enum):
763
- operator = 'operator'
764
- authorizer = 'authorizer'
765
-
766
-
767
- class OperatorStatus(str, Enum):
768
- active = 'active'
769
- disabled = 'disabled'
@@ -22,7 +22,6 @@ from .enums import (
22
22
  CardType,
23
23
  EventType,
24
24
  KYCFileType,
25
- OperationalEventAction,
26
25
  SessionType,
27
26
  TermsOfService,
28
27
  TransferNetwork,
@@ -154,15 +153,6 @@ class AccountQuery(QueryParams):
154
153
  account_number: Optional[str] = None
155
154
 
156
155
 
157
- class OperationalEventQuery(QueryParams):
158
- actor_id: Optional[str] = None
159
- action: Optional[OperationalEventAction] = None
160
-
161
-
162
- class OperatorQuery(QueryParams):
163
- email: Optional[EmailStr] = None
164
-
165
-
166
156
  class BalanceEntryQuery(QueryParams):
167
157
  funding_instrument_uri: Optional[str] = None
168
158
  wallet_id: str = 'default'
@@ -18,7 +18,6 @@ from pydantic_extra_types.coordinate import Coordinate
18
18
 
19
19
  from ..types.enums import (
20
20
  AccountUseType,
21
- AccountValidationStatus,
22
21
  AuthorizerTransaction,
23
22
  CardDesign,
24
23
  CardFundingType,
@@ -37,8 +36,6 @@ from ..types.enums import (
37
36
  KYCValidationSource,
38
37
  MonthlyMovementsType,
39
38
  MonthlySpendingType,
40
- OperatorRole,
41
- OperatorStatus,
42
39
  PlatformType,
43
40
  PosCapability,
44
41
  Profession,
@@ -632,29 +629,11 @@ class UserLoginRequest(BaseRequest):
632
629
  )
633
630
 
634
631
 
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
-
652
632
  class SessionRequest(BaseRequest):
653
633
  user_id: str
654
634
  type: SessionType
655
635
  success_url: Optional[SerializableAnyUrl] = None
656
636
  failure_url: Optional[SerializableAnyUrl] = None
657
- metadata: Optional[SessionMetadata] = None
658
637
  model_config = ConfigDict(
659
638
  json_schema_extra={
660
639
  'example': {
@@ -756,39 +735,6 @@ class LimitedWalletRequest(BaseRequest):
756
735
  allowed_rfc: Optional[Rfc] = None
757
736
 
758
737
 
759
- class AccountRequest(BaseRequest):
760
- name: StrictStr
761
- account_number: Clabe
762
- alias: Optional[StrictStr] = None
763
- curp: Optional[Curp] = None
764
- rfc: Optional[Rfc] = None
765
-
766
- model_config = ConfigDict(
767
- json_schema_extra={
768
- 'example': {
769
- 'name': 'Aceros del Norte SA de CV',
770
- 'account_number': '072691004495711499',
771
- 'alias': 'Proveedor acero',
772
- }
773
- },
774
- )
775
-
776
-
777
- class AccountUpdateRequest(BaseRequest):
778
- name: Optional[StrictStr] = None
779
- alias: Optional[StrictStr] = None
780
- validation_status: Optional[AccountValidationStatus] = None
781
-
782
- model_config = ConfigDict(
783
- json_schema_extra={
784
- 'example': {
785
- 'alias': 'Fletes',
786
- 'validation_status': 'verified',
787
- }
788
- },
789
- )
790
-
791
-
792
738
  class PlatformRequest(BaseModel):
793
739
  name: str
794
740
  rfc: Optional[str] = None
@@ -920,6 +866,7 @@ class PartnerUpdateRequest(BaseRequest):
920
866
  class LegalPersonRequest(BaseRequest):
921
867
  legal_name: str
922
868
  rfc: Rfc
869
+ address: AddressRequest
923
870
  legal_representatives: list[LegalRepresentative]
924
871
 
925
872
  model_config = ConfigDict(
@@ -927,6 +874,7 @@ class LegalPersonRequest(BaseRequest):
927
874
  'example': {
928
875
  'legal_name': 'Aceros del Norte SA de CV',
929
876
  'rfc': 'ADN850101ABC',
877
+ 'address': AddressRequest.model_json_schema().get('example'),
930
878
  'legal_representatives': [
931
879
  {
932
880
  'names': 'Juan',
@@ -943,104 +891,13 @@ class LegalPersonRequest(BaseRequest):
943
891
  },
944
892
  )
945
893
 
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
894
 
954
895
  class LegalPersonUpdateRequest(BaseRequest):
955
896
  legal_name: Optional[str] = None
956
897
  rfc: Optional[Rfc] = None
898
+ address: Optional[AddressRequest] = None
957
899
  legal_representatives: Optional[list[LegalRepresentative]] = None
958
900
 
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
901
 
1045
902
  class PhoneVerificationAssociationRequest(BaseRequest):
1046
903
  verification_id: str
@@ -0,0 +1 @@
1
+ __version__ = '2.1.42'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuenca_validations
3
- Version: 2.1.41.dev3
3
+ Version: 2.1.42
4
4
  Summary: Cuenca common validations
5
5
  Home-page: https://github.com/cuenca-mx/cuenca-validations
6
6
  Author: Cuenca
@@ -0,0 +1,130 @@
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)
@@ -1 +0,0 @@
1
- __version__ = '2.1.41.dev3'
@@ -1,314 +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 (
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)