From e312db4b3d7989c5a02651875d618750c599f570 Mon Sep 17 00:00:00 2001 From: FelixFelix0815 Date: Wed, 4 Feb 2026 19:51:36 +0100 Subject: [PATCH 1/4] reformat sipa/model/pycroft/user.py and refactor deprecated None check as safe default --- sipa/model/pycroft/user.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/sipa/model/pycroft/user.py b/sipa/model/pycroft/user.py index ef8e3d05..4b1f36c9 100644 --- a/sipa/model/pycroft/user.py +++ b/sipa/model/pycroft/user.py @@ -383,24 +383,25 @@ def is_member(self) -> bool: return self.has_property('member') def evaluate_status(self, status: UserStatus): - message = None - style = None + message, style = gettext('Ok'), 'success' + if status.violation: message, style = gettext('Verstoß gegen Netzordnung'), 'danger' elif not status.account_balanced: message, style = gettext('Nicht bezahlt'), 'warning' elif status.traffic_exceeded: message, style = gettext('Trafficlimit überschritten'), 'danger' - elif not status.member and self.user_data.membership_begin_date is not None: + elif not status.member \ + and self.user_data.membership_begin_date is not None: message, style = "{} {}".format(gettext('Mitglied ab'), - self.user_data.membership_begin_date.isoformat()), \ - 'warning' + self.user_data.membership_begin_date.isoformat() + ), 'warning' elif not status.member: message, style = gettext('Kein Mitglied'), 'muted' elif status.member and self.membership_end_date.raw_value is not None: message, style = "{} {}".format(gettext('Mitglied bis'), - self.membership_end_date.value.isoformat()), \ - 'warning' + self.membership_end_date.value.isoformat() + ), 'warning' elif status.member: message, style = gettext('Mitglied'), 'success' @@ -408,10 +409,8 @@ def evaluate_status(self, status: UserStatus): if len(self.user_data.interfaces) > 0: message += ', {}'.format(gettext('Netzzugang gesperrt')) else: - message += ', {}'.format(gettext('Kabelgebundener Zugang nicht aktiviert')) - - if message is None: - message, style = gettext('Ok'), 'success' + message += ', {}'.format(gettext( + 'Kabelgebundener Zugang nicht aktiviert')) return message, style From a66a734ed33ff765d681ed0f35974d988272279e Mon Sep 17 00:00:00 2001 From: FelixFelix0815 Date: Wed, 4 Feb 2026 19:53:56 +0100 Subject: [PATCH 2/4] add tests for UserStatus function on pycroft user model --- tests/model/test_user.py | 104 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/tests/model/test_user.py b/tests/model/test_user.py index 8b48df46..30f6f085 100644 --- a/tests/model/test_user.py +++ b/tests/model/test_user.py @@ -2,6 +2,10 @@ from sipa.model.user import BaseUser from sipa.model.finance import BaseFinanceInformation +from sipa.model.pycroft.user import User as PycroftUser +from sipa.model.pycroft.schema import UserStatus + +from datetime import date, datetime, timedelta class TestBaseUserCase(TestCase): @@ -89,3 +93,103 @@ def test_balance_row_supported(self): def test_balance_correct_name(self): assert self.bal.name == "finance_balance" + +class UserStatusTest(TestCase): + class FakeActiveProperty: + raw_value: any + + def __init__(self, val): + self.raw_value = val + + @property + def value(self): + return self.raw_value + + def __bool__(self): + return bool(self.raw_value) + + def __len__(self): + return 1 if self.raw_value else 0 + + class TestUserData: + membership_begin_date: date | None + membership_end_date: "FakeActiveProperty" + + def setUp(self): + self.user = DegenerateUser + self.user.user_data = self.TestUserData() + self.user.user_data.membership_begin_date = None + self.user.membership_end_date = self.FakeActiveProperty(None) + + self.status = UserStatus( + violation=False, + network_access=True, + member=True, + account_balanced=True, + traffic_exceeded=False, + ) + + def test_status_member(self): + assert (PycroftUser.evaluate_status(self.user, self.status) + == ('Mitglied', 'success')) + + def test_status_violation(self): + self.status.violation = True + assert (PycroftUser.evaluate_status(self.user, self.status) + == ('Verstoß gegen Netzordnung', 'danger')) + + def test_status_unbalanced(self): + self.status.account_balanced = False + assert (PycroftUser.evaluate_status(self.user, self.status) + == ('Nicht bezahlt', 'warning')) + + def test_status_traffic_exceeded(self): + self.status.traffic_exceeded = True + assert (PycroftUser.evaluate_status(self.user, self.status) + == ('Trafficlimit überschritten', 'danger')) + + def test_status_membership_begin_date(self): + d = date.today() + self.status.member = False + self.user.user_data.membership_begin_date = d + assert (PycroftUser.evaluate_status(self.user, self.status) + == (f'Mitglied ab {d.isoformat()}', 'warning')) + + def test_status_not_member(self): + self.status.member = False + assert (PycroftUser.evaluate_status(self.user, self.status) + == ('Kein Mitglied', 'muted')) + + def test_status_membership_end_date(self): + d = date.today() + self.user.membership_end_date = self.FakeActiveProperty(d) + assert (PycroftUser.evaluate_status(self.user, self.status) + == (f'Mitglied bis {d.isoformat()}', 'warning')) + + def test_exhaust_everything(self): + def set_property(property: list[str], value: any): + curr_val = self + for value in property[0:-1]: + curr_val = getattr(curr_val, value) + + setattr(curr_val, property[-1], value) + + properties = [["status", "violation"], + ["status", "account_balanced"], + ["status", "traffic_exceeded"], + ["user", "user_data", "membership_begin_date"], + ["status", "member"], + ["user", "membership_end_date"]] + true_val = self.FakeActiveProperty(date.today()) + false_val = self.FakeActiveProperty(None) + + for val_mapping in range(2 ** len(properties) - 1): + for i in range(len(properties)): + if val_mapping >> i & 1: + set_property(properties[i], true_val) + else: + set_property(properties[i], false_val) + + message, style = PycroftUser.evaluate_status(self.user, self.status) + assert message is not None + assert style is not None From fa89ffdc6908cea9be889c4dc75a0e2782b75666 Mon Sep 17 00:00:00 2001 From: FelixFelix0815 Date: Wed, 4 Feb 2026 19:55:21 +0100 Subject: [PATCH 3/4] remove unused imports on pycroft user model test --- tests/model/test_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/model/test_user.py b/tests/model/test_user.py index 30f6f085..fe75bc7e 100644 --- a/tests/model/test_user.py +++ b/tests/model/test_user.py @@ -5,7 +5,7 @@ from sipa.model.pycroft.user import User as PycroftUser from sipa.model.pycroft.schema import UserStatus -from datetime import date, datetime, timedelta +from datetime import date class TestBaseUserCase(TestCase): From 308f5a1af7800f99f606d6ba57e59642ff966fc8 Mon Sep 17 00:00:00 2001 From: FelixFelix0815 Date: Wed, 4 Feb 2026 20:02:15 +0100 Subject: [PATCH 4/4] fix "F821 Undefined name" in pycroft user model tests --- tests/model/test_user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/model/test_user.py b/tests/model/test_user.py index fe75bc7e..96c39410 100644 --- a/tests/model/test_user.py +++ b/tests/model/test_user.py @@ -113,7 +113,7 @@ def __len__(self): class TestUserData: membership_begin_date: date | None - membership_end_date: "FakeActiveProperty" + membership_end_date: "UserStatusTest.FakeActiveProperty" def setUp(self): self.user = DegenerateUser