From 9aba816e80a5aeb8d61f2bdbbbd4da2e1e76f05b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 17 Sep 2020 18:41:07 -0700 Subject: [PATCH] refactor MockBug in preperation for adding `status` field to `Node` --- src/budget_sync/test/mock_bug.py | 43 ++++++++++++---- src/budget_sync/test/test_mock_bug.py | 72 +++++++++++++++++++++++++++ src/budget_sync/util.py | 30 ++++++++++- 3 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 src/budget_sync/test/test_mock_bug.py diff --git a/src/budget_sync/test/mock_bug.py b/src/budget_sync/test/mock_bug.py index acc2cae..e63ab46 100644 --- a/src/budget_sync/test/mock_bug.py +++ b/src/budget_sync/test/mock_bug.py @@ -1,18 +1,19 @@ -from typing import Optional +from typing import Optional, Union +from budget_sync.util import BugStatus class MockBug: def __init__(self, bug_id: int, - cf_budget_parent: Optional[int], - cf_budget: str, - cf_total_budget: str, - cf_nlnet_milestone: Optional[str], - cf_payees_list: str, - summary: str): + cf_budget_parent: Optional[int] = None, + cf_budget: str = "0", + cf_total_budget: str = "0", + cf_nlnet_milestone: Optional[str] = None, + cf_payees_list: str = "", + summary: str = "", + status: Union[str, BugStatus] = BugStatus.CONFIRMED): self.id = bug_id - if cf_budget_parent is not None: - self.cf_budget_parent = cf_budget_parent + self.__budget_parent = cf_budget_parent self.cf_budget = cf_budget self.cf_total_budget = cf_total_budget if cf_nlnet_milestone is None: @@ -20,13 +21,35 @@ class MockBug: self.cf_nlnet_milestone = cf_nlnet_milestone self.cf_payees_list = cf_payees_list self.summary = summary + self.status = str(status) + + @property + def cf_budget_parent(self) -> int: + if self.__budget_parent is None: + raise AttributeError( + "'MockBug' object has no attribute 'cf_budget_parent'") + return self.__budget_parent + + @cf_budget_parent.setter + def cf_budget_parent(self, value: int): + if isinstance(value, int): + self.__budget_parent = value + else: + raise TypeError("cf_budget_parent must be an int") + + @cf_budget_parent.deleter + def cf_budget_parent(self): + self.cf_budget_parent # trigger AttributeError if property cleared + self.__budget_parent = None def __repr__(self): cf_budget_parent = getattr(self, "cf_budget_parent", None) + status = BugStatus.cast(self.status, unknown_allowed=True) return (f"MockBug(bug_id={self.id!r}, " f"cf_budget_parent={cf_budget_parent!r}, " f"cf_budget={self.cf_budget!r}, " f"cf_total_budget={self.cf_total_budget!r}, " f"cf_nlnet_milestone={self.cf_nlnet_milestone!r}, " f"cf_payees_list={self.cf_payees_list!r}, " - f"summary={self.summary!r})") + f"summary={self.summary!r}, " + f"status={status!r})") diff --git a/src/budget_sync/test/test_mock_bug.py b/src/budget_sync/test/test_mock_bug.py new file mode 100644 index 0000000..fd3624f --- /dev/null +++ b/src/budget_sync/test/test_mock_bug.py @@ -0,0 +1,72 @@ +import unittest +from budget_sync.test.mock_bug import MockBug +from budget_sync.util import BugStatus + + +class TestBugStatus(unittest.TestCase): + def test_values(self): + for i in BugStatus: + self.assertIs(i, getattr(BugStatus, i.value)) + self.assertEqual(str(i), i.value) + self.assertEqual(repr(i), f"BugStatus.{i.value}") + + def test_cast(self): + for i in BugStatus: + self.assertEqual(i, BugStatus.cast(i)) + self.assertEqual(i, BugStatus.cast(str(i))) + self.assertEqual(i, BugStatus.cast(str(i), unknown_allowed=True)) + with self.assertRaises(ValueError): + BugStatus.cast("") + self.assertEqual("", + BugStatus.cast("", unknown_allowed=True)) + + +class TestMockBug(unittest.TestCase): + maxDiff = None + + def test_repr(self): + bug = MockBug(bug_id=12) + self.assertEqual( + repr(bug), + "MockBug(bug_id=12, cf_budget_parent=None, cf_budget='0', " + "cf_total_budget='0', cf_nlnet_milestone='---', " + "cf_payees_list='', summary='', " + "status=BugStatus.CONFIRMED)") + bug = MockBug(bug_id=34, + cf_budget_parent=1, + cf_budget="45", + cf_total_budget="23", + cf_nlnet_milestone="abc", + cf_payees_list="# a", + summary="blah blah", + status="blah") + self.assertEqual( + repr(bug), + "MockBug(bug_id=34, cf_budget_parent=1, cf_budget='45', " + "cf_total_budget='23', cf_nlnet_milestone='abc', " + "cf_payees_list='# a', summary='blah blah', status='blah')") + + def test_cf_budget_parent(self): + bug = MockBug(bug_id=1, cf_budget_parent=None) + with self.assertRaises(AttributeError): + bug.cf_budget_parent + self.assertIsNone(getattr(bug, "cf_budget_parent", None)) + bug.cf_budget_parent = 1 + self.assertEqual(bug.cf_budget_parent, 1) + with self.assertRaises(TypeError): + bug.cf_budget_parent = "abc" + del bug.cf_budget_parent + with self.assertRaises(AttributeError): + bug.cf_budget_parent + with self.assertRaises(AttributeError): + del bug.cf_budget_parent + with self.assertRaises(AttributeError): + bug.cf_budget_parent + bug.cf_budget_parent = 5 + self.assertEqual(bug.cf_budget_parent, 5) + bug = MockBug(bug_id=1, cf_budget_parent=2) + self.assertEqual(bug.cf_budget_parent, 2) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/budget_sync/util.py b/src/budget_sync/util.py index ed1c300..20e7da6 100644 --- a/src/budget_sync/util.py +++ b/src/budget_sync/util.py @@ -1,6 +1,34 @@ from bugzilla import Bugzilla from bugzilla.bug import Bug -from typing import Iterator +from typing import Iterator, Union +from enum import Enum + + +class BugStatus(Enum): + UNCONFIRMED = "UNCONFIRMED" + CONFIRMED = "CONFIRMED" + IN_PROGRESS = "IN_PROGRESS" + DEFERRED = "DEFERRED" + RESOLVED = "RESOLVED" + VERIFIED = "VERIFIED" + PAYMENTPENDING = "PAYMENTPENDING" + + def __str__(self): + return self.value + + def __repr__(self): + return f"BugStatus.{self.value}" + + @staticmethod + def cast(v: Union[str, "BugStatus"], + unknown_allowed: bool = False) -> Union[str, "BugStatus"]: + s = str(v) + try: + return BugStatus(s) + except ValueError: + if unknown_allowed: + return s + raise def all_bugs(bz: Bugzilla) -> Iterator[Bug]: -- 2.30.2