sysnet-pyutils 1.7.2__tar.gz → 1.7.3__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 (58) hide show
  1. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/PKG-INFO +1 -1
  2. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/pyproject.toml +1 -1
  3. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/__init__.py +1 -0
  4. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/models/general.py +38 -2
  5. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils.egg-info/PKG-INFO +1 -1
  6. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils.egg-info/SOURCES.txt +1 -0
  7. sysnet_pyutils-1.7.3/tests/test_models_contact.py +333 -0
  8. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/LICENSE +0 -0
  9. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/README.md +0 -0
  10. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/setup.cfg +0 -0
  11. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/barcode.py +0 -0
  12. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/constants.py +0 -0
  13. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/cron_utils/__init__.py +0 -0
  14. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/cron_utils/validation.py +0 -0
  15. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/data_utils.py +0 -0
  16. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/domino.py +0 -0
  17. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/geo.py +0 -0
  18. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/globalmodel/__init__.py +0 -0
  19. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/globalmodel/global_model.py +0 -0
  20. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/globalmodel/models.py +0 -0
  21. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/globalmodel/timeutils.py +0 -0
  22. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/ident.py +0 -0
  23. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/log.py +0 -0
  24. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/models/__init__.py +0 -0
  25. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/ses.py +0 -0
  26. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/syslog/__init__.py +0 -0
  27. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/syslog/factory.py +0 -0
  28. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/tools.py +0 -0
  29. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils/utils.py +0 -0
  30. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils.egg-info/dependency_links.txt +0 -0
  31. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils.egg-info/not-zip-safe +0 -0
  32. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils.egg-info/requires.txt +0 -0
  33. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/sysnet_pyutils.egg-info/top_level.txt +0 -0
  34. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_cron_utils.py +0 -0
  35. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_cron_validation_edge.py +0 -0
  36. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_data_utils.py +0 -0
  37. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_data_utils_edge.py +0 -0
  38. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_data_utils_missing_branches.py +0 -0
  39. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_domino.py +0 -0
  40. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_domino_edge.py +0 -0
  41. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_geo.py +0 -0
  42. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_global_model_recursive.py +0 -0
  43. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_global_model_timezone.py +0 -0
  44. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_ident_barcode.py +0 -0
  45. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_log_importerror_branch.py +0 -0
  46. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_log_mongo.py +0 -0
  47. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_models_general.py +0 -0
  48. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_models_general_edge.py +0 -0
  49. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_package_init_version.py +0 -0
  50. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_recursive_update.py +0 -0
  51. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_ses.py +0 -0
  52. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_ses_importerror_branch.py +0 -0
  53. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_syslog_factory.py +0 -0
  54. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_tools.py +0 -0
  55. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_utils.py +0 -0
  56. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_utils_edge.py +0 -0
  57. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_utils_log_base64.py +0 -0
  58. {sysnet_pyutils-1.7.2 → sysnet_pyutils-1.7.3}/tests/test_utils_missing_branches.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sysnet-pyutils
3
- Version: 1.7.2
3
+ Version: 1.7.3
4
4
  Summary: SYSNET Python Utilities - kompletní sada nástrojů pro Python aplikace
5
5
  Author-email: Data Developer <info@sysnet.cz>
