This commit is contained in:
2025-06-06 08:20:45 +03:00
parent 346ffd4255
commit caf367262c
205 changed files with 6115 additions and 200 deletions

0
tests/local/__init__.py Normal file
View File

View File

@@ -0,0 +1,99 @@
import dynamix_sdk.types as sdk_types
from dynamix_sdk.utils import gen_cls_name_from_url_path
def test_api_class_naming(api_groups):
for api_group in api_groups:
correct_class_name = f'{api_group.name.capitalize()}API'
assert api_group.cls.__qualname__ == correct_class_name, (
f'\nAPI group: {api_group.name}'
f'\nCorrect API class name: {correct_class_name}'
)
for api_subgroup in api_group.subgroups:
correct_class_name = (
f'{api_group.name.capitalize()}'
f'{api_subgroup.name.capitalize()}'
f'API'
)
assert api_subgroup.cls.__qualname__ == correct_class_name, (
f'\nAPI group: {api_group.name}'
f'\nAPI subgroup: {api_subgroup.name}'
f'\nCorrect API class name: {correct_class_name}'
)
def test_protocol_class_naming(sdk_dx_functions):
for sdk_func in sdk_dx_functions:
correct_class_name = gen_cls_name_from_url_path(
url_path=sdk_func.url_path,
postfix='Protocol',
)
assert sdk_func.proto_cls.__qualname__ == correct_class_name, (
f'\nFunction call attributes: {sdk_func.call_attrs}'
f'\nURL path: {sdk_func.url_path}'
f'\nCorrect Protocol class name: {correct_class_name}'
)
def get_subclasses(cls: type, result: list[type] | None = None):
if result:
_result = result
else:
_result: list[type] = []
for subclass in cls.__subclasses__():
if issubclass(subclass.__bases__[0], cls):
_result.append(subclass)
_result += get_subclasses(subclass)
return _result
def test_params_nested_model_class_naming():
base_cls = sdk_types.BaseAPIParamsNestedModel
for params_nm_cls in get_subclasses(base_cls):
suffix = 'APIParamsNM'
assert params_nm_cls.__qualname__.endswith(suffix), (
f'Class {params_nm_cls.__qualname__}:'
f' all subclasses of {base_cls.__qualname__}'
f' must have a name with suffix "{suffix}".'
)
def test_result_class_naming(sdk_dx_functions):
for sdk_func in sdk_dx_functions:
if issubclass(sdk_func.result_cls, sdk_types.BaseAPIResultModel):
result_cls_postfix = 'ResultModel'
elif issubclass(sdk_func.result_cls, sdk_types.BaseAPIResultStr):
result_cls_postfix = 'ResultStr'
elif issubclass(sdk_func.result_cls, sdk_types.BaseAPIResultInt):
result_cls_postfix = 'ResultInt'
elif issubclass(sdk_func.result_cls, sdk_types.BaseAPIResultBool):
result_cls_postfix = 'ResultBool'
else:
raise TypeError
result_cls_name = gen_cls_name_from_url_path(
url_path=sdk_func.url_path,
postfix=result_cls_postfix,
)
assert sdk_func.result_cls.__qualname__ == result_cls_name, (
f'\nFunction call attributes: {sdk_func.call_attrs}'
f'\nURL path: {sdk_func.url_path}'
f'\nResult base class:'
f' {sdk_func.result_cls.__bases__[0].__qualname__}'
f'\nCorrect result class name: {result_cls_name}'
)
def test_result_nested_model_class_naming():
base_cls = sdk_types.BaseAPIResultNestedModel
for result_nm_cls in get_subclasses(base_cls):
suffix = 'APIResultNM'
assert result_nm_cls.__qualname__.endswith(suffix), (
f'Class {result_nm_cls.__qualname__}:'
f' all subclasses of {base_cls.__qualname__}'
f' must have a name with suffix "{suffix}".'
)

View File

@@ -0,0 +1,68 @@
from dynamix_sdk import types as sdk_types
from dynamix_sdk.base import (
get_alias,
name_mapping_dict,
)
def test_missing_mappings_in_name_mapping_file(dx_models):
attrs_without_mapping = []
for model_cls in dx_models:
for field_name in model_cls.model_fields.keys():
try:
get_alias(
field_name=field_name,
model_cls=model_cls,
name_mapping_dict=name_mapping_dict
)
except KeyError:
attrs_without_mapping.append(
f'{model_cls.__qualname__}.{field_name}'
)
attrs_without_mapping.sort()
assert not attrs_without_mapping, (
f'{len(attrs_without_mapping)} attributes without mapping:'
f' {attrs_without_mapping}'
)
def test_unused_mappings_in_name_mapping_file(dx_models):
mapping_dict_keys = set(name_mapping_dict.keys())
def exclude_used_keys(
model_cls: type[sdk_types.BaseModel],
mapping_dict_keys: set[str],
):
for field_name in model_cls.__annotations__.keys():
used_key = None
individual_alias_key = f'{field_name}__{model_cls.__qualname__}'
if individual_alias_key in mapping_dict_keys:
used_key = individual_alias_key
elif field_name in mapping_dict_keys:
used_key = field_name
if used_key and used_key in mapping_dict_keys:
mapping_dict_keys.remove(used_key)
for base_cls in model_cls.__bases__:
if issubclass(base_cls, sdk_types.BaseModel):
exclude_used_keys(
model_cls=base_cls,
mapping_dict_keys=mapping_dict_keys,
)
for model_cls in dx_models:
exclude_used_keys(
model_cls=model_cls,
mapping_dict_keys=mapping_dict_keys,
)
unused_mapping_dict_keys = sorted(mapping_dict_keys)
assert not unused_mapping_dict_keys, (
f'{len(unused_mapping_dict_keys)} unused keys in mapping file:'
f' {unused_mapping_dict_keys}.'
)

154
tests/local/test_types.py Normal file
View File

@@ -0,0 +1,154 @@
from enum import Enum
import inspect
from types import GenericAlias, ModuleType, UnionType
from typing import Any, get_args
import dynamix_sdk.types as sdk_types
import dynamix_sdk.api._nested.params as nested_params
import dynamix_sdk.api._nested.result as nested_result
import dynamix_sdk.api._nested.enums as nested_enums
from tests.conftest import SDKFunction
def check_model_field_annotation(
annotation: Any,
field_descr: str,
valid_nested_model_cls: type[sdk_types.BaseModel],
):
if not annotation:
raise TypeError
if annotation is Any:
return
assert annotation is not list, (
f'{field_descr}: missing list elements type annotation.'
)
if isinstance(annotation, (UnionType, GenericAlias)):
for annotation in get_args(annotation):
check_model_field_annotation(
annotation=annotation,
field_descr=field_descr,
valid_nested_model_cls=valid_nested_model_cls,
)
elif issubclass(annotation, sdk_types.BaseModel):
model_cls = annotation
assert issubclass(model_cls, valid_nested_model_cls), (
f'{field_descr}: nested model class must be'
f' a subclass of {valid_nested_model_cls.__qualname__}.'
)
check_model_field_annotations(
model_cls=model_cls,
valid_nested_model_cls=valid_nested_model_cls,
)
def check_model_field_annotations(
model_cls: type[sdk_types.BaseModel],
valid_nested_model_cls: type[sdk_types.BaseModel],
):
for field_name, field_info in model_cls.model_fields.items():
field_descr = f'{model_cls.__qualname__}.{field_name}'
check_model_field_annotation(
annotation=field_info.annotation,
field_descr=field_descr,
valid_nested_model_cls=valid_nested_model_cls,
)
def test_params_model_fields_type(sdk_dx_functions: tuple[SDKFunction, ...]):
for sdk_func in sdk_dx_functions:
params = inspect.signature(sdk_func.proto_method).parameters
for param_name, param in params.items():
if param_name == 'self':
continue
field_descr = (
f'{sdk_func.proto_cls.__qualname__}.'
f'{sdk_func.call_attrs[-1]}.'
f'{param_name}'
)
assert param.annotation is not inspect.Parameter.empty, (
f'{field_descr}: missing type annotation.'
)
assert param.kind == param.KEYWORD_ONLY, (
f'Parameter {field_descr} must be keyword-only.'
)
check_model_field_annotation(
annotation=param.annotation,
field_descr=field_descr,
valid_nested_model_cls=sdk_types.BaseAPIParamsNestedModel,
)
def test_function_return_type(sdk_dx_functions):
for sdk_func in sdk_dx_functions:
assert issubclass(sdk_func.result_cls, sdk_types.BaseAPIResult), (
f'Return type for method'
f' {sdk_func.proto_cls.__qualname__}.{sdk_func.call_attrs[-1]}'
f' must be a subclass of BaseAPIResult.'
)
def test_result_model_fields_type():
valid_nested_model_cls = sdk_types.BaseAPIResultNestedModel
for subcls in sdk_types.BaseAPIResultModel.__subclasses__():
check_model_field_annotations(
model_cls=subcls,
valid_nested_model_cls=valid_nested_model_cls,
)
for subcls in sdk_types.BaseAPIResultNestedModel.__subclasses__():
check_model_field_annotations(
model_cls=subcls,
valid_nested_model_cls=valid_nested_model_cls,
)
def check_class_inheritance_in_module(
module: ModuleType,
valid_base_cls: type,
):
for attr_name in dir(module):
if attr_name.startswith('_'):
continue
attr = getattr(module, attr_name)
if not isinstance(attr, type):
continue
nested_result_cls = attr
assert issubclass(nested_result_cls, valid_base_cls), (
f'Class {nested_result_cls.__qualname__}:'
f' must be a subclass of class {valid_base_cls.__qualname__}.'
)
def test_class_inheritance_in_nested_result_module():
check_class_inheritance_in_module(
module=nested_result,
valid_base_cls=sdk_types.BaseAPIResultNestedModel,
)
def test_class_inheritance_in_nested_params_module():
check_class_inheritance_in_module(
module=nested_params,
valid_base_cls=sdk_types.BaseAPIParamsNestedModel,
)
def test_class_inheritance_in_nested_enums_module():
check_class_inheritance_in_module(
module=nested_enums,
valid_base_cls=Enum,
)