import pytest from myutils.ProxyObject import ProxyObject # Test fixtures class Person: def __init__(self, name, age): self.name = name self.age = age class City: def __init__(self, name, zip_code): self.name = name self.zip_code = zip_code class Address: def __init__(self, city, country): self.city = city self.country = country class Employee: def __init__(self, name, age, address): self.name = name self.age = age self.address = address @pytest.fixture def person(): return Person("John", 30) @pytest.fixture def employee(): address = Address("Paris", "France") return Employee("Alice", 25, address) # ============================================================================ # Basic tests # ============================================================================ def test_i_can_create_proxy_object_with_simple_mapping(person): """ ProxyObject should be created successfully with a simple mapping. """ mappings = {"display_name": "name"} proxy = ProxyObject(person, mappings) assert isinstance(proxy, ProxyObject) def test_i_can_access_mapped_property(person): """ Accessing a mapped property should return the value from the source object. """ mappings = {"display_name": "name"} proxy = ProxyObject(person, mappings) assert proxy.display_name == "John" def test_i_can_map_nested_properties(employee): """ Nested properties using dot notation should be mapped correctly. """ mappings = {"city_name": "address.city"} proxy = ProxyObject(employee, mappings) assert proxy.city_name == "Paris" def test_i_can_map_nested_nested_properties(): address = Address(City("Paris", "75001"), "France") employee2 = Employee("Alice", 25, address) mappings = {"city_zip_code": "address.city.zip_code"} proxy = ProxyObject(employee2, mappings) assert proxy.city_zip_code == "75001" def test_i_can_map_multiple_properties(employee): """ Multiple mappings should work simultaneously. """ mappings = { "full_name": "name", "years": "age", "city_name": "address.city" } proxy = ProxyObject(employee, mappings) assert proxy.full_name == "Alice" assert proxy.years == 25 assert proxy.city_name == "Paris" def test_i_can_manage_wild_card(employee): mappings = {"*": ""} proxy = ProxyObject(employee, mappings) assert proxy.name == employee.name assert proxy.age == employee.age assert proxy.address == employee.address def test_i_can_manage_wild_card_2(employee): mappings = {"*": "*"} proxy = ProxyObject(employee, mappings) assert proxy.name == employee.name assert proxy.age == employee.age assert proxy.city == employee.address.city assert proxy.country == employee.address.country def test_i_can_manage_wild_card_on_nested_properties(): address = Address(City("Paris", "75001"), "France") employee2 = Employee("Alice", 25, address) mappings = {"name": "name", "*": "address"} proxy = ProxyObject(employee2, mappings) assert proxy.name == employee2.name assert proxy.city == employee2.address.city assert proxy.country == employee2.address.country def test_i_can_manage_wild_card_on_multiple_properties(): address = Address(City("Paris", "75001"), "France") employee2 = Employee("Alice", 25, address) mappings = {"*": "address.*"} proxy = ProxyObject(employee2, mappings) assert proxy.name == employee2.address.city.name assert proxy.zip_code == employee2.address.city.zip_code assert proxy.country == employee2.address.country # ============================================================================ # Edge case tests # ============================================================================ def test_i_cannot_access_unmapped_property(person): """ Accessing an unmapped property should raise AttributeError. """ mappings = {"display_name": "name"} proxy = ProxyObject(person, mappings) with pytest.raises(AttributeError): _ = proxy.unknown_property def test_i_can_handle_missing_source_property(person): """ When the source property doesn't exist, the proxy should handle it gracefully. The current implementation breaks the loop and assigns the last valid value. """ mappings = {"missing": "non_existent_attr"} proxy = ProxyObject(person, mappings) # The property exists in _props (because of break behavior) # but contains the original object assert proxy.missing == person def test_i_can_handle_none_values(): """ None values should be handled correctly. """ person = Person(None, 30) mappings = {"display_name": "name"} proxy = ProxyObject(person, mappings) assert proxy.display_name is None # ============================================================================ # Special methods tests # ============================================================================ def test_proxy_objects_with_same_props_are_equal(person): """ Two ProxyObject instances with the same _props should be equal. """ mappings = {"display_name": "name", "years": "age"} proxy1 = ProxyObject(person, mappings) proxy2 = ProxyObject(person, mappings) assert proxy1 == proxy2 def test_proxy_objects_with_different_props_are_not_equal(person): """ Two ProxyObject instances with different _props should not be equal. """ mappings1 = {"display_name": "name"} mappings2 = {"years": "age"} proxy1 = ProxyObject(person, mappings1) proxy2 = ProxyObject(person, mappings2) assert proxy1 != proxy2 def test_i_can_hash_proxy_object(person): """ ProxyObject should be hashable and usable in sets/dicts. """ mappings = {"display_name": "name", "years": "age"} proxy = ProxyObject(person, mappings) # Should not raise an exception proxy_hash = hash(proxy) assert isinstance(proxy_hash, int) # Should be usable in a set proxy_set = {proxy} assert proxy in proxy_set def test_repr_shows_key_when_present(): """ When 'key' is present in _props, __repr__ should show it. """ person = Person("key_value", 30) mappings = {"key": "name"} proxy = ProxyObject(person, mappings) repr_str = repr(proxy) assert "key=key_value" in repr_str def test_repr_truncates_long_content(person): """ When _props content is longer than 50 chars, __repr__ should truncate it. """ mappings = { "very_long_property_name_1": "name", "very_long_property_name_2": "age" } proxy = ProxyObject(person, mappings) repr_str = repr(proxy) assert "..." in repr_str assert len(repr_str) < 100 # Should be truncated