6
6
  Maintainer-email: SYSNET Development Team <info@sysnet.cz>
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sysnet-pyutils"
7
- version = "1.7.2"
7
+ version = "1.7.3"
8
8
  description = "SYSNET Python Utilities - kompletní sada nástrojů pro Python aplikace"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -86,6 +86,7 @@ from sysnet_pyutils.models.general import (
86
86
  BaseEnum,
87
87
  BasinType,
88
88
  CodeValueType,
89
+ ContactType,
89
90
  ContainerHistoryItemType,
90
91
  ErrorModel,
91
92
  GeoPointJtskType,
@@ -3,9 +3,9 @@ from __future__ import annotations
3
3
  import json
4
4
  from datetime import datetime
5
5
  from enum import Enum
6
- from typing import Any
6
+ from typing import Any, Optional, List
7
7
 
8
- from pydantic import BaseModel, EmailStr, Field
8
+ from pydantic import BaseModel, ConfigDict, EmailStr, Field
9
9
  from typing_extensions import Self
10
10
 
11
11
  from sysnet_pyutils.utils import is_valid_pid, is_valid_unid, is_valid_uuid, local_now
@@ -425,3 +425,39 @@ class MailAddressType(BaseModel):
425
425
 
426
426
  name: str | None = Field(default=None, description="Název adresa elektronické pošty (práce, domů)")
427
427
  email: str | None = Field(default=None, description="Adresa elektronické pošty")
428
+
429
+ class ContactType(BaseModel):
430
+ """
431
+ Kontaktní údaje osoby — jméno, telefony, e-maily a další kontaktní informace.
432
+
433
+ Pole ``phone_default`` / ``phoneDefault`` (alias) určuje index primárního
434
+ telefonního čísla v seznamu ``phone``. Alias je zachován pro kompatibilitu
435
+ s JSON API (camelCase). Model je nastaven s ``populate_by_name=True``, takže
436
+ lze použít jak Python název (``phone_default``), tak alias (``phoneDefault``).
437
+
438
+ Příklad::
439
+
440
+ c = ContactType(
441
+ first_name="Jan",
442
+ last_name="Novák",
443
+ phone=[PhoneNumberType(prefix="+420", number="602000001")],
444
+ email=[MailAddressType(email="jan@example.com")],
445
+ )
446
+ """ # noqa: E501
447
+
448
+ model_config = ConfigDict(populate_by_name=True)
449
+ identifier: Optional[str] = Field(default=None, description="identifikátor uuid")
450
+ first_name: Optional[str] = Field(default=None, description="Křestní jméno")
451
+ last_name: Optional[str] = Field(default=None, description="Příjmení")
452
+ degree_before: Optional[List[Optional[str]]] = Field(default=None, description="Tituly před jménem")
453
+ degree_after: Optional[List[Optional[str]]] = Field(default=None, description="Tituly za jménem")
454
+ full_name: Optional[str] = Field(default=None, description="Celé jméno pro zobrazení a tisk")
455
+ birthdate: Optional[datetime] = Field(default=None, description="Datum narození")
456
+ phone: Optional[List[Optional[PhoneNumberType]]] = Field(default=None, description="Telefonní číslo")
457
+ phone_default: Optional[int] = Field(default=0, description="Index hlavního telefonního čísla", alias="phoneDefault")
458
+ email: Optional[List[Optional[MailAddressType]]] = Field(default=None, description="Elektronická pošta")
459
+ email_default: Optional[int] = Field(default=0, description="Index hlavní adresy elektronické pošty")
460
+ web: Optional[str] = Field(default=None, description="Webová stránka subjektu")
461
+ isds: Optional[str] = Field(default=None, description="Identifikátor datové schránky")
462
+ reference: Optional[str] = Field(default=None, description="identifikátor uuid")
463
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sysnet-pyutils
3
- Version: 1.7.2
3
+ Version: 1.7.3
4
4
  Summary: SYSNET Python Utilities - kompletní sada nástrojů pro Python aplikace
5
5
  Author-email: Data Developer <info@sysnet.cz>
6
6
  Maintainer-email: SYSNET Development Team <info@sysnet.cz>
@@ -41,6 +41,7 @@ tests/test_global_model_timezone.py
41
41
  tests/test_ident_barcode.py
42
42
  tests/test_log_importerror_branch.py
43
43
  tests/test_log_mongo.py
44
+ tests/test_models_contact.py
44
45
  tests/test_models_general.py
45
46
  tests/test_models_general_edge.py
46
47
  tests/test_package_init_version.py
@@ -0,0 +1,333 @@
1
+ """
2
+ Testy pro ContactType a přidružené modely (PhoneNumberType, MailAddressType)
3
+ v modulu sysnet_pyutils.models.general
4
+
5
+ Pokrývá:
6
+ - pyutils-bcn.2: základní vytvoření a pole
7
+ - pyutils-bcn.3: integrace PhoneNumberType a MailAddressType
8
+ - pyutils-bcn.4: alias phoneDefault / phone_default
9
+ - pyutils-bcn.5: hraniční případy
10
+ """
11
+
12
+ from datetime import datetime
13
+
14
+ import pytest
15
+ from pydantic import ValidationError
16
+
17
+ from sysnet_pyutils.models.general import (
18
+ ContactType,
19
+ MailAddressType,
20
+ PhoneNumberType,
21
+ )
22
+
23
+
24
+ # ---------------------------------------------------------------------------
25
+ # Pomocné továrny
26
+ # ---------------------------------------------------------------------------
27
+
28
+ def make_phone(name: str = "mobil", prefix: str = "+420", number: str = "602000001") -> PhoneNumberType:
29
+ return PhoneNumberType(name=name, prefix=prefix, number=number)
30
+
31
+
32
+ def make_email(name: str = "práce", email: str = "test@example.com") -> MailAddressType:
33
+ return MailAddressType(name=name, email=email)
34
+
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # PhoneNumberType
38
+ # ---------------------------------------------------------------------------
39
+
40
+ class TestPhoneNumberType:
41
+ """Testy pro PhoneNumberType"""
42
+
43
+ def test_create_with_all_fields(self):
44
+ phone = PhoneNumberType(name="mobil", prefix="+420", number="602000001")
45
+ assert phone.name == "mobil"
46
+ assert phone.prefix == "+420"
47
+ assert phone.number == "602000001"
48
+
49
+ def test_create_empty(self):
50
+ phone = PhoneNumberType()
51
+ assert phone.name is None
52
+ assert phone.prefix is None
53
+ assert phone.number is None
54
+
55
+ def test_create_partial(self):
56
+ phone = PhoneNumberType(number="123456789")
57
+ assert phone.number == "123456789"
58
+ assert phone.name is None
59
+ assert phone.prefix is None
60
+
61
+
62
+ # ---------------------------------------------------------------------------
63
+ # MailAddressType
64
+ # ---------------------------------------------------------------------------
65
+
66
+ class TestMailAddressType:
67
+ """Testy pro MailAddressType"""
68
+
69
+ def test_create_with_all_fields(self):
70
+ mail = MailAddressType(name="práce", email="jan@example.com")
71
+ assert mail.name == "práce"
72
+ assert mail.email == "jan@example.com"
73
+
74
+ def test_create_empty(self):
75
+ mail = MailAddressType()
76
+ assert mail.name is None
77
+ assert mail.email is None
78
+
79
+ def test_create_partial(self):
80
+ mail = MailAddressType(email="only@email.cz")
81
+ assert mail.email == "only@email.cz"
82
+ assert mail.name is None
83
+
84
+
85
+ # ---------------------------------------------------------------------------
86
+ # ContactType — bcn.2: základní vytvoření a pole
87
+ # ---------------------------------------------------------------------------
88
+
89
+ class TestContactTypeBasic:
90
+ """Testy základního vytvoření ContactType a nastavení polí (bcn.2)"""
91
+
92
+ def test_create_empty(self):
93
+ """ContactType lze vytvořit bez argumentů — vše None / default"""
94
+ c = ContactType()
95
+ assert c.identifier is None
96
+ assert c.first_name is None
97
+ assert c.last_name is None
98
+ assert c.degree_before is None
99
+ assert c.degree_after is None
100
+ assert c.full_name is None
101
+ assert c.birthdate is None
102
+ assert c.phone is None
103
+ assert c.phone_default == 0
104
+ assert c.email is None
105
+ assert c.email_default == 0
106
+ assert c.web is None
107
+ assert c.isds is None
108
+ assert c.reference is None
109
+
110
+ def test_create_with_scalar_fields(self):
111
+ """Nastavení všech skalárních polí"""
112
+ uuid = "550e8400-e29b-41d4-a716-446655440000"
113
+ c = ContactType(
114
+ identifier=uuid,
115
+ first_name="Jan",
116
+ last_name="Novák",
117
+ full_name="Ing. Jan Novák",
118
+ web="https://novak.cz",
119
+ isds="abc1234",
120
+ reference=uuid,
121
+ )
122
+ assert c.identifier == uuid
123
+ assert c.first_name == "Jan"
124
+ assert c.last_name == "Novák"
125
+ assert c.full_name == "Ing. Jan Novák"
126
+ assert c.web == "https://novak.cz"
127
+ assert c.isds == "abc1234"
128
+ assert c.reference == uuid
129
+
130
+ def test_create_with_birthdate(self):
131
+ """Pole birthdate přijímá datetime objekt"""
132
+ bday = datetime(1985, 6, 15)
133
+ c = ContactType(birthdate=bday)
134
+ assert c.birthdate == bday
135
+ assert c.birthdate.year == 1985
136
+
137
+ def test_create_with_degree_lists(self):
138
+ """Tituly před/za jménem jsou listy stringů"""
139
+ c = ContactType(
140
+ degree_before=["Ing.", "Bc."],
141
+ degree_after=["PhD.", "MBA"],
142
+ )
143
+ assert c.degree_before == ["Ing.", "Bc."]
144
+ assert c.degree_after == ["PhD.", "MBA"]
145
+
146
+
147
+ # ---------------------------------------------------------------------------
148
+ # ContactType — bcn.3: integrace PhoneNumberType a MailAddressType
149
+ # ---------------------------------------------------------------------------
150
+
151
+ class TestContactTypePhoneEmail:
152
+ """Testy integrace PhoneNumberType a MailAddressType v ContactType (bcn.3)"""
153
+
154
+ def test_single_phone(self):
155
+ """ContactType s jedním telefonním číslem"""
156
+ c = ContactType(phone=[make_phone()])
157
+ assert len(c.phone) == 1
158
+ assert c.phone[0].number == "602000001"
159
+
160
+ def test_multiple_phones(self):
161
+ """ContactType s více telefonními čísly"""
162
+ phones = [
163
+ make_phone(name="mobil", number="602000001"),
164
+ make_phone(name="práce", number="257000002"),
165
+ ]
166
+ c = ContactType(phone=phones)
167
+ assert len(c.phone) == 2
168
+ assert c.phone[0].name == "mobil"
169
+ assert c.phone[1].name == "práce"
170
+
171
+ def test_single_email(self):
172
+ """ContactType s jednou e-mailovou adresou"""
173
+ c = ContactType(email=[make_email()])
174
+ assert len(c.email) == 1
175
+ assert c.email[0].email == "test@example.com"
176
+
177
+ def test_multiple_emails(self):
178
+ """ContactType s více e-mailovými adresami"""
179
+ emails = [
180
+ make_email(name="práce", email="jan@firma.cz"),
181
+ make_email(name="domů", email="jan@home.cz"),
182
+ ]
183
+ c = ContactType(email=emails)
184
+ assert len(c.email) == 2
185
+ assert c.email[0].name == "práce"
186
+ assert c.email[1].name == "domů"
187
+
188
+ def test_phone_and_email_combined(self):
189
+ """ContactType kombinuje phone i email"""
190
+ c = ContactType(
191
+ first_name="Eva",
192
+ last_name="Svobodová",
193
+ phone=[make_phone()],
194
+ email=[make_email()],
195
+ )
196
+ assert c.first_name == "Eva"
197
+ assert len(c.phone) == 1
198
+ assert len(c.email) == 1
199
+
200
+
201
+ # ---------------------------------------------------------------------------
202
+ # ContactType — bcn.4: alias phoneDefault / phone_default
203
+ # ---------------------------------------------------------------------------
204
+
205
+ class TestContactTypeAlias:
206
+ """Testy Pydantic aliasu phoneDefault vs phone_default (bcn.4)"""
207
+
208
+ def test_phone_default_python_name(self):
209
+ """phone_default lze nastavit přes Python název pole"""
210
+ c = ContactType(phone_default=1)
211
+ assert c.phone_default == 1
212
+
213
+ def test_phone_default_via_alias_dict(self):
214
+ """Parsování z dict s camelCase klíčem phoneDefault"""
215
+ data = {"phoneDefault": 2}
216
+ c = ContactType.model_validate(data)
217
+ assert c.phone_default == 2
218
+
219
+ def test_phone_default_default_value(self):
220
+ """Výchozí hodnota phone_default je 0"""
221
+ c = ContactType()
222
+ assert c.phone_default == 0
223
+
224
+ def test_email_default_default_value(self):
225
+ """Výchozí hodnota email_default je 0"""
226
+ c = ContactType()
227
+ assert c.email_default == 0
228
+
229
+ def test_email_default_set(self):
230
+ """email_default lze nastavit"""
231
+ c = ContactType(email_default=3)
232
+ assert c.email_default == 3
233
+
234
+ def test_model_dump_snake_case(self):
235
+ """model_dump() vrátí phone_default (snake_case)"""
236
+ c = ContactType(phone_default=1)
237
+ d = c.model_dump()
238
+ assert "phone_default" in d
239
+ assert d["phone_default"] == 1
240
+
241
+ def test_model_dump_by_alias(self):
242
+ """model_dump(by_alias=True) vrátí phoneDefault (camelCase)"""
243
+ c = ContactType(phone_default=1)
244
+ d = c.model_dump(by_alias=True)
245
+ assert "phoneDefault" in d
246
+ assert d["phoneDefault"] == 1
247
+
248
+ def test_model_dump_by_alias_excludes_snake(self):
249
+ """model_dump(by_alias=True) neobsahuje phone_default snake variantu"""
250
+ c = ContactType(phone_default=1)
251
+ d = c.model_dump(by_alias=True)
252
+ assert "phone_default" not in d
253
+
254
+
255
+ # ---------------------------------------------------------------------------
256
+ # ContactType — bcn.5: hraniční případy
257
+ # ---------------------------------------------------------------------------
258
+
259
+ class TestContactTypeEdgeCases:
260
+ """Hraniční případy ContactType (bcn.5)"""
261
+
262
+ def test_all_none_fields(self):
263
+ """ContactType přijme explicitní None u všech Optional polí"""
264
+ c = ContactType(
265
+ identifier=None,
266
+ first_name=None,
267
+ last_name=None,
268
+ degree_before=None,
269
+ degree_after=None,
270
+ full_name=None,
271
+ birthdate=None,
272
+ phone=None,
273
+ email=None,
274
+ web=None,
275
+ isds=None,
276
+ reference=None,
277
+ )
278
+ assert c.phone is None
279
+ assert c.email is None
280
+
281
+ def test_empty_lists_for_phone_and_email(self):
282
+ """Prázdný seznam pro phone/email je validní"""
283
+ c = ContactType(phone=[], email=[])
284
+ assert c.phone == []
285
+ assert c.email == []
286
+
287
+ def test_degree_before_with_none_items(self):
288
+ """List titulů může obsahovat None hodnoty"""
289
+ c = ContactType(degree_before=["Ing.", None, "Bc."])
290
+ assert c.degree_before == ["Ing.", None, "Bc."]
291
+
292
+ def test_phone_list_with_none_item(self):
293
+ """Seznam telefonů může obsahovat None (dle type annotation)"""
294
+ c = ContactType(phone=[make_phone(), None])
295
+ assert c.phone[0].number == "602000001"
296
+ assert c.phone[1] is None
297
+
298
+ def test_serialization_round_trip(self):
299
+ """model_dump → model_validate zachová data"""
300
+ original = ContactType(
301
+ identifier="abc-123",
302
+ first_name="Petr",
303
+ last_name="Kos",
304
+ phone=[make_phone()],
305
+ email=[make_email()],
306
+ phone_default=0,
307
+ email_default=0,
308
+ )
309
+ data = original.model_dump()
310
+ restored = ContactType.model_validate(data)
311
+ assert restored.identifier == original.identifier
312
+ assert restored.first_name == original.first_name
313
+ assert restored.phone[0].number == original.phone[0].number
314
+ assert restored.email[0].email == original.email[0].email
315
+
316
+ def test_json_round_trip(self):
317
+ """model_dump_json → model_validate_json zachová data"""
318
+ original = ContactType(
319
+ first_name="Jana",
320
+ isds="xyz9876",
321
+ phone=[make_phone(number="777888999")],
322
+ )
323
+ json_str = original.model_dump_json()
324
+ restored = ContactType.model_validate_json(json_str)
325
+ assert restored.first_name == "Jana"
326
+ assert restored.isds == "xyz9876"
327
+ assert restored.phone[0].number == "777888999"
328
+
329
+ def test_export_from_package_init(self):
330
+ """ContactType je exportován z hlavního __init__ balíčku"""
331
+ from sysnet_pyutils import ContactType as CT
332
+ assert CT is ContactType
333
+
File without changes
File without changes
File without changes