Files

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")