GeneralManager 0.3.2__tar.gz → 0.4.1__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.
- {generalmanager-0.3.2 → generalmanager-0.4.1}/GeneralManager.egg-info/PKG-INFO +1 -1
- {generalmanager-0.3.2 → generalmanager-0.4.1}/GeneralManager.egg-info/SOURCES.txt +3 -1
- {generalmanager-0.3.2 → generalmanager-0.4.1}/PKG-INFO +1 -1
- {generalmanager-0.3.2 → generalmanager-0.4.1}/pyproject.toml +1 -1
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/factory/__init__.py +1 -1
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/factory/factories.py +71 -54
- generalmanager-0.4.1/src/general_manager/factory/factoryMethods.py +109 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/measurement/measurement.py +8 -0
- generalmanager-0.4.1/tests/test_factories.py +350 -0
- generalmanager-0.4.1/tests/test_factoryMethods.py +228 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_noneToZero.py +2 -2
- generalmanager-0.3.2/src/general_manager/factory/lazy_methods.py +0 -38
- {generalmanager-0.3.2 → generalmanager-0.4.1}/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/LICENSE +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/README.md +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/setup.cfg +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/apps.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/auxiliary/__init__.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/auxiliary/argsToKwargs.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/auxiliary/filterParser.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/auxiliary/jsonEncoder.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/auxiliary/makeCacheKey.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/auxiliary/noneToZero.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/auxiliary/pathMapping.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/cache/cacheDecorator.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/cache/cacheTracker.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/cache/dependencyIndex.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/cache/modelDependencyCollector.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/interface/baseInterface.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/interface/calculationInterface.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/interface/databaseInterface.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/manager/generalManager.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/manager/groupManager.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/measurement/measurementField.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/permission/basePermission.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/permission/fileBasedPermission.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/permission/managerBasedPermission.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/permission/permissionChecks.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/permission/permissionDataManager.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_argsToKwargs.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_basePermission.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_cacheDecorator.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_cacheTracker.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_dependencyIndex.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_filterParser.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_graph_ql.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_input.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_jsonEncoder.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_makeCacheKey.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_managerBasedPermission.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_measurement.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_measurement_field.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_modelDependencyCollector.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_rule_handler.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_rules.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_settings.py +0 -0
- {generalmanager-0.3.2 → generalmanager-0.4.1}/tests/test_signals.py +0 -0
@@ -25,7 +25,7 @@ src/general_manager/cache/modelDependencyCollector.py
|
|
25
25
|
src/general_manager/cache/signals.py
|
26
26
|
src/general_manager/factory/__init__.py
|
27
27
|
src/general_manager/factory/factories.py
|
28
|
-
src/general_manager/factory/
|
28
|
+
src/general_manager/factory/factoryMethods.py
|
29
29
|
src/general_manager/interface/__init__.py
|
30
30
|
src/general_manager/interface/baseInterface.py
|
31
31
|
src/general_manager/interface/calculationInterface.py
|
@@ -52,6 +52,8 @@ tests/test_basePermission.py
|
|
52
52
|
tests/test_cacheDecorator.py
|
53
53
|
tests/test_cacheTracker.py
|
54
54
|
tests/test_dependencyIndex.py
|
55
|
+
tests/test_factories.py
|
56
|
+
tests/test_factoryMethods.py
|
55
57
|
tests/test_filterParser.py
|
56
58
|
tests/test_graph_ql.py
|
57
59
|
tests/test_input.py
|
@@ -1,17 +1,16 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
from typing import TYPE_CHECKING, Type, Callable, Union, Any, TypeVar, Literal, cast
|
3
|
-
from factory.declarations import LazyFunction
|
3
|
+
from factory.declarations import LazyFunction
|
4
4
|
from factory.faker import Faker
|
5
5
|
import exrex # type: ignore
|
6
6
|
from django.db import models
|
7
7
|
from django.core.validators import RegexValidator
|
8
8
|
from factory.django import DjangoModelFactory
|
9
|
-
from django.utils import timezone
|
10
9
|
import random
|
11
10
|
from decimal import Decimal
|
12
11
|
from general_manager.measurement.measurement import Measurement
|
13
12
|
from general_manager.measurement.measurementField import MeasurementField
|
14
|
-
from datetime import date, datetime, time
|
13
|
+
from datetime import date, datetime, time, timezone
|
15
14
|
|
16
15
|
if TYPE_CHECKING:
|
17
16
|
from general_manager.interface.databaseInterface import (
|
@@ -33,7 +32,7 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
33
32
|
) = None
|
34
33
|
|
35
34
|
@classmethod
|
36
|
-
def _generate(
|
35
|
+
def _generate(
|
37
36
|
cls, strategy: Literal["build", "create"], params: dict[str, Any]
|
38
37
|
) -> models.Model | list[models.Model]:
|
39
38
|
cls._original_params = params
|
@@ -64,7 +63,7 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
64
63
|
continue # Skip fields that are already set
|
65
64
|
if isinstance(field, models.AutoField) or field.auto_created:
|
66
65
|
continue # Skip auto fields
|
67
|
-
params[field.name] =
|
66
|
+
params[field.name] = getFieldValue(field)
|
68
67
|
|
69
68
|
obj: list[models.Model] | models.Model = super()._generate(strategy, params)
|
70
69
|
if isinstance(obj, list):
|
@@ -84,7 +83,7 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
84
83
|
if field.name in attrs:
|
85
84
|
m2m_values = attrs[field.name]
|
86
85
|
else:
|
87
|
-
m2m_values =
|
86
|
+
m2m_values = getManyToManyFieldValue(field)
|
88
87
|
if m2m_values:
|
89
88
|
getattr(obj, field.name).set(m2m_values)
|
90
89
|
|
@@ -98,7 +97,7 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
98
97
|
return kwargs
|
99
98
|
|
100
99
|
@classmethod
|
101
|
-
def _create(
|
100
|
+
def _create(
|
102
101
|
cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
|
103
102
|
) -> models.Model | list[models.Model]:
|
104
103
|
kwargs = cls._adjust_kwargs(**kwargs)
|
@@ -107,7 +106,7 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
107
106
|
return cls._modelCreation(model_class, **kwargs)
|
108
107
|
|
109
108
|
@classmethod
|
110
|
-
def _build(
|
109
|
+
def _build(
|
111
110
|
cls, model_class: Type[models.Model], *args: list[Any], **kwargs: dict[str, Any]
|
112
111
|
) -> models.Model | list[models.Model]:
|
113
112
|
kwargs = cls._adjust_kwargs(**kwargs)
|
@@ -157,7 +156,7 @@ class AutoFactory(DjangoModelFactory[modelsModel]):
|
|
157
156
|
return created_objects
|
158
157
|
|
159
158
|
|
160
|
-
def
|
159
|
+
def getFieldValue(field: models.Field[Any, Any] | models.ForeignObjectRel) -> object:
|
161
160
|
"""
|
162
161
|
Returns a suitable value for a given Django model field.
|
163
162
|
"""
|
@@ -166,22 +165,12 @@ def get_field_value(field: models.Field[Any, Any] | models.ForeignObjectRel) ->
|
|
166
165
|
return None
|
167
166
|
|
168
167
|
if isinstance(field, MeasurementField):
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
regex = None
|
176
|
-
for validator in field.validators:
|
177
|
-
if isinstance(validator, RegexValidator):
|
178
|
-
regex = getattr(validator.regex, "pattern", None)
|
179
|
-
break
|
180
|
-
if regex:
|
181
|
-
# Use exrex to generate a string matching the regex
|
182
|
-
return LazyFunction(lambda: exrex.getone(regex)) # type: ignore
|
183
|
-
else:
|
184
|
-
return cast(str, Faker("text", max_nb_chars=max_length))
|
168
|
+
|
169
|
+
def _measurement():
|
170
|
+
value = Decimal(random.randrange(0, 10_000_000)) / Decimal("100") # two dp
|
171
|
+
return Measurement(value, field.base_unit)
|
172
|
+
|
173
|
+
return LazyFunction(_measurement)
|
185
174
|
elif isinstance(field, models.TextField):
|
186
175
|
return cast(str, Faker("paragraph"))
|
187
176
|
elif isinstance(field, models.IntegerField):
|
@@ -201,8 +190,6 @@ def get_field_value(field: models.Field[Any, Any] | models.ForeignObjectRel) ->
|
|
201
190
|
)
|
202
191
|
elif isinstance(field, models.FloatField):
|
203
192
|
return cast(float, Faker("pyfloat", positive=True))
|
204
|
-
elif isinstance(field, models.DateField):
|
205
|
-
return cast(date, Faker("date_between", start_date="-1y", end_date="today"))
|
206
193
|
elif isinstance(field, models.DateTimeField):
|
207
194
|
return cast(
|
208
195
|
datetime,
|
@@ -213,10 +200,11 @@ def get_field_value(field: models.Field[Any, Any] | models.ForeignObjectRel) ->
|
|
213
200
|
tzinfo=timezone.utc,
|
214
201
|
),
|
215
202
|
)
|
203
|
+
elif isinstance(field, models.DateField):
|
204
|
+
return cast(date, Faker("date_between", start_date="-1y", end_date="today"))
|
216
205
|
elif isinstance(field, models.BooleanField):
|
217
206
|
return cast(bool, Faker("pybool"))
|
218
|
-
elif isinstance(field, models.
|
219
|
-
# Create or get an instance of the related model
|
207
|
+
elif isinstance(field, models.OneToOneField):
|
220
208
|
if hasattr(field.related_model, "_general_manager_class"):
|
221
209
|
related_factory = field.related_model._general_manager_class.Factory
|
222
210
|
return related_factory()
|
@@ -229,11 +217,19 @@ def get_field_value(field: models.Field[Any, Any] | models.ForeignObjectRel) ->
|
|
229
217
|
raise ValueError(
|
230
218
|
f"No factory found for {field.related_model.__name__} and no instances found"
|
231
219
|
)
|
232
|
-
elif isinstance(field, models.
|
233
|
-
#
|
220
|
+
elif isinstance(field, models.ForeignKey):
|
221
|
+
# Create or get an instance of the related model
|
234
222
|
if hasattr(field.related_model, "_general_manager_class"):
|
223
|
+
create_a_new_instance = random.choice([True, True, False])
|
224
|
+
if not create_a_new_instance:
|
225
|
+
existing_instances = list(field.related_model.objects.all())
|
226
|
+
if existing_instances:
|
227
|
+
# Pick a random existing instance
|
228
|
+
return LazyFunction(lambda: random.choice(existing_instances))
|
229
|
+
|
235
230
|
related_factory = field.related_model._general_manager_class.Factory
|
236
231
|
return related_factory()
|
232
|
+
|
237
233
|
else:
|
238
234
|
# If no factory exists, pick a random existing instance
|
239
235
|
related_instances = list(field.related_model.objects.all())
|
@@ -253,35 +249,56 @@ def get_field_value(field: models.Field[Any, Any] | models.ForeignObjectRel) ->
|
|
253
249
|
return cast(str, Faker("uuid4"))
|
254
250
|
elif isinstance(field, models.DurationField):
|
255
251
|
return cast(time, Faker("time_delta"))
|
252
|
+
elif isinstance(field, models.CharField):
|
253
|
+
max_length = field.max_length or 100
|
254
|
+
# Check for RegexValidator
|
255
|
+
regex = None
|
256
|
+
for validator in field.validators:
|
257
|
+
if isinstance(validator, RegexValidator):
|
258
|
+
regex = getattr(validator.regex, "pattern", None)
|
259
|
+
break
|
260
|
+
if regex:
|
261
|
+
# Use exrex to generate a string matching the regex
|
262
|
+
return LazyFunction(lambda: exrex.getone(regex)) # type: ignore
|
263
|
+
else:
|
264
|
+
return cast(str, Faker("text", max_nb_chars=max_length))
|
256
265
|
else:
|
257
266
|
return None # For unsupported field types
|
258
267
|
|
259
268
|
|
260
|
-
def
|
269
|
+
def getManyToManyFieldValue(
|
270
|
+
field: models.ManyToManyField,
|
271
|
+
) -> list[models.Model]:
|
261
272
|
"""
|
262
273
|
Returns a list of instances for a ManyToMany field.
|
263
274
|
"""
|
264
|
-
related_factory =
|
265
|
-
|
275
|
+
related_factory = None
|
276
|
+
related_instances = list(field.related_model.objects.all())
|
277
|
+
if hasattr(field.related_model, "_general_manager_class"):
|
278
|
+
related_factory = field.related_model._general_manager_class.Factory
|
266
279
|
|
267
|
-
if
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
280
|
+
min_required = 0 if field.blank else 1
|
281
|
+
number_of_instances = random.randint(min_required, 10)
|
282
|
+
if related_factory and related_instances:
|
283
|
+
number_to_create = random.randint(min_required, number_of_instances)
|
284
|
+
number_to_pick = number_of_instances - number_to_create
|
285
|
+
if number_to_pick > len(related_instances):
|
286
|
+
number_to_pick = len(related_instances)
|
287
|
+
existing_instances = random.sample(related_instances, number_to_pick)
|
288
|
+
new_instances = [related_factory() for _ in range(number_to_create)]
|
289
|
+
return existing_instances + new_instances
|
290
|
+
elif related_factory:
|
291
|
+
number_to_create = number_of_instances
|
292
|
+
new_instances = [related_factory() for _ in range(number_to_create)]
|
293
|
+
return new_instances
|
294
|
+
elif related_instances:
|
295
|
+
number_to_create = 0
|
296
|
+
number_to_pick = number_of_instances
|
297
|
+
if number_to_pick > len(related_instances):
|
298
|
+
number_to_pick = len(related_instances)
|
299
|
+
existing_instances = random.sample(related_instances, number_to_pick)
|
300
|
+
return existing_instances
|
278
301
|
else:
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
num_instances = random.randint(0, max_instances)
|
283
|
-
return random.sample(existing_instances, num_instances)
|
284
|
-
else:
|
285
|
-
raise ValueError(
|
286
|
-
f"No factory found for {field.related_model.__name__} and no instances found"
|
287
|
-
)
|
302
|
+
raise ValueError(
|
303
|
+
f"No factory found for {field.related_model.__name__} and no instances found"
|
304
|
+
)
|
@@ -0,0 +1,109 @@
|
|
1
|
+
from typing import Any, Optional
|
2
|
+
from factory.declarations import LazyFunction, LazyAttribute, LazyAttributeSequence
|
3
|
+
import random
|
4
|
+
from general_manager.measurement.measurement import Measurement
|
5
|
+
from datetime import timedelta, date, datetime
|
6
|
+
from faker import Faker
|
7
|
+
import uuid
|
8
|
+
from decimal import Decimal
|
9
|
+
|
10
|
+
fake = Faker()
|
11
|
+
|
12
|
+
|
13
|
+
def LazyMeasurement(
|
14
|
+
min_value: int | float, max_value: int | float, unit: str
|
15
|
+
) -> LazyFunction:
|
16
|
+
return LazyFunction(
|
17
|
+
lambda: Measurement(str(random.uniform(min_value, max_value))[:10], unit)
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
def LazyDeltaDate(avg_delta_days: int, base_attribute: str) -> LazyAttribute:
|
22
|
+
return LazyAttribute(
|
23
|
+
lambda obj: (getattr(obj, base_attribute) or date.today())
|
24
|
+
+ timedelta(days=random.randint(avg_delta_days // 2, avg_delta_days * 3 // 2))
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
def LazyProjectName() -> LazyFunction:
|
29
|
+
return LazyFunction(
|
30
|
+
lambda: (
|
31
|
+
f"{fake.word().capitalize()} "
|
32
|
+
f"{fake.word().capitalize()} "
|
33
|
+
f"{fake.random_element(elements=('X', 'Z', 'G'))}"
|
34
|
+
f"-{fake.random_int(min=1, max=1000)}"
|
35
|
+
)
|
36
|
+
)
|
37
|
+
|
38
|
+
|
39
|
+
def LazyDateToday() -> LazyFunction:
|
40
|
+
return LazyFunction(lambda: date.today())
|
41
|
+
|
42
|
+
|
43
|
+
def LazyDateBetween(start_date: date, end_date: date) -> LazyAttribute:
|
44
|
+
delta = (end_date - start_date).days
|
45
|
+
return LazyAttribute(
|
46
|
+
lambda obj: start_date + timedelta(days=random.randint(0, delta))
|
47
|
+
)
|
48
|
+
|
49
|
+
|
50
|
+
def LazyDateTimeBetween(start: datetime, end: datetime) -> LazyAttribute:
|
51
|
+
span = (end - start).total_seconds()
|
52
|
+
return LazyAttribute(
|
53
|
+
lambda obj: start + timedelta(seconds=random.randint(0, int(span)))
|
54
|
+
)
|
55
|
+
|
56
|
+
|
57
|
+
def LazyInteger(min_value: int, max_value: int) -> LazyFunction:
|
58
|
+
return LazyFunction(lambda: random.randint(min_value, max_value))
|
59
|
+
|
60
|
+
|
61
|
+
def LazyDecimal(min_value: float, max_value: float, precision: int = 2) -> LazyFunction:
|
62
|
+
fmt = f"{{:.{precision}f}}"
|
63
|
+
return LazyFunction(
|
64
|
+
lambda: Decimal(fmt.format(random.uniform(min_value, max_value)))
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
def LazyChoice(options: list[Any]) -> LazyFunction:
|
69
|
+
return LazyFunction(lambda: random.choice(options))
|
70
|
+
|
71
|
+
|
72
|
+
def LazySequence(start: int = 0, step: int = 1) -> LazyAttributeSequence:
|
73
|
+
return LazyAttributeSequence(lambda obj, n: start + n * step)
|
74
|
+
|
75
|
+
|
76
|
+
def LazyBoolean(trues_ratio: float = 0.5) -> LazyFunction:
|
77
|
+
return LazyFunction(lambda: random.random() < trues_ratio)
|
78
|
+
|
79
|
+
|
80
|
+
def LazyUUID() -> LazyFunction:
|
81
|
+
return LazyFunction(lambda: str(uuid.uuid4()))
|
82
|
+
|
83
|
+
|
84
|
+
def LazyFakerName() -> LazyFunction:
|
85
|
+
return LazyFunction(lambda: fake.name())
|
86
|
+
|
87
|
+
|
88
|
+
def LazyFakerEmail(
|
89
|
+
name: Optional[str] = None, domain: Optional[str] = None
|
90
|
+
) -> LazyFunction:
|
91
|
+
if not name and not domain:
|
92
|
+
return LazyFunction(lambda: fake.email(domain=domain))
|
93
|
+
if not name:
|
94
|
+
name = fake.name()
|
95
|
+
if not domain:
|
96
|
+
domain = fake.domain_name()
|
97
|
+
return LazyFunction(lambda: name.replace(" ", "_") + "@" + domain)
|
98
|
+
|
99
|
+
|
100
|
+
def LazyFakerSentence(number_of_words: int = 6) -> LazyFunction:
|
101
|
+
return LazyFunction(lambda: fake.sentence(nb_words=number_of_words))
|
102
|
+
|
103
|
+
|
104
|
+
def LazyFakerAddress() -> LazyFunction:
|
105
|
+
return LazyFunction(lambda: fake.address())
|
106
|
+
|
107
|
+
|
108
|
+
def LazyFakerUrl() -> LazyFunction:
|
109
|
+
return LazyFunction(lambda: fake.url())
|
{generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/measurement/measurement.py
RENAMED
@@ -46,6 +46,14 @@ class Measurement:
|
|
46
46
|
def quantity(self) -> pint.Quantity:
|
47
47
|
return self.__quantity
|
48
48
|
|
49
|
+
@property
|
50
|
+
def magnitude(self) -> Decimal:
|
51
|
+
return self.__quantity.magnitude
|
52
|
+
|
53
|
+
@property
|
54
|
+
def unit(self) -> str:
|
55
|
+
return str(self.__quantity.units)
|
56
|
+
|
49
57
|
@classmethod
|
50
58
|
def from_string(cls, value: str) -> Measurement:
|
51
59
|
value, unit = value.split(" ")
|
@@ -0,0 +1,350 @@
|
|
1
|
+
# tests/test_factory_helpers.py
|
2
|
+
from datetime import date, datetime, timedelta
|
3
|
+
from decimal import Decimal
|
4
|
+
|
5
|
+
from django.db import models
|
6
|
+
from django.test import TestCase
|
7
|
+
from django.core.validators import RegexValidator
|
8
|
+
|
9
|
+
from factory.declarations import LazyFunction, LazyAttribute
|
10
|
+
from factory.faker import Faker
|
11
|
+
from general_manager.factory.factories import getFieldValue, getManyToManyFieldValue
|
12
|
+
from general_manager.measurement.measurementField import MeasurementField
|
13
|
+
from general_manager.measurement.measurement import Measurement
|
14
|
+
from unittest.mock import patch
|
15
|
+
|
16
|
+
|
17
|
+
class DummyForeignKey(models.Model):
|
18
|
+
"""
|
19
|
+
Dummy ForeignKey model for testing purposes.
|
20
|
+
"""
|
21
|
+
|
22
|
+
name = models.CharField(max_length=10, null=False)
|
23
|
+
|
24
|
+
class Meta:
|
25
|
+
app_label = "tests"
|
26
|
+
managed = False
|
27
|
+
|
28
|
+
|
29
|
+
class DummyForeignKey2(models.Model):
|
30
|
+
"""
|
31
|
+
Dummy ForeignKey model for testing purposes.
|
32
|
+
"""
|
33
|
+
|
34
|
+
name = models.CharField(max_length=10, null=False)
|
35
|
+
|
36
|
+
class Meta:
|
37
|
+
app_label = "tests"
|
38
|
+
managed = False
|
39
|
+
|
40
|
+
|
41
|
+
class DummyManyToMany(models.Model):
|
42
|
+
"""
|
43
|
+
Dummy ManyToMany model for testing purposes.
|
44
|
+
"""
|
45
|
+
|
46
|
+
name = models.CharField(max_length=10, null=False)
|
47
|
+
|
48
|
+
class Meta:
|
49
|
+
app_label = "tests"
|
50
|
+
managed = False
|
51
|
+
|
52
|
+
|
53
|
+
class DummyModel(models.Model):
|
54
|
+
# String without Regex‐Validator
|
55
|
+
char_field = models.CharField(max_length=10, null=False)
|
56
|
+
text_field = models.TextField(null=False)
|
57
|
+
# String with Regex‐Validator
|
58
|
+
regex_field = models.CharField(
|
59
|
+
max_length=10, null=False, validators=[RegexValidator(r"[A-Z]{3}\d{2}")]
|
60
|
+
)
|
61
|
+
# number types
|
62
|
+
int_field = models.IntegerField(null=False)
|
63
|
+
dec_field = models.DecimalField(max_digits=5, decimal_places=2, null=False)
|
64
|
+
float_field = models.FloatField(null=False)
|
65
|
+
# date/time types
|
66
|
+
date_field = models.DateField(null=False)
|
67
|
+
datetime_field = models.DateTimeField(null=False)
|
68
|
+
duration_field = models.DurationField(null=False)
|
69
|
+
# etc
|
70
|
+
bool_field = models.BooleanField(null=False)
|
71
|
+
email_field = models.EmailField(null=False)
|
72
|
+
url_field = models.URLField(null=False)
|
73
|
+
ip_field = models.GenericIPAddressField(null=False)
|
74
|
+
uuid_field = models.UUIDField(null=False)
|
75
|
+
# MeasurementField
|
76
|
+
measurement_field = MeasurementField(base_unit="kg", null=False)
|
77
|
+
# special fields
|
78
|
+
test_none = models.CharField(max_length=10, null=True)
|
79
|
+
dummy_fk = models.ForeignKey(
|
80
|
+
DummyForeignKey,
|
81
|
+
on_delete=models.CASCADE,
|
82
|
+
null=False,
|
83
|
+
)
|
84
|
+
dummy_one_to_one = models.OneToOneField(
|
85
|
+
DummyForeignKey2,
|
86
|
+
on_delete=models.CASCADE,
|
87
|
+
null=False,
|
88
|
+
)
|
89
|
+
dummy_m2m = models.ManyToManyField(
|
90
|
+
DummyManyToMany,
|
91
|
+
related_name="dummy_m2m",
|
92
|
+
blank=True,
|
93
|
+
)
|
94
|
+
|
95
|
+
class Meta:
|
96
|
+
app_label = "tests"
|
97
|
+
managed = False
|
98
|
+
|
99
|
+
|
100
|
+
class TestGetFieldValue(TestCase):
|
101
|
+
def _evaluate(self, declaration):
|
102
|
+
"""
|
103
|
+
Evaluate a field declaration to get its value.
|
104
|
+
This is a helper method to handle different types of declarations.
|
105
|
+
"""
|
106
|
+
obj = type("DummyModel", (object,), {})()
|
107
|
+
if isinstance(declaration, (LazyFunction, LazyAttribute, Faker)):
|
108
|
+
return declaration.evaluate(obj, None, {"locale": "en_US"})
|
109
|
+
return declaration
|
110
|
+
|
111
|
+
def test_all_not_relational_field_types(self):
|
112
|
+
field_expectations = [
|
113
|
+
# (fieldname, expected_type, optional: regex to match)
|
114
|
+
("char_field", str, None),
|
115
|
+
("text_field", str, None),
|
116
|
+
("regex_field", str, r"^[A-Z]{3}\d{2}$"),
|
117
|
+
("int_field", int, None),
|
118
|
+
("dec_field", Decimal, None),
|
119
|
+
("float_field", float, None),
|
120
|
+
("date_field", date, None),
|
121
|
+
("datetime_field", datetime, None),
|
122
|
+
("duration_field", timedelta, None),
|
123
|
+
("bool_field", bool, None),
|
124
|
+
("email_field", str, r"^[^@]+@[^@]+\.[^@]+$"),
|
125
|
+
("url_field", str, r"^https?://"),
|
126
|
+
("ip_field", str, r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"),
|
127
|
+
("uuid_field", str, r"^[0-9a-fA-F-]{36}$"),
|
128
|
+
]
|
129
|
+
|
130
|
+
for name, expected_type, pattern in field_expectations:
|
131
|
+
with self.subTest(field=name):
|
132
|
+
field = DummyModel._meta.get_field(name)
|
133
|
+
decl = getFieldValue(field)
|
134
|
+
|
135
|
+
self.assertIn(
|
136
|
+
decl.__class__.__name__,
|
137
|
+
("LazyFunction", "LazyAttribute", "NoneType", "Faker"),
|
138
|
+
msg=f"Unexpected declaration for {name}: {decl!r}",
|
139
|
+
)
|
140
|
+
|
141
|
+
value = self._evaluate(decl)
|
142
|
+
|
143
|
+
self.assertIsInstance(
|
144
|
+
value,
|
145
|
+
expected_type,
|
146
|
+
msg=f"Field {name!r} returned {value!r} ({type(value)})",
|
147
|
+
)
|
148
|
+
|
149
|
+
if pattern:
|
150
|
+
self.assertRegex(
|
151
|
+
str(value),
|
152
|
+
pattern,
|
153
|
+
msg=f"Value for {name!r} does not match {pattern}",
|
154
|
+
)
|
155
|
+
|
156
|
+
def test_nullable_field(self):
|
157
|
+
with patch(
|
158
|
+
"general_manager.factory.factories.random.choice",
|
159
|
+
return_value=True,
|
160
|
+
):
|
161
|
+
field = DummyModel._meta.get_field("test_none")
|
162
|
+
decl = getFieldValue(field)
|
163
|
+
value = self._evaluate(decl)
|
164
|
+
self.assertIsNone(value, msg="Nullable field should return None")
|
165
|
+
|
166
|
+
def test_measurement_field(self):
|
167
|
+
field = DummyModel.measurement_field
|
168
|
+
decl = getFieldValue(field)
|
169
|
+
self.assertIsInstance(decl, LazyFunction)
|
170
|
+
value = decl.evaluate(None, None, None) # type: ignore
|
171
|
+
self.assertIsInstance(value, Measurement)
|
172
|
+
self.assertIsInstance(value.magnitude, Decimal)
|
173
|
+
self.assertIsInstance(value.unit, str)
|
174
|
+
|
175
|
+
|
176
|
+
class TestRelationFieldValue(TestCase):
|
177
|
+
def setUp(self):
|
178
|
+
# Aufräumen: kein _general_manager_class voraussetzen
|
179
|
+
for M in (DummyForeignKey, DummyForeignKey2):
|
180
|
+
if hasattr(M, "_general_manager_class"):
|
181
|
+
delattr(M, "_general_manager_class")
|
182
|
+
|
183
|
+
def test_fk_with_factory_new_instance(self):
|
184
|
+
# 1) _general_manager_class.Factory liefert direkt ein Objekt
|
185
|
+
dummy = DummyForeignKey(name="foo")
|
186
|
+
|
187
|
+
class GMC:
|
188
|
+
pass
|
189
|
+
|
190
|
+
GMC.Factory = lambda **kwargs: dummy # type: ignore
|
191
|
+
DummyForeignKey._general_manager_class = GMC # type: ignore
|
192
|
+
with patch(
|
193
|
+
"general_manager.factory.factoryMethods.random.choice", return_value=True
|
194
|
+
):
|
195
|
+
|
196
|
+
field = DummyModel._meta.get_field("dummy_fk")
|
197
|
+
result = getFieldValue(field)
|
198
|
+
# Hier kommt kein LazyFunction, sondern direkt das factory-Ergebnis
|
199
|
+
self.assertIs(result, dummy)
|
200
|
+
|
201
|
+
def test_fk_with_factory_existing_instance(self):
|
202
|
+
dummy1 = DummyForeignKey(name="a")
|
203
|
+
dummy2 = DummyForeignKey(name="b")
|
204
|
+
field = DummyModel._meta.get_field("dummy_fk")
|
205
|
+
|
206
|
+
class GMC:
|
207
|
+
pass
|
208
|
+
|
209
|
+
GMC.Factory = lambda **kwargs: dummy1 # type: ignore
|
210
|
+
DummyForeignKey._general_manager_class = GMC # type: ignore
|
211
|
+
|
212
|
+
with (
|
213
|
+
patch(
|
214
|
+
"general_manager.factory.factories.random.choice",
|
215
|
+
return_value=False,
|
216
|
+
),
|
217
|
+
patch.object(DummyForeignKey.objects, "all", return_value=[dummy1, dummy2]),
|
218
|
+
):
|
219
|
+
decl = getFieldValue(field)
|
220
|
+
self.assertIsInstance(decl, LazyFunction)
|
221
|
+
inst = decl.evaluate(None, None, None) # type: ignore
|
222
|
+
self.assertIn(inst, (dummy1, dummy2))
|
223
|
+
|
224
|
+
def test_one_to_one_with_factory(self):
|
225
|
+
dummy = DummyForeignKey2(name="bar")
|
226
|
+
|
227
|
+
class GMC2:
|
228
|
+
pass
|
229
|
+
|
230
|
+
GMC2.Factory = lambda **kwargs: dummy # type: ignore
|
231
|
+
DummyForeignKey2._general_manager_class = GMC2 # type: ignore
|
232
|
+
|
233
|
+
field = DummyModel._meta.get_field("dummy_one_to_one")
|
234
|
+
result = getFieldValue(field)
|
235
|
+
self.assertIs(result, dummy)
|
236
|
+
|
237
|
+
def test_fk_without_factory_with_existing_instances(self):
|
238
|
+
# 2) kein factory, aber vorhandene Objekte → LazyFunction
|
239
|
+
dummy1 = DummyForeignKey(name="a")
|
240
|
+
dummy2 = DummyForeignKey(name="b")
|
241
|
+
field = DummyModel._meta.get_field("dummy_fk")
|
242
|
+
|
243
|
+
with patch.object(
|
244
|
+
DummyForeignKey.objects, "all", return_value=[dummy1, dummy2]
|
245
|
+
):
|
246
|
+
decl = getFieldValue(field)
|
247
|
+
self.assertIsInstance(decl, LazyFunction)
|
248
|
+
# beim Evaluieren sollte eins der beiden Objekte zurückkommen
|
249
|
+
inst = decl.evaluate(None, None, None) # type: ignore
|
250
|
+
self.assertIn(inst, (dummy1, dummy2))
|
251
|
+
|
252
|
+
def test_one_to_one_without_factory_with_existing_instances(self):
|
253
|
+
dummy1 = DummyForeignKey2(name="x")
|
254
|
+
dummy2 = DummyForeignKey2(name="y")
|
255
|
+
field = DummyModel._meta.get_field("dummy_one_to_one")
|
256
|
+
|
257
|
+
with patch.object(
|
258
|
+
DummyForeignKey2.objects, "all", return_value=[dummy1, dummy2]
|
259
|
+
):
|
260
|
+
decl = getFieldValue(field)
|
261
|
+
self.assertIsInstance(decl, LazyFunction)
|
262
|
+
inst = decl.evaluate(None, None, None) # type: ignore
|
263
|
+
self.assertIn(inst, (dummy1, dummy2))
|
264
|
+
|
265
|
+
def test_fk_without_factory_and_no_instances_raises(self):
|
266
|
+
field = DummyModel._meta.get_field("dummy_fk")
|
267
|
+
with (
|
268
|
+
patch.object(DummyForeignKey.objects, "all", return_value=[]),
|
269
|
+
self.assertRaisesMessage(
|
270
|
+
ValueError, "No factory found for DummyForeignKey"
|
271
|
+
),
|
272
|
+
):
|
273
|
+
getFieldValue(field)
|
274
|
+
|
275
|
+
def test_one_to_one_without_factory_and_no_instances_raises(self):
|
276
|
+
field = DummyModel._meta.get_field("dummy_one_to_one")
|
277
|
+
with (
|
278
|
+
patch.object(DummyForeignKey2.objects, "all", return_value=[]),
|
279
|
+
self.assertRaisesMessage(
|
280
|
+
ValueError, "No factory found for DummyForeignKey2"
|
281
|
+
),
|
282
|
+
):
|
283
|
+
getFieldValue(field)
|
284
|
+
|
285
|
+
|
286
|
+
class TestGetManyToManyFieldValue(TestCase):
|
287
|
+
def setUp(self):
|
288
|
+
# tidy up: no _general_manager_class assumed
|
289
|
+
for M in (DummyManyToMany, DummyModel):
|
290
|
+
if hasattr(M, "_general_manager_class"):
|
291
|
+
delattr(M, "_general_manager_class")
|
292
|
+
|
293
|
+
def test_m2m_with_factory_and_existing(self):
|
294
|
+
dummy1 = DummyManyToMany(name="foo", id=1)
|
295
|
+
dummy2 = DummyManyToMany(name="bar", id=2)
|
296
|
+
|
297
|
+
class GMC:
|
298
|
+
pass
|
299
|
+
|
300
|
+
GMC.Factory = lambda **kwargs: dummy1 # type: ignore
|
301
|
+
DummyManyToMany._general_manager_class = GMC # type: ignore
|
302
|
+
|
303
|
+
field = DummyModel._meta.get_field("dummy_m2m")
|
304
|
+
with patch.object(
|
305
|
+
field.related_model.objects, "all", return_value=[dummy1, dummy2] # type: ignore
|
306
|
+
):
|
307
|
+
result = getManyToManyFieldValue(field) # type: ignore
|
308
|
+
self.assertIsInstance(result, list)
|
309
|
+
self.assertTrue(
|
310
|
+
set(result).issubset({dummy1, dummy2}),
|
311
|
+
"Returned instances are not a subset of existing objects",
|
312
|
+
)
|
313
|
+
|
314
|
+
def test_m2m_with_factory(self):
|
315
|
+
dummy1 = DummyManyToMany(name="foo", id=1)
|
316
|
+
|
317
|
+
class GMC:
|
318
|
+
pass
|
319
|
+
|
320
|
+
GMC.Factory = lambda **kwargs: dummy1 # type: ignore
|
321
|
+
DummyManyToMany._general_manager_class = GMC # type: ignore
|
322
|
+
|
323
|
+
field = DummyModel._meta.get_field("dummy_m2m")
|
324
|
+
with patch.object(field.related_model.objects, "all", return_value=[]): # type: ignore
|
325
|
+
result = getManyToManyFieldValue(field) # type: ignore
|
326
|
+
self.assertIsInstance(result, list)
|
327
|
+
self.assertIn(dummy1, result)
|
328
|
+
|
329
|
+
def test_m2m_without_factory(self):
|
330
|
+
dummy1 = DummyManyToMany(name="foo", id=1)
|
331
|
+
dummy2 = DummyManyToMany(name="bar", id=2)
|
332
|
+
|
333
|
+
field = DummyModel._meta.get_field("dummy_m2m")
|
334
|
+
with patch.object(
|
335
|
+
field.related_model.objects, "all", return_value=[dummy1, dummy2] # type: ignore
|
336
|
+
):
|
337
|
+
result = getManyToManyFieldValue(field) # type: ignore
|
338
|
+
self.assertIsInstance(result, list)
|
339
|
+
self.assertTrue(
|
340
|
+
set(result).issubset({dummy1, dummy2}),
|
341
|
+
"Returned instances are not a subset of existing objects",
|
342
|
+
)
|
343
|
+
|
344
|
+
def test_m2m_without_factory_and_no_instances_raises(self):
|
345
|
+
field = DummyModel._meta.get_field("dummy_m2m")
|
346
|
+
with (
|
347
|
+
patch.object(field.related_model.objects, "all", return_value=[]), # type: ignore
|
348
|
+
self.assertRaises(ValueError),
|
349
|
+
):
|
350
|
+
getManyToManyFieldValue(field) # type: ignore
|
@@ -0,0 +1,228 @@
|
|
1
|
+
from django.test import SimpleTestCase
|
2
|
+
from general_manager.factory.factoryMethods import (
|
3
|
+
LazyMeasurement,
|
4
|
+
LazyDeltaDate,
|
5
|
+
LazyProjectName,
|
6
|
+
LazyDateToday,
|
7
|
+
LazyDateBetween,
|
8
|
+
LazyDateTimeBetween,
|
9
|
+
LazyInteger,
|
10
|
+
LazyDecimal,
|
11
|
+
LazyUUID,
|
12
|
+
LazyBoolean,
|
13
|
+
LazyFakerName,
|
14
|
+
LazyFakerEmail,
|
15
|
+
LazyFakerSentence,
|
16
|
+
LazyFakerAddress,
|
17
|
+
LazyFakerUrl,
|
18
|
+
LazyChoice,
|
19
|
+
LazySequence,
|
20
|
+
)
|
21
|
+
from general_manager.measurement.measurement import Measurement
|
22
|
+
from datetime import date, datetime
|
23
|
+
from decimal import Decimal
|
24
|
+
from types import SimpleNamespace
|
25
|
+
|
26
|
+
|
27
|
+
class TestFactoryMethods(SimpleTestCase):
|
28
|
+
def test_LazyMeasurement(self):
|
29
|
+
min_value = 10.0
|
30
|
+
max_value = 30.5
|
31
|
+
unit = "kilogram"
|
32
|
+
obj = type("TestObject", (object,), {})()
|
33
|
+
for i in range(100):
|
34
|
+
with self.subTest(run=i):
|
35
|
+
measurement = LazyMeasurement(min_value, max_value, unit).evaluate(
|
36
|
+
obj, 1, None
|
37
|
+
)
|
38
|
+
self.assertIsInstance(measurement, Measurement)
|
39
|
+
self.assertTrue(min_value <= float(measurement.magnitude) <= max_value)
|
40
|
+
self.assertEqual(measurement.unit, unit)
|
41
|
+
|
42
|
+
def test_LazyDeltaDate(self):
|
43
|
+
avg_delta_days = 5 # -> 2.5 to 7.5 days
|
44
|
+
base_attribute = "start_date"
|
45
|
+
obj = type("TestObject", (object,), {base_attribute: date(2023, 1, 1)})()
|
46
|
+
for i in range(100):
|
47
|
+
with self.subTest(run=i):
|
48
|
+
delta_date = LazyDeltaDate(avg_delta_days, base_attribute).evaluate(
|
49
|
+
obj, 1, None
|
50
|
+
)
|
51
|
+
self.assertIsInstance(delta_date, date)
|
52
|
+
self.assertTrue(
|
53
|
+
date(2023, 1, 1) <= delta_date <= date(2023, 1, 8),
|
54
|
+
f"Run {i}: {delta_date} is not in the expected range.",
|
55
|
+
)
|
56
|
+
|
57
|
+
def test_LazyProjectName(self):
|
58
|
+
obj = type("TestObject", (object,), {})()
|
59
|
+
for i in range(100):
|
60
|
+
with self.subTest(run=i):
|
61
|
+
project_name = LazyProjectName().evaluate(obj, 1, None)
|
62
|
+
self.assertIsInstance(project_name, str)
|
63
|
+
|
64
|
+
def test_LazyDateToday(self):
|
65
|
+
obj = type("TestObject", (object,), {})()
|
66
|
+
for i in range(100):
|
67
|
+
with self.subTest(run=i):
|
68
|
+
date_today = LazyDateToday().evaluate(obj, 1, None)
|
69
|
+
self.assertIsInstance(date_today, date)
|
70
|
+
self.assertEqual(date_today, date.today())
|
71
|
+
|
72
|
+
def test_LazyDateBetween(self):
|
73
|
+
start_date = date(2023, 1, 1)
|
74
|
+
end_date = date(2023, 12, 31)
|
75
|
+
obj = type("TestObject", (object,), {})()
|
76
|
+
for i in range(100):
|
77
|
+
with self.subTest(run=i):
|
78
|
+
date_between = LazyDateBetween(start_date, end_date).evaluate(
|
79
|
+
obj, 1, None
|
80
|
+
)
|
81
|
+
self.assertIsInstance(date_between, date)
|
82
|
+
self.assertTrue(
|
83
|
+
start_date <= date_between <= end_date,
|
84
|
+
f"Run {i}: {date_between} is not in the expected range.",
|
85
|
+
)
|
86
|
+
|
87
|
+
def test_LazyDateTimeBetween(self):
|
88
|
+
start = datetime(2023, 1, 1)
|
89
|
+
end = datetime(2023, 12, 31)
|
90
|
+
obj = type("TestObject", (object,), {})()
|
91
|
+
for i in range(100):
|
92
|
+
with self.subTest(run=i):
|
93
|
+
datetime_between = LazyDateTimeBetween(start, end).evaluate(
|
94
|
+
obj, 1, None
|
95
|
+
)
|
96
|
+
self.assertIsInstance(datetime_between, datetime)
|
97
|
+
self.assertTrue(
|
98
|
+
start <= datetime_between <= end,
|
99
|
+
f"Run {i}: {datetime_between} is not in the expected range.",
|
100
|
+
)
|
101
|
+
|
102
|
+
def test_LazyInteger(self):
|
103
|
+
min_value = 1
|
104
|
+
max_value = 100
|
105
|
+
obj = type("TestObject", (object,), {})()
|
106
|
+
for i in range(100):
|
107
|
+
with self.subTest(run=i):
|
108
|
+
integer_value = LazyInteger(min_value, max_value).evaluate(obj, 1, None)
|
109
|
+
self.assertIsInstance(integer_value, int)
|
110
|
+
self.assertTrue(min_value <= integer_value <= max_value)
|
111
|
+
|
112
|
+
def test_LazyDecimal(self):
|
113
|
+
min_value = 1.0
|
114
|
+
max_value = 100.0
|
115
|
+
precision = 4
|
116
|
+
obj = type("TestObject", (object,), {})()
|
117
|
+
for i in range(100):
|
118
|
+
with self.subTest(run=i):
|
119
|
+
decimal_value = LazyDecimal(min_value, max_value, precision).evaluate(
|
120
|
+
obj, 1, None
|
121
|
+
)
|
122
|
+
self.assertIsInstance(decimal_value, Decimal)
|
123
|
+
self.assertTrue(
|
124
|
+
Decimal(min_value) <= decimal_value <= Decimal(max_value)
|
125
|
+
)
|
126
|
+
decimal_str = format(decimal_value, "f")
|
127
|
+
if "." in decimal_str:
|
128
|
+
self.assertEqual(len(decimal_str.split(".")[1]), precision)
|
129
|
+
|
130
|
+
def test_LazyUUID(self):
|
131
|
+
obj = type("TestObject", (object,), {})()
|
132
|
+
for i in range(100):
|
133
|
+
with self.subTest(run=i):
|
134
|
+
uuid_value = LazyUUID().evaluate(obj, 1, None)
|
135
|
+
self.assertIsInstance(uuid_value, str)
|
136
|
+
self.assertRegex(
|
137
|
+
uuid_value,
|
138
|
+
r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
139
|
+
)
|
140
|
+
|
141
|
+
def test_LazyBoolean(self):
|
142
|
+
obj = type("TestObject", (object,), {})()
|
143
|
+
for i in range(100):
|
144
|
+
with self.subTest(run=i):
|
145
|
+
boolean_value = LazyBoolean().evaluate(obj, 1, None)
|
146
|
+
self.assertIsInstance(boolean_value, bool)
|
147
|
+
|
148
|
+
def test_LazyFakerName(self):
|
149
|
+
obj = type("TestObject", (object,), {})()
|
150
|
+
for i in range(100):
|
151
|
+
with self.subTest(run=i):
|
152
|
+
name_value = LazyFakerName().evaluate(obj, 1, None)
|
153
|
+
self.assertIsInstance(name_value, str)
|
154
|
+
self.assertTrue(len(name_value) > 0)
|
155
|
+
|
156
|
+
def test_LazyFakerEmail(self):
|
157
|
+
obj = type("TestObject", (object,), {})()
|
158
|
+
for i in range(100):
|
159
|
+
with self.subTest(run=i):
|
160
|
+
email_value = LazyFakerEmail().evaluate(obj, 1, None)
|
161
|
+
self.assertIsInstance(email_value, str)
|
162
|
+
self.assertRegex(email_value, r"^[\w\.-]+@[\w\.-]+\.\w+$")
|
163
|
+
|
164
|
+
def test_LazyFakerEmail_with_presets(self):
|
165
|
+
obj = type("TestObject", (object,), {})()
|
166
|
+
names = [
|
167
|
+
"John Doe",
|
168
|
+
"Jane Smith",
|
169
|
+
"Alice Johnson",
|
170
|
+
"Bob Brown",
|
171
|
+
"Charlie Davis Jr.",
|
172
|
+
None,
|
173
|
+
None,
|
174
|
+
]
|
175
|
+
domains = ["example.com", None]
|
176
|
+
for i, name in enumerate(names):
|
177
|
+
domain = domains[i % len(domains)]
|
178
|
+
with self.subTest(run=i):
|
179
|
+
email_value = LazyFakerEmail(name, domain).evaluate(obj, 1, None)
|
180
|
+
self.assertIsInstance(email_value, str)
|
181
|
+
self.assertRegex(email_value, r"^[\w\.-]+@[\w\.-]+\.\w+$")
|
182
|
+
|
183
|
+
def test_LazyFakerSentence(self):
|
184
|
+
obj = type("TestObject", (object,), {})()
|
185
|
+
for i in range(100):
|
186
|
+
with self.subTest(run=i):
|
187
|
+
sentence_value = LazyFakerSentence().evaluate(obj, 1, None)
|
188
|
+
self.assertIsInstance(sentence_value, str)
|
189
|
+
self.assertTrue(len(sentence_value) > 0)
|
190
|
+
|
191
|
+
def test_LazyFakerAddress(self):
|
192
|
+
obj = type("TestObject", (object,), {})()
|
193
|
+
for i in range(100):
|
194
|
+
with self.subTest(run=i):
|
195
|
+
address_value = LazyFakerAddress().evaluate(obj, 1, None)
|
196
|
+
self.assertIsInstance(address_value, str)
|
197
|
+
self.assertTrue(len(address_value) > 0)
|
198
|
+
|
199
|
+
def test_LazyFakerUrl(self):
|
200
|
+
obj = type("TestObject", (object,), {})()
|
201
|
+
for i in range(100):
|
202
|
+
with self.subTest(run=i):
|
203
|
+
url_value = LazyFakerUrl().evaluate(obj, 1, None)
|
204
|
+
self.assertIsInstance(url_value, str)
|
205
|
+
self.assertRegex(url_value, r"^https?://[^\s/$.?#].[^\s]*$")
|
206
|
+
self.assertTrue(len(url_value) > 0)
|
207
|
+
|
208
|
+
def test_LazyChoice(self):
|
209
|
+
options = ["option1", "option2", "option3"]
|
210
|
+
obj = type("TestObject", (object,), {})()
|
211
|
+
for i in range(100):
|
212
|
+
with self.subTest(run=i):
|
213
|
+
choice_value = LazyChoice(options).evaluate(obj, 1, None)
|
214
|
+
self.assertIn(choice_value, options)
|
215
|
+
self.assertIsInstance(choice_value, str)
|
216
|
+
self.assertTrue(len(choice_value) > 0)
|
217
|
+
|
218
|
+
def test_LazySequence(self):
|
219
|
+
start = 0
|
220
|
+
step = 2
|
221
|
+
obj = type("TestObject", (object,), {})()
|
222
|
+
|
223
|
+
for i in range(100):
|
224
|
+
with self.subTest(run=i):
|
225
|
+
context = SimpleNamespace(sequence=i)
|
226
|
+
sequence_value = LazySequence(start, step).evaluate(obj, context, None)
|
227
|
+
self.assertEqual(sequence_value, start + i * step)
|
228
|
+
self.assertIsInstance(sequence_value, int)
|
@@ -1,9 +1,9 @@
|
|
1
|
-
from django.test import
|
1
|
+
from django.test import SimpleTestCase
|
2
2
|
from general_manager.auxiliary.noneToZero import noneToZero
|
3
3
|
from general_manager.measurement import Measurement
|
4
4
|
|
5
5
|
|
6
|
-
class TestNoneToZero(
|
6
|
+
class TestNoneToZero(SimpleTestCase):
|
7
7
|
|
8
8
|
def test_none_to_zero(self):
|
9
9
|
"""
|
@@ -1,38 +0,0 @@
|
|
1
|
-
from typing import Any
|
2
|
-
from factory.declarations import LazyFunction, LazyAttribute, LazyAttributeSequence
|
3
|
-
import random
|
4
|
-
from general_manager.measurement.measurement import Measurement
|
5
|
-
from datetime import timedelta, date
|
6
|
-
from faker import Faker
|
7
|
-
|
8
|
-
fake = Faker()
|
9
|
-
|
10
|
-
|
11
|
-
def LazyMeasurement(
|
12
|
-
min_value: int | float, max_value: int | float, unit: str
|
13
|
-
) -> LazyFunction:
|
14
|
-
return LazyFunction(
|
15
|
-
lambda: Measurement(str(random.uniform(min_value, max_value))[:10], unit)
|
16
|
-
)
|
17
|
-
|
18
|
-
|
19
|
-
def LazyDeltaDate(avg_delta_days: int, base_attribute: str) -> LazyAttribute:
|
20
|
-
return LazyAttribute(
|
21
|
-
lambda obj: (getattr(obj, base_attribute) or date.today())
|
22
|
-
+ timedelta(days=random.randint(avg_delta_days // 2, avg_delta_days * 3 // 2))
|
23
|
-
)
|
24
|
-
|
25
|
-
|
26
|
-
def LazyProjectName() -> LazyFunction:
|
27
|
-
return LazyFunction(
|
28
|
-
lambda: (
|
29
|
-
f"{fake.word().capitalize()} "
|
30
|
-
f"{fake.word().capitalize()} "
|
31
|
-
f"{fake.random_element(elements=('X', 'Z', 'G'))}"
|
32
|
-
f"-{fake.random_int(min=1, max=1000)}"
|
33
|
-
)
|
34
|
-
)
|
35
|
-
|
36
|
-
|
37
|
-
def LazySapNumber() -> LazyAttributeSequence:
|
38
|
-
return LazyAttributeSequence(lambda obj, n: f"60{n:04d}")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/cache/modelDependencyCollector.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
{generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/interface/baseInterface.py
RENAMED
File without changes
|
{generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/interface/calculationInterface.py
RENAMED
File without changes
|
{generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/interface/databaseInterface.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/measurement/measurementField.py
RENAMED
File without changes
|
File without changes
|
{generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/permission/basePermission.py
RENAMED
File without changes
|
{generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/permission/fileBasedPermission.py
RENAMED
File without changes
|
File without changes
|
{generalmanager-0.3.2 → generalmanager-0.4.1}/src/general_manager/permission/permissionChecks.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|