mirror of
https://github.com/Ichinga-Samuel/aiomql.git
synced 2026-07-30 21:57:44 +00:00
226 lines
7.2 KiB
Python
226 lines
7.2 KiB
Python
"""Comprehensive tests for the Base and _Base classes.
|
|
|
|
Tests cover:
|
|
- Base class initialization and attribute handling
|
|
- Dictionary conversion with include/exclude filtering
|
|
- Annotations and class variables
|
|
- _Base class MetaTrader and Config integration
|
|
- Pickling/serialization support
|
|
- Mode switching (async/sync)
|
|
"""
|
|
|
|
import enum
|
|
import pytest
|
|
from aiomql.core.base import Base, _Base
|
|
from aiomql.core.config import Config
|
|
from aiomql.core.meta_trader import MetaTrader
|
|
|
|
|
|
class ChildClass(Base):
|
|
attr: int
|
|
attr2: str
|
|
cls_attr: int = 10
|
|
|
|
|
|
class ChildBaseClass(_Base):
|
|
"""Test subclass of _Base for testing MT5/Config integration."""
|
|
attr: int
|
|
attr2: str
|
|
cls_attr: int = 20
|
|
|
|
|
|
class TestEnum(enum.Enum):
|
|
"""Test enum for repr testing."""
|
|
VALUE_A = 1
|
|
VALUE_B = 2
|
|
|
|
|
|
class EnumChild(Base):
|
|
"""Test class with enum attribute."""
|
|
name: str
|
|
status: TestEnum
|
|
|
|
|
|
class TestBaseClass:
|
|
"""Tests for the Base class."""
|
|
|
|
@pytest.fixture
|
|
def child(self):
|
|
return ChildClass(attr=1, attr2="test")
|
|
|
|
def test_repr(self, child):
|
|
repr_str = repr(child)
|
|
assert repr_str.startswith("ChildClass(")
|
|
assert "attr=1" in repr_str
|
|
assert "attr2=test" in repr_str
|
|
|
|
def test_repr_with_enum(self):
|
|
"""Test repr correctly displays enum values."""
|
|
obj = EnumChild(name="test", status=TestEnum.VALUE_A)
|
|
repr_str = repr(obj)
|
|
assert "name=test" in repr_str
|
|
assert "VALUE_A" in repr_str
|
|
|
|
def test_repr_truncates_long_attributes(self):
|
|
"""Test repr truncates when there are more than 3 attributes."""
|
|
class ManyAttrs(Base):
|
|
a: int
|
|
b: int
|
|
c: int
|
|
d: int
|
|
e: int
|
|
|
|
obj = ManyAttrs(a=1, b=2, c=3, d=4, e=5)
|
|
repr_str = repr(obj)
|
|
assert "..." in repr_str
|
|
assert "a=1" in repr_str
|
|
assert "e=5" in repr_str
|
|
|
|
def test_set_attributes(self, child):
|
|
child.set_attributes(attr3=3.14, attr2="str")
|
|
assert child.attr2 == "str"
|
|
assert getattr(child, "attr3", None) is None
|
|
|
|
def test_set_attributes_type_conversion(self):
|
|
"""Test set_attributes converts types based on annotations."""
|
|
child = ChildClass(attr="42", attr2=123)
|
|
assert child.attr == 42
|
|
assert child.attr2 == "123"
|
|
|
|
def test_annotations(self, child):
|
|
annotations = child.annotations
|
|
assert isinstance(annotations, dict)
|
|
assert "attr" in annotations
|
|
assert "attr2" in annotations
|
|
|
|
def test_annotations_includes_parent_classes(self):
|
|
"""Test annotations includes attributes from parent classes."""
|
|
class GrandChild(ChildClass):
|
|
extra: float
|
|
|
|
grandchild = GrandChild(attr=1, attr2="test", extra=3.14)
|
|
annotations = grandchild.annotations
|
|
assert "attr" in annotations
|
|
assert "attr2" in annotations
|
|
assert "extra" in annotations
|
|
|
|
def test_get_dict(self, child):
|
|
child.set_attributes(attr2="test")
|
|
result = child.get_dict()
|
|
assert result["attr"] == 1
|
|
assert result["attr2"] == "test"
|
|
|
|
def test_get_dict_with_exclude(self, child):
|
|
child.set_attributes(attr2="test")
|
|
result = child.get_dict(exclude={"attr"})
|
|
assert "attr" not in result
|
|
assert result["attr2"] == "test"
|
|
|
|
def test_get_dict_with_include(self, child):
|
|
child.set_attributes(attr3=3.14)
|
|
result = child.get_dict(include={"attr"})
|
|
assert result["attr"] == 1
|
|
assert "attr2" not in result
|
|
|
|
def test_get_dict_include_takes_precedence(self, child):
|
|
"""Test that include takes precedence over exclude."""
|
|
result = child.get_dict(include={"attr"}, exclude={"attr"})
|
|
assert "attr" in result
|
|
|
|
def test_class_vars(self, child):
|
|
class_vars = child.class_vars
|
|
assert isinstance(class_vars, dict)
|
|
assert "cls_attr" in class_vars
|
|
assert "attr" not in class_vars
|
|
|
|
def test_dict_property(self, child):
|
|
child.set_attributes(attr2="test")
|
|
dict_prop = child.dict
|
|
assert dict_prop["attr"] == 1
|
|
assert dict_prop["attr2"] == "test"
|
|
assert dict_prop["cls_attr"] == 10
|
|
|
|
def test_dict_excludes_none_values(self):
|
|
"""Test dict property excludes None values."""
|
|
class OptionalAttr(Base):
|
|
required: int
|
|
optional: str = None
|
|
|
|
obj = OptionalAttr(required=1)
|
|
assert "optional" not in obj.dict
|
|
|
|
def test_dict_excludes_internal_attributes(self, child):
|
|
"""Test dict excludes internal attributes like mt5, config."""
|
|
dict_prop = child.dict
|
|
assert "mt5" not in dict_prop
|
|
assert "config" not in dict_prop
|
|
assert "exclude" not in dict_prop
|
|
assert "include" not in dict_prop
|
|
|
|
|
|
class TestUnderscoreBaseClass:
|
|
"""Tests for the _Base class with MT5/Config integration."""
|
|
|
|
@pytest.fixture
|
|
def base_child(self):
|
|
return ChildBaseClass(attr=1, attr2="test")
|
|
|
|
def test_has_mt5_attribute(self, base_child):
|
|
"""Test _Base provides mt5 attribute."""
|
|
assert hasattr(base_child, "mt5")
|
|
|
|
def test_has_config_attribute(self, base_child):
|
|
"""Test _Base provides config attribute."""
|
|
assert hasattr(base_child, "config")
|
|
assert isinstance(base_child.config, Config)
|
|
|
|
def test_mt5_is_metatrader_instance(self, base_child):
|
|
"""Test mt5 is a MetaTrader instance in async mode."""
|
|
# Default mode is async
|
|
assert isinstance(base_child.mt5, MetaTrader)
|
|
|
|
def test_config_is_shared(self):
|
|
"""Test config is shared across instances."""
|
|
child1 = ChildBaseClass(attr=1, attr2="test1")
|
|
child2 = ChildBaseClass(attr=2, attr2="test2")
|
|
assert child1.config is child2.config
|
|
|
|
def test_mt5_is_shared(self):
|
|
"""Test mt5 is shared across instances."""
|
|
child1 = ChildBaseClass(attr=1, attr2="test1")
|
|
child2 = ChildBaseClass(attr=2, attr2="test2")
|
|
assert child1.mt5 is child2.mt5
|
|
|
|
def test_getstate_excludes_mt5(self, base_child):
|
|
"""Test __getstate__ excludes mt5 for pickling."""
|
|
state = base_child.__getstate__()
|
|
assert "mt5" not in state
|
|
|
|
def test_getstate_preserves_other_attributes(self, base_child):
|
|
"""Test __getstate__ preserves other instance attributes."""
|
|
state = base_child.__getstate__()
|
|
assert state["attr"] == 1
|
|
assert state["attr2"] == "test"
|
|
|
|
def test_inherits_from_base(self, base_child):
|
|
"""Test _Base inherits from Base."""
|
|
assert isinstance(base_child, Base)
|
|
|
|
def test_dict_property_works(self, base_child):
|
|
"""Test dict property works correctly."""
|
|
dict_prop = base_child.dict
|
|
assert dict_prop["attr"] == 1
|
|
assert dict_prop["attr2"] == "test"
|
|
assert dict_prop["cls_attr"] == 20
|
|
|
|
def test_mode_attribute(self, base_child):
|
|
"""Test default mode is async."""
|
|
assert base_child.mode == "async"
|
|
|
|
def test_class_setup_called_on_new(self):
|
|
"""Test _setup is called during instance creation."""
|
|
child = ChildBaseClass(attr=1, attr2="test")
|
|
# If _setup was called, mt5 and config should be set
|
|
assert hasattr(ChildBaseClass, "mt5")
|
|
assert hasattr(ChildBaseClass, "config")
|