Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions hcloud/core/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,9 @@ def __repr__(self) -> str:
# models, as they will generate a lot of API call trying to print all the fields
# of the model.
return object.__repr__(self)

def __eq__(self, other: Any) -> bool:
"""Compare a bound model object with another of the same type."""
if not isinstance(other, self.__class__):
return NotImplemented
return self.data_model == other.data_model
11 changes: 11 additions & 0 deletions hcloud/core/domain.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from typing import Any


class BaseDomain:
__api_properties__: tuple
Expand All @@ -16,6 +18,15 @@ def __repr__(self) -> str:
kwargs = [f"{key}={getattr(self, key)!r}" for key in self.__api_properties__] # type: ignore[var-annotated]
return f"{self.__class__.__qualname__}({', '.join(kwargs)})"

def __eq__(self, other: Any) -> bool:
"""Compare a domain object with another of the same type."""
if not isinstance(other, self.__class__):
return NotImplemented
for key in self.__api_properties__:
if getattr(self, key) != getattr(other, key):
return False
return True


class DomainIdentityMixin:

Expand Down
17 changes: 17 additions & 0 deletions tests/unit/core/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ def test_get_non_exists_model_attribute_incomplete_model(
client.get_by_id.assert_not_called()
assert bound_model.complete is False

def test_equality(self, bound_model_class, client):
data = {"id": 1, "name": "name", "description": "my_description"}
bound_model_a = bound_model_class(client=client, data=data)
bound_model_b = bound_model_class(client=client, data=data)

# Comparing a bound model with a base domain
assert bound_model_a == bound_model_a.data_model

# Identical bound models
assert bound_model_a == bound_model_b
assert bound_model_a == bound_model_b.data_model

# Differing bound models
bound_model_b.data_model.name = "changed_name"
assert bound_model_a != bound_model_b
assert bound_model_a != bound_model_b.data_model


class TestClientEntityBase:
@pytest.fixture()
Expand Down
29 changes: 29 additions & 0 deletions tests/unit/core/test_domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,32 @@ def test_from_dict_ok(self, data_dict, expected_result):
)
def test_repr_ok(self, data, expected):
assert data.__repr__() == expected

def test__eq__(self):
a1 = ActionDomain(id=1, name="action")
assert a1 == ActionDomain(id=1, name="action")
assert a1 != ActionDomain(id=2, name="action")
assert a1 != ActionDomain(id=1, name="something")
assert a1 != SomeOtherDomain(id=1, name="action")

def test_nested__eq__(self):
child1 = ActionDomain(id=1, name="child")
d1 = SomeOtherDomain(id=1, name="parent", child=child1)
d2 = SomeOtherDomain(id=1, name="parent", child=child1)

assert d1 == d2

d2.child = ActionDomain(id=2, name="child2")

assert d1 != d2

def test_nested_list__eq__(self):
child1 = ActionDomain(id=1, name="child")
d1 = SomeOtherDomain(id=1, name="parent", child=[child1])
d2 = SomeOtherDomain(id=1, name="parent", child=[child1])

assert d1 == d2

d2.child = [ActionDomain(id=2, name="child2")]

assert d1 != d